Consider the following piece of code, which does:
exit(0)
."close"
to its end of the pipe and then the parent returns.#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
void child()
{
char cmd[16] = {'\0'};
int B = 0;
do {
B = scanf("%s", cmd);
} while (B == 1 && strcmp(cmd, "close") != 0);
printf("Exit child for command %s.\n", cmd); fflush(stdout);
exit(0);
}
struct forked_t
{
pid_t pid;
int fd;
};
struct forked_t fork_child()
{
struct forked_t ret = { -1, -1 };
int pipe_fds[2];
int err = pipe(pipe_fds); if (err == -1) return ret;
pid_t pid_child = fork(); if (pid_child == -1) return ret;
if (pid_child == 0) {
close(pipe_fds[1]);
dup2(pipe_fds[0], 0);
close(pipe_fds[0]);
child();
} else
close(pipe_fds[0]);
ret.pid = pid_child;
ret.fd = pipe_fds[1];
return ret;
}
int main()
{
struct forked_t child = fork_child();
if (child.fd <= 0 || child.pid <= 0) {
printf("Error creating child.\n");
return -1;
}
char c;
int B = scanf("%c", &c);
B = write(child.fd, "close", 5); if (B != 5) return -1;
//waitpid(child.pid, NULL, 0);
return 0;
}
Executing it gives:
$ ./wait
c
Exit child for command close.
However, if I uncomment the line "waitpid" after the write, both parent and child freeze. I don't know why. Based on extra printf
s added to the child, it looks like the child has not received the write operation, which is weird.
Is adding waitpid
after writting close
to the pipe causes the pipe contents not to be sent to the child somehow?
B = scanf("%s", cmd);
will block, attempting to consume as many non-whitespace characters as it can from the standard input. It will only stop when it encounters a whitespace character (as defined by isspace
), or when stdin
reaches end-of-file (or an error occurs).
(It is for reasons like this that an unbound %s
specifier makes scanf
as dangerous as gets()
. Always use a field-width specifier (e.g., %15s
) to prevent buffer overflows.)
When the parent exits, the last open write-end of the pipe is also closed, and thus the child sees end-of-file on its read-end of the pipe.
If the parent instead blocks on waitpid
, without having closed its write-end of the pipe, the processes will deadlock.
Use close(child.fd);
after the write
in the parent, before waitpid
.
Alternatively, writing the six bytes of "close "
to the pipe with strncmp(cmd, "close", 5)
in the child, and then waiting without closing the write-end of the pipe in the parent process highlights the issue.
Note that a parent process should always wait
for its child processes, otherwise you are creating orphan processes.