Background process is exiting faster than I can add its pid for management
I'm creating background processes in C using fork().
When I created one of these processes, I add its pid to an array so I can keep track of background processes.
pid = fork();
if(pid == -1)
{
printf("error: fork()\n");
}
else if(pid == 0)
{
execvp(*args, args);
exit(0);
}
else
{
// add process to tracking array
addBGroundProc开发者_开发技巧ess(pid, args[0]);
}
I have a handler for reaping zombies
void childHandler(int signum)
{
pid_t pid;
int status;
/* loop as long as there are children to process */
while (1) {
/* get zombie pids */
pid = waitpid(-1, &status, WNOHANG);
if (pid == -1)
{
if (errno == EINTR)
{
continue;
}
break;
}
else if (pid == 0)
{
break;
}
/* Remove this child from tracking array */
if (pid != mainPid)
cleanUpChild(pid);
}
}
When I create a background process, the handler is executing and attempting to clean up the child before I can even make the call to addBGroundProcess.
I'm using commands like emacs& which should not be exiting immediately.
What am I missing?
Thanks.
You're right, there is a race condition there. I suggest that you block the delivery of SIGCHLD
using the sigprocmask
function. When you have added the new PID to your data structure, unblock the signal again. When a signal is blocked, if that signal is received, the kernel remembers that it needs to deliver that signal, and when the signal is unblocked, it's delivered.
Here's what I mean, specifically:
sigset_t mask, prevmask;
//Initialize mask with just the SIGCHLD signal
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, &prevmask); /*block SIGCHLD, get previous mask*/
pid = fork();
if(pid == -1)
{
printf("error: fork()\n");
}
else if(pid == 0)
{
execvp(*args, args);
exit(0);
}
else
{
// add process to tracking array
addBGroundProcess(pid, args[0]);
// Unblock SIGCHLD again
sigprocmask(SIG_SETMASK, &prevmask, NULL);
}
Also, I think there's a possibility that execvp
could be failing. (It's good to handle this in general, even if it's not happening in this case.) It depends exactly how it's implemented, but I don't think that you're allowed to put a &
on the end of a command to get it to run in the background. Running emacs
by itself is probably what you want in this case anyway, and putting &
on the end of a command line is a feature provided by the shell.
Edit: I saw your comments about how you don't want emacs to run in the current terminal session. How do you want it to run, exactly - in a separate X11 window, perhaps? If so, there are other ways of achieving that.
A fairly easy way of handling execvp
's failure is to do this:
execvp(*args, args);
perror("execvp failed");
_exit(127);
Your code just catches the exit of the child process it fork'ed, which is not to say that another process wasn't fork'ed by that child first. I'm guessing that emacs in your case is doing another fork() on itself for some reason, and then allowing the initial process to exit (that's a trick daemons will do).
The setsid() function might also be worth looking at, although without writing up some code myself to check it I'm not sure if that's relevant here.
You should not be using the shell with &
to run background processes. If you do that, they come out as grandchildren which you cannot track and wait on. Instead you need to either mimic what the shell does to run background processes in your own code, or it would probably work just as well to close the terminal (or rather stdin/out/err) and open /dev/null
in its place in the child processes so they don't try to write to the terminal or take control of it.
精彩评论