C服务器程序无法分叉

C服务器程序无法分叉,c,fork,C,Fork,我有一个用C编写的服务器程序,它应该接受一个请求并派生一个子进程来处理该请求。以下是相关代码: client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c); if (client_sock < 0) { perror("accept failed"); return 1; } if((pid = fork())

我有一个用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);
client\u sock=accept(socket\u desc,(struct sockaddr*)和client,(socklen\u t*)和c);
如果(客户_sock<0){
perror(“接受失败”);
返回1;
}
如果((pid=fork())<0){
佩罗尔(“福克”);
出口(1);
}如果(pid==0){
关闭(插座描述);
/*处理请求的代码*/
关闭(客户端_sock);
//出口(0);
kill(getpid(),SIGKILL);
}
关闭(客户端_sock);

我想问,当我在我的子进程中使用
exit(0)
kill()
时,该进程不是被杀死了吗?当我运行上述程序并向其发送请求时,在大约10000个请求之后,fork调用失败,并显示消息
fork:Resource temporary unavailable
。如果我正在可靠地终止fork进程,那么fork调用为什么会失败?另外,如何在不更改系统限制的情况下管理此问题?

您无法分叉,这可能是因为您在可以创建的进程数量上达到了用户或系统限制。这样做的原因是,当您让孩子们正确地退出时,您并不是在家长那里“等待”他们。这意味着流程不会被清理,并且会为其分配某些资源

处理这种情况的一种方法是让父级响应SIGCHLD,这是操作系统发送给父级的一个信号,通知它子级终止

使用中的示例,您为操作系统添加一个函数,以便在事件发生时调用,然后使用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);
}

很好的一点是,您可以使用此方法对来自操作系统的其他信号作出反应,以及在孩子死亡时扩展它

根据@immibus和@ShadowRanger的建议,我对代码进行了如下修改,效果很好(在我的服务器接受while循环之外添加了信号调用):

信号(SIGCHLD,SIG_IGN);
...
而(1){
c=sizeof(结构sockaddr_in);
client\u sock=accept(socket\u desc,(struct sockaddr*)和client,(socklen\u t*)和c);
如果(客户_sock<0){
perror(“接受失败”);
返回1;
}
如果((pid=fork())<0){
佩罗尔(“福克”);
出口(1);
}如果(pid==0){
关闭(插座描述);
/*处理请求的代码*/
关闭(客户端_sock);
//出口(0);
kill(getpid(),SIGKILL);
}
关闭(客户端_sock);
}

如果你连接5000次,比如说运行
ps aux
,它会显示5000个服务器进程吗?我使用了
ps-eo user=| sort | uniq-c
。我可以看到运行服务器计数的用户的进程计数随着请求的到来而增加。父进程需要获取死的子进程(例如,使用
wait
/
waitpid
,可能通过安装
SIGCHLD
处理程序,或者仅通过将处理程序设置为
SIG\u IGN
,更多详细信息);否则,子进程会像僵尸一样等待父进程读取其退出状态。我是否仍应保留子进程中的
kill(getpid(),SIGKILL)
调用并在父进程中捕获该信号?这是正确的吗?@Jake父进程需要获取死的子进程(例如,使用wait/waitpid,可能通过安装SIGCHLD处理程序,或者仅通过将处理程序设置为SIG_IGN,更多详细信息);否则,子进程就会像僵尸一样等待父进程读取其退出状态。
 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);
 }