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 服务器

似乎无法在 SBT 中的分叉 JVM 中运行 Play 应用程序

C/C++程序中的损坏堆栈问题

意外的分叉行为

Nginx,无法分叉:无法分配内存

如何在分叉进程中处理套接字连接