【问题标题】:Why select() in a parent process makes accept() unusable in a child process?为什么父进程中的 select() 使得 accept() 在子进程中无法使用?
【发布时间】:2012-04-25 21:46:18
【问题描述】:

我有一个父进程,它创建 2 个服务器套接字并在它们上调用 select() 以等待新连接。当连接到达时,会向子进程发送一条消息(使用fork() 创建,在服务器套接字创建之后,因此它们是共享的)。

在这个孩子中,在服务器套接字上调用 accept() 不起作用。我收到EAGAIN 错误(非阻塞套接字)。而在主进程中调用accept() 效果很好。

当然,我根本不会在主进程中调用accept(),我只是测试一下它是否有效。

为什么我不能在父进程中的 select() 之后在子进程中调用 accept()

编辑:这里的目标是创建固定数量的工作人员(比如说 8 个)来处理客户端连接,就像在 prefork 模型中一样。这些连接将是长连接,不像 HTTP。目标是负载平衡工作人员之间的连接。

为此,我使用了一个共享内存变量,其中包含一个工作人员当前连接的客户端的数量。我想“要求”客户端数量最少的工作人员来处理新连接。

这就是为什么我在父进程中执行select(),然后向子进程发送消息,因为我想“选择”哪个进程将处理新连接。

服务器侦听多个套接字(一个用于 ssl,一个不用于),这就是为什么我在子进程中使用 select() 而不是直接使用 accept(),因为我不能在我的多个套接字上使用 accept()童工。

【问题讨论】:

  • EAGAIN 本身并不是一个真正的错误,它只是意味着调用是非阻塞的,但没有准备好连接。睡一会再试。当然,除 EAGAIN 之外的任何其他错误都是实际错误。
  • 为什么不在分叉前先调用accept?特别是因为您知道它有效。
  • 我不明白你在做什么。您能否发布一些最少的代码,以便我们重现您的观察结果?
  • FastCGI / SCGI pre-fork的可能重复
  • 如果您在父进程中打开文件描述符,您可以使用Unix-domain socket magic将该描述符传递给子进程。

标签: python linux sockets process


【解决方案1】:

事实上,问题并不是我最初想的那样。以下是我为在工作进程之间的连接进行一些基本负载平衡所做的工作的回顾。

  • 一个主进程(父进程)创建 2 个服务器套接字,bind() 和 listen() 它们(例如有和没有 ssl)
  • 我使用 fork() 创建了 8 个子进程,因此它们继承父级的套接字
  • 主进程无限循环运行select()
  • 当它的两个套接字中的一个可用时,它通过管道向一个子节点发送一条消息。子进程由共享内存值确定,其中包含“在子进程中”的当前客户端数量。选择当前处理最少客户端数量的进程。
  • 这个子进程然后在服务器套接字上调用accept()(两者之间使用的套接字是在管道中传递的,所以子进程知道在哪一个上调用accept()

问题是我的父进程告诉子进程接受套接字并立即重新进入循环,它再次运行select()。但是如果孩子还没有接受套接字,select() 再次返回,用于相同的连接。这就是我收到 EAGAIN 错误的原因,实际上我调用了两次 accept()(或更多次数,具体取决于速度进程间竞争条件)!

解决方法是等待孩子在管道上回答“嘿,我接受了连接,没关系!”,然后返回到select() 循环。

这工作得很好。好奇的人可以在这里找到 Python 中的实现:https://github.com/thibautd/Kiwi

【讨论】:

    猜你喜欢
    • 2021-12-31
    • 1970-01-01
    • 2017-06-29
    • 1970-01-01
    • 1970-01-01
    • 2020-01-22
    • 2014-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多