创建可fork子进程的C守护进程时出现僵尸进程问题求助
Hey there! Let's break down why your child processes are turning into zombies after setting up the daemon, and how to fix it.
What's Causing the Zombies?
Looking at your ps -ajx output and code, the main culprit is likely incorrect error handling in your wait loop combined with potential unhandled failures in execv. Here's the breakdown:
- When
wait()gets interrupted by a signal (likeEINTR), your current code treats it as a child exit, decrementsnum_of_children, and eventually exits the loop. This leaves un-reaped child processes that become zombies. - If
execvfails (e.g., wrong path, missing execute permissions), the child process exits immediately—but if yourwaitloop isn't working right, it won't get reaped.
Step-by-Step Fixes
1. Fix the wait Loop Error Handling
Update your wait loop to properly handle interrupts and errors, so it only decrements the child count when a valid child exit is detected:
int status; pid_t pid; while(num_of_children > 0) { pid = wait(&status); if (pid == -1) { if (errno == EINTR) { // Wait was interrupted by a signal—keep waiting continue; } else if (errno == ECHILD) { // No more children to wait for, exit loop syslog(LOG_INFO, "No remaining child processes to monitor"); break; } else { // Log other wait errors and exit syslog(LOG_ERR, "Wait failed: %s", strerror(errno)); break; } } // Log valid child exit syslog(LOG_INFO, "Child %d exited with status 0x%x", pid, status); num_of_children--; }
This ensures you don't incorrectly count interrupts as child exits, and properly handle all edge cases for wait().
2. Add Error Handling for execv
If execv fails (returns instead of replacing the process), your child process will exit immediately—but you should log this failure to debug issues like missing executables:
if(i == 0) { syslog(LOG_INFO,"Forked message_server..."); char * argv_list[] = {"message_queue_server",NULL}; execv("./assignment_queue_server",argv_list); // If we reach here, execv failed syslog(LOG_ERR, "Failed to start message_queue_server: %s", strerror(errno)); exit(EXIT_FAILURE); } if(i == 1) { syslog(LOG_INFO,"Forked time_check..."); char * argv_list[] = {"time_check_process",NULL}; execv("./time_check_process",argv_list); // If we reach here, execv failed syslog(LOG_ERR, "Failed to start time_check_process: %s", strerror(errno)); exit(EXIT_FAILURE); }
Check your system logs (usually /var/log/syslog or /var/log/messages) for these error messages if you still see issues.
3. Improve Daemonization (Optional but Recommended)
Your current daemon setup misses a critical step: a second fork. This ensures your daemon isn't a session leader, preventing it from accidentally opening a terminal. Update your daemonization code:
int main() { int pid = fork(); if(pid > 0) { printf("Parent process exiting..\n"); exit(EXIT_SUCCESS); // No need to sleep—exit immediately } else if (pid == 0) { printf("First child process..\n"); if(setsid() < 0) { exit(EXIT_FAILURE); } // Second fork to avoid being session leader pid = fork(); if(pid > 0) { exit(EXIT_SUCCESS); } else if(pid == 0) { umask(0); if(chdir("/") < 0) { exit(EXIT_FAILURE); } // Close all file descriptors int x; for(x = sysconf(_SC_OPEN_MAX); x>=0;x--) { close(x); } openlog("assignment_daemon", LOG_PID|LOG_CONS,LOG_USER); syslog(LOG_INFO,"assignment_daemon started..."); // Rest of your code to fork child processes goes here } } return 0; }
This follows the standard Unix daemonization best practices.
Additional Troubleshooting Tips
- Verify that
./assignment_queue_serverand./time_check_processexist and have execute permissions (runls -lon them). - Check your system logs to confirm if the child processes are starting successfully or failing to execute.
- Remove the
sleep(10)in your initial parent process—it's unnecessary and can delay daemon startup.
内容的提问来源于stack exchange,提问作者Pony1487

