I have the following simplified code template:
pid_t pid;
int pipe1[2], pipe2[2];
pid = fork();
pipe(pipe1); pipe(pipe2)
if(pid == 0) //child
{
read(pipe1[0],...);
write(pipe1[1],...);
close(pipe1[0]);
close(pipe1[1]);
close(pipe2[1]);
read(pipe2[0]...);
}
else //parent
{
write(pipe1[1],...);
wait(NULL);
read(pipe1[0]...);
close(pipe1[0]);
close(pipe1[1]);
close(pipe2[0]);
write(pipe2[1]...);
}
If I am not using the pipe2 in parent and child, the code works perfectly, but if I do, it seems like the child has nothing to read(the program does nothing until I intrerrupt it). Also, is there a way to use only one pipe for more than 2 read/writes? I tried using wait(NULL) more than once but that didn't work.
Simply put, your code template is garbage. Let me explain why.
Each pipe is unidirectional.
If you use a pipe to send data from the child to the parent, close the read end in the child, and the write end in the parent. This allows the parent to see when the child (write end) closes the pipe or exits, as read()
will then return -1
with errno == EPIPE
.
If you use a pipe to send data from the parent to the child, close the read end in the parent, and the write end in the child. This allows the parent to detect if the child exits prematurely, as write()
will then return with -1
with errno == EPIPE
and a SIGPIPE signal gets raised in the parent.
If you need bidirectional "pipe" between the parent and a child, use an Unix domain stream socket pair via socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair)
.
Such a socket pair works very much like a pipe, except that the socket pair is bidirectional. You can also use send(descriptor, buffer, length, MSG_NOSIGNAL)
instead of write(descriptor, buffer, length)
; in the former case, no SIGPIPE signal is raised if the other end of the socket is already closed.
Use one of the descriptors in the parent, and the other in the child. Both parent and child should close the other descriptor. Otherwise, one end cannot detect when the other end has closed its descriptor.
In some cases, an Unix Domain datagram socket pair may be preferable. Each send()
generates a separate datagram, that is received using a single recv()
. (That is, message boundaries are retained.) If the receiving end knows the maximum size of a datagram the sending side might send, this is an extremely robust and simple way to implement bidirectional communications between a parent and a child process; I personally use it a lot.
read()
and write()
to pipes and sockets may be short.
(POSIX states that you should always be able to stuff at least 512 bytes into a pipe, though; Linux supports that at least up to a full page, if I recall correctly.)
This means that rather than a single call, you need to do a loop until you have as much data as you need.
With sockets, send()
either sends all the data, or fails with -1
(with errno == EMSGSIZE
, or some other error code).
For datagram sockets (Unix domain datagram sockets, UDP sockets), if the buffer is large enough, recv()
either receives the entire datagram, or fails with -1
(with errno
set). Receiving zero-length datagrams is iffy, so don't try to do that.
For stream sockets, recv()
may return only some of the data (i.e. short, or partial receive).
When two processes both send and receive, or read and write, data to/from each other, deadlock is a serious, common problem.
Simply put, both ends may end up waiting for the other end to read/write at the same time, with nothing at all happening.
There are three typical solutions to avoid a deadlock in such situations:
Use a query - response protocol, so that one endpoint always initiates communications, and then waits for the other endpoint to respond. At most one endpoint is transferring data at any given time.
Use nonblocking/asynchronous I/O. That is, before trying to write()
/send()
, each endpoint does a read()
/recv()
to see if the other end has already sent something. This supports full duplex communications (information can flow both ways at the same time).
Use a separate thread to continuously read()
/recv()
, while another does write()
/send()
. This essentially separates each socket into two unidirectional "lanes", with one thread handling their direction only. This is useful for protocols where one end produces a lot of data, and the other end sends occasional commands.
Combining all the above, we'll find that there is no single template one should use. There are variants with significant differences, making them better in some use cases but harder/worse in others. One should pick one depending on the situation at hand. If OP wants to see a better example ("template"), they should describe an actual problem case, including the desired behaviour.