代码之家  ›  专栏  ›  技术社区  ›  Matteo

这是关闭fork上套接字描述符的正确方法吗?

  •  2
  • Matteo  · 技术社区  · 8 年前

    考虑以下代码:

    socket_fd = start_server(port);
    
    while (1){
    
        new_socket_fd = accept_client(socket_fd);
    
        int pid = fork();
    
        if (pid == 0){
    
            //I am the child server process
    
            close(socket_fd);      <------(1)
    
            do_stuff_with_client(new_socket_fd, buffer);
    
            close(new_socket_fd);       <------(2)
    
            exit(0);
    
        } else if (pid > 0){
    
            //I am the parent server process
    
            close(new_socket_fd);      <------(3)
    
        } else {
    
            fprintf(stderr, "Fork error\n");
            return 1;
        }
    }
    

    据我所知,当一个进程调用fork()时,它的地址空间是重复的,但不是共享的,因此如果我更改子进程的变量或关闭文件描述符,它不会影响父进程。

    也就是说,在服务器接受新连接(从而创建 new_socket_fd )它自己分叉,孩子关上了 socket_fd 插座fd

    子进程处理请求,然后关闭其 新插座fd (2) 然后退出。

    当子进程执行所有这些操作时,父进程已经关闭 新插座fd (3) 因为连接正在由子级处理。

    1 回复  |  直到 8 年前
        1
  •  5
  •   Jonathan Leffler    8 年前

    将评论流转换为答案。

    TL;博士

    在某种程度上,您的父进程应该等待已死亡的子进程,以防止僵尸的累积(但它不应该阻止,直到子进程死亡)。使用 waitpid() 在循环中 WNOHANG 在父循环关闭 new_socket_fd SIGCHLD (因此永远不会创建僵尸),或者您可以安排定期唤醒,在此期间父进程检查僵尸。

    讨论

    babon asked

    家长关闭 socket_fd 当它退出循环或被告知停止侦听套接字时。在所示的代码中没有真正的规定,因此当父进程被终止(或发生fork故障)时,它将被关闭。关键是,侦听套接字可以用于许多连接,在您完成侦听之前,您不想在父连接中关闭它。

    Matteo noted

    listen(socket_fd, N) .

    listen() call是可排队等待侦听过程的未完成连接数。也就是说,尚未通过返回值的连接请求数 accept() 呼叫这并不是对连接后可以同时活动的连接数的限制 接受()

    Ajay Brahmakshatriya asked

    在孩子关闭 插座fd

    传入数据包与套接字的“打开文件描述”(或与“文件描述符”或“套接字描述符”不同的等效项)相关联。它可供父母或孩子使用,无论谁先阅读。类似地,传入的连接请求在 插座fd

    马特奥 commented

    阿贾伊 responded

    如果是这种情况,同样的情况也应该发生在 新插座fd 因为两个都是开放的。这意味着,在家长关闭数据包之前,孩子将无法读取数据包。这可能导致比赛条件。

    这是基于误解。数据包通过文件描述符可供两个进程使用。当进程关闭文件描述符时,它不再能够访问发送到连接的信息(当然,也不能发送该连接上的数据)。在那之前,除非参与者进程在谁读取数据和谁侦听连接方面达成一致,否则只能靠抽奖才能看到什么

    马特奥 responded

    但文件描述符不应该干涉父级和子级;这就是为什么关闭 在孩子方面,并没有阻止父母倾听。

    commented

    同意。但我觉得你应该关门 插座fd 在while循环之后。万一明天你把回路换成因某种情况而断开,你就冒着无缘无故保持插座打开的风险。

    while (1) 循环,故障模式执行 return 在执行该操作之前,它可以关闭插座 ). 如果程序退出,那么系统会关闭套接字,因此这并不重要,总之,关闭打开的内容是很好的管理。

    阿贾伊 notes

    描述符不同,但它们所指的套接字连接是相同的。该数据包可用于任何读取它的父进程或子进程。

    responded

    在我的例子中 accept_client 创建 sockaddr 结构,因此4元组转到child的 .

    这不太准确。第一 accept_client() 在有孩子之前就叫;这个 当该函数完成时,位于父级中(仅限)。第二,在 fork() ,两个进程都可以访问 新插座fd 新插座fd