问题
I fail at fork 101. I expect this to fork a child process, and output both child and parent printfs:
pid_t fpid;
if ((fpid = fork()) < 0)
{
printf("fork: %s\n", strerror(errno));
exit(-1);
}
if (0 == fpid) // child
{
printf("\nI am the child\n");
}
else
{
printf("\nI am the parent\n");
pid_t wpid;
while ((wpid = waitpid(WAIT_ANY, NULL, 0)))
{
if (errno == ECHILD)
break;
else if (wpid < 0)
printf("wait: %s\n", strerror(errno));
}
}
Instead, I get this output:
I am the parent
wait: Interrupted system call
So my question is: why doesn't the child get a chance to live and run? Won't someone please think of the children! Also, where does the EINTR come from? Obviously, this is somehow related to my first question.
Furthermore, when I run that code in a standalone program, it works correctly, but not when inside a larger program of mine; what could the larger program do to upset waitpid?
FWIW this is on OSX 10.9.
回答1:
On OSX, it's not legal to do much on the child side of a fork before/without execing. See the caveat at the bottom of the fork man page. The list of safe functions is on the sigaction(2) man page. printf()
is not among them.
Also, stdout is likely buffered. The results of the printf()
may not be being flushed. It would be flushed if you called exit()
, but that's also not legal on the child side of a fork. (It's appropriate to use _exit()
but that doesn't flush open streams.) As it is, you don't appear to be exiting your child process, which means flow of execution continues to the caller of the code you've shown, presumably returning to the rest of your program. It may be getting stuck there because of the limitations on the child side of a fork.
You may have more luck if you do something like this in the child:
const char msg[] = "\nI am the child\n";
write(STDOUT_FILENO, msg, sizeof(msg) - 1);
_exit(0);
Finally, I think you should pass fpid
rather than WAIT_ANY
to waitpid()
. You have a specific child process you want to wait for. In the context of a larger program, you don't want to steal the notification of the termination of a child spawned by some other subcomponent. And you always need to loop around interruptible syscalls until they return something other than EINTR
.
回答2:
Only check errno
if a failure was indicated, for example by a system call returning -1
.
The code should look like this:
pid_t fpid;
if ((fpid = fork()) < 0)
{
printf("fork: %s\n", strerror(errno));
exit(-1);
}
if (0 == fpid) // child
{
printf("\nI am the child\n");
}
else
{
printf("\nI am the parent\n");
pid_t wpid;
while ((wpid = waitpid(WAIT_ANY, NULL, 0)))
{
if (-1 == wpid)
{
perror("waitpid() failed");
}
else if (wpid == fpid)
{
/* My child ended, so stop waiting for it. */
break;
}
}
}
来源:https://stackoverflow.com/questions/20555550/fork-wait-gets-eintr-on-osx