C 服务器程序无法分叉
Posted
技术标签:
【中文标题】C 服务器程序无法分叉【英文标题】:C server program unable to fork 【发布时间】:2018-09-18 01:08:51 【问题描述】:我有一个用 C 语言编写的服务器程序,它应该接受一个请求并派生一个子进程来处理该请求。以下是相关代码:
client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
if (client_sock < 0)
perror("accept failed");
return 1;
if((pid = fork()) < 0)
perror("fork");
exit(1);
else if (pid == 0)
close(socket_desc);
/* code to handle request */
close(client_sock);
// exit(0);
kill(getpid(), SIGKILL);
close(client_sock);
我想问,当我在子进程中使用exit(0)
或kill()
时,那个进程不是被杀死了吗?当我运行上述程序并向其发送请求时,大约 10000 个请求后,fork 调用失败并显示消息fork: Resource temporarily unavailable
。如果我可靠地杀死了分叉的进程,那么为什么 fork 调用会失败?另外,如何在不更改系统限制的情况下进行管理?
【问题讨论】:
如果你连接 5000 次,然后运行 ps aux
,它会显示 5000 个服务器进程吗?
我使用了ps -eo user=|sort|uniq -c
。随着请求的进入,我可以看到运行服务器计数的用户的进程计数增加。
父进程需要获取死掉的子进程(例如使用wait
/waitpid
,可能通过安装SIGCHLD
处理程序,或者只是将处理程序设置为SIG_IGN
,更多细节here);否则子进程会像僵尸一样等待父进程读取其退出状态。
我是否仍应将kill(getpid(), SIGKILL)
呼叫保留在子级中并在父级中捕获该信号?对吗?
@Jake 父进程需要获取死掉的子进程(例如,使用 wait/waitpid,可能通过安装 SIGCHLD 处理程序,或者只是将处理程序设置为 SIG_IGN,更多详细信息here);否则子进程会像僵尸一样等待父进程读取其退出状态。
【参考方案1】:
您无法分叉,可能是因为您已达到用户或系统对您可以创建的进程数量的限制。这样做的原因是,当您让孩子正确退出时,您并没有在父母之后“等待”他们。这意味着该进程没有得到清理,并且为它分配了某些资源。
处理此问题的一种方法是让父级响应 SIGCHLD,这是操作系统发送给父级以通知其子级终止的信号。
使用https://beej.us/guide/bgnet/html/multi/clientserver.html#simpleserver 中的示例,您可以添加一个函数供操作系统在事件发生时调用,然后使用 sys 调用通知操作系统您打算将此函数用于该信号。
你添加
void sigchld_handler(int s)
// waitpid() might overwrite errno, so we save and restore it:
int saved_errno = errno;
while(waitpid(-1, NULL, WNOHANG) > 0);
errno = saved_errno;
在你的主要功能中的某个时刻你会这样做
struct sigaction sa;
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1)
perror("sigaction");
exit(1);
好处是您可以使用此方法对来自操作系统的其他信号做出反应,并在孩子死亡时扩展它。
【讨论】:
【参考方案2】:根据@immibus 和@ShadowRanger 的建议,我对代码进行了如下修改,它运行良好(在我的服务器接受while 循环之外添加了信号调用):
signal(SIGCHLD, SIG_IGN);
...
while (1)
c = sizeof(struct sockaddr_in);
client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
if (client_sock < 0)
perror("accept failed");
return 1;
if((pid = fork()) < 0)
perror("fork");
exit(1);
else if (pid == 0)
close(socket_desc);
/* code to handle request */
close(client_sock);
// exit(0);
kill(getpid(), SIGKILL);
close(client_sock);
【讨论】:
以上是关于C 服务器程序无法分叉的主要内容,如果未能解决你的问题,请参考以下文章
处理对 Django Web 应用程序的计算密集型请求,可能使用预分叉 RPC 服务器