到目前为止,没有一种解决方案可以不使用SIGCHLD作为问题请求的方法。以下是使用
poll的替代方法的实现,如
此答案所述(它还解释了为什么在这种情况下应避免使用SIGCHLD):
确保您对创建的每个子进程都有一个管道。它可以是它们的stdin/stdout/stderr或只是额外的虚拟文件描述符。当子进程终止时,它的管道末端将关闭,您的主事件循环将检测到该文件描述符上的活动。从它关闭的事实中,您可以识别出子进程已经停止运行,并调用waitpid来收回僵尸进程。
(注意:出于简洁起见,我省略了一些最佳实践,例如错误检查和清理文件描述符)
#define MAX_CLIENT_COUNT 1000
struct process_table {
pid_t clientpids[MAX_CLIENT_COUNT];
struct pollfd clientfds[MAX_CLIENT_COUNT];
} PT;
void initialize_table() {
for (int i = 0; i < MAX_CLIENT_COUNT; i++) {
PT.clientfds[i].fd = -1;
}
}
int get_next_available_entry() {
for (int i = 0; i < MAX_CLIENT_COUNT; i++) {
if (PT.clientfds[i].fd == -1) {
return i;
}
}
return -1;
}
void add_process_to_table(int i, pid_t pid, int fd) {
PT.clientpids[i] = pid;
PT.clientfds[i].fd = fd;
}
void remove_process_from_table(int i) {
PT.clientfds[i].fd = -1;
}
void reap_zombie_processes() {
int p = poll(PT.clientfds, MAX_CLIENT_COUNT, 0);
if (p > 0) {
for (int i = 0; i < MAX_CLIENT_COUNT; i++) {
if ((PT.clientfds[i].revents & POLLHUP) != 0) {
waitpid(PT.clientpids[i], NULL, 0);
remove_process_from_table(i);
}
}
}
}
void accept() {
sleep((rand() % 4) + 1);
}
void childfunction() {
sleep((rand() % 10) + 1);
exit(0);
}
int main() {
initialize_table();
while (1) {
accept();
int p[2];
pipe(p);
pid_t cpid = fork();
if (cpid == 0) {
close(p[0]);
childfunction();
}
else {
close(p[1]);
int i = get_next_available_entry();
add_process_to_table(i, cpid, p[0]);
reap_zombie_processes();
}
}
return 0;
}
以下是取消注释
printf
语句后运行程序的示例输出:
[31066] started
[31067] started
[31068] started
[31069] started
[31066] done
[31070] started
[31067] done
[31068] done
[31071] started
[31069] done
[31072] started
[31070] done
[31073] started
[31074] started
[31072] done
[31075] started
[31071] done
[31074] done
[31081] started
[31075] done