我正在开发一些模仿Apache MPM prefork服务器的Python代码。我更擅长应用程序开发而不是网络编程,并且距离我阅读Stevens已经过去了10年,所以我正试图提高理解这段代码的能力。
我找到了Sander Temme关于Apache预分配代码如何工作的简短描述。
父进程通常作为root用户运行,绑定到一个socket上(通常是端口80或443)。它会生成子进程,子进程继承打开的文件描述符并将uid和gid更改为非特权用户和组。子进程构建监听器文件描述符的pollset(如果有多个监听器)并监视其中的活动情况。如果发现有活动,子进程就在活动的socket上调用accept()并处理连接。完成后,子进程返回监视pollset(或监听器文件描述符)状态。
由于有多个活跃的子进程并且它们都继承了相同的socket文件描述符,它们将监视相同的pollset。一个accept mutex允许只有一个子进程实际上监视pollset,在找到活动的socket后,它将解锁mutex,以便下一个子进程可以开始监视pollset。如果只有一个监听器,则不使用该accept mutex,所有子进程都将在accept()中挂起。
这基本上就是我正在研究的代码工作方式,但我不理解其中的一些内容。
1)“child”和“listener”之间有什么区别?我认为每个子进程都是一个监听器,在我看的代码中也是如此,但是在Temme的描述中可以有“单个监听器”和“子进程”。什么情况下子进程会有多个监听器?
2) (与1相关)这是进程级互斥锁还是系统级互斥锁?说起来,为什么需要互斥锁?accept(2)难道不会在所有监听器上自己进行互斥锁操作吗?我的研究表明我确实需要一个互斥锁,而且该互斥锁必须跨整个系统。(flock、semaphore等)
Temme接着说:
子进程在共享内存区域(记分牌)中记录它们最后提供服务的时间。空闲的子进程可能会被父进程杀死,以满足MaxSpareServers的要求。如果空闲的子进程太少,父进程将生成更多的子进程以满足MinSpareServers。
3) 是否有这种实现的好参考代码(最好用Python)?我发现Perl的Net::Server::Prefork使用管道而不是记分牌的共享内存。我发现了Randal Schwartz的一篇文章,但它只做了预分叉而没有做记分牌。
Perl Cookbook的预分叉示例没有在select周围进行任何形式的锁定,而Chris Siebenmann的Python示例说它是基于Apache的,但使用成对的套接字来进行记分牌,而不是共享内存,并使用这些套接字进行控制,包括给定子进程的“接受”控制。这完全不符合Apache的描述。
mod_wsgi
作为 Apache 和 Python 之间的接口?如果是,它应该会为你处理所有这些问题。 - S.Lott