我正在开发一些以Apache的MPM prefork服务器为模型的Python代码。我是一名应用程序员,而不是网络程序员,自从我读Stevens已经10年了,所以我正努力加快理解代码。
我找到了how Apache's prefork code works, by Sander Temme的简短说明。
父进程通常以root身份运行,绑定到套接字 (通常是80或443端口)。它产生了继承开放的孩子 套接字的文件描述符,并将uid和gid更改为 无特权的用户和组。孩子们构建了一个pollset 监听器文件描述符(如果有多个监听器) 并注意它/他们的活动。如果找到活动,则孩子打电话 在活动套接字上接受()并处理连接。几时 完成后,它返回到观察pollset(或监听器文件) 描述符)。
由于多个孩子都是活跃的,他们都继承了同样的孩子 套接字文件描述符,他们将观看相同的pollset。 接受互斥锁只允许一个孩子实际观看轮询集, 一旦找到一个活动的套接字,它将解锁互斥锁 下一个孩子可以开始观看民意调查。如果只有一个 听众,不接受互斥,并且所有孩子都会挂在上面 接受()。
这几乎就是我所看到的代码的工作方式,但我不了解一些事情。
1)“孩子”和“听众”之间有什么区别?我认为每个孩子都是一个倾听者,这对我正在看的代码都是如此,但在Temme的描述中,可以有“单个听众”和“孩子”。孩子什么时候会有多个听众?
2)(与1相关)这是一个进程互斥锁还是系统互斥锁?就此而言,为什么要使用互斥锁?不接受(2)在所有听众中做自己的互斥量?我的研究表明我确实需要一个互斥锁,并且互斥锁必须跨越整个系统。 (flock,semaphore等)
Temme接着说:
儿童在共享记忆中记录 区域(记分牌),当他们持续 提出要求。闲暇的孩子可能会 被父进程杀死 满足MaxSpareServers。如果太少了 孩子们闲着,父母会 产卵儿童满足 比MinSpareServers。
3)这个实现是否有一个很好的参考代码(最好是在Python中)?我找到了Perl的Net::Server::Prefork,它使用管道而不是记分板的共享内存。我在Randal Schwartz找到了一篇文章,它只做了预备,但没有做记分牌。
pre-fork example from the Perl Cookbook在select周围没有任何锁定,Chris Siebenmann's Python example表示它基于Apache但是使用配对套接字作为记分板,而不是共享内存,并使用套接字进行控制,包括控制给定的孩子'a'ccept。这根本不符合Apache描述。
答案 0 :(得分:16)
关于(1),监听器仅仅是对存在接受连接的套接字的引用。由于Apache可以同时接受多个套接字上的连接,例如80/443,因此有多个侦听器套接字。当它到来时,每个子进程都需要监听所有这些套接字。由于accept()一次只能在一个套接字上完成,因此它之前是poll / select,因此可以知道哪个侦听器套接字应该执行accept。
就(2)而言,它是一个全局或跨进程的互斥体。也就是说,锁定它的一个进程将阻止尝试获取相同锁的其他进程。尽管accept()在技术上会将进程序列化,但是多个侦听器套接字的存在意味着你不能依赖它,因为你事先不知道哪个套接字要执行接受。即使在单个侦听器套接字的情况下,接受互斥的原因是如果有大量进程处理请求,那么如果操作系统唤醒所有进程以查看哪个进程接受()返回它,则可能相当昂贵。由于Apache在prefork模式下可能有100多个进程,这可能会导致问题。
因此,如果您只有一个侦听器套接字并且知道只有少数进程想要执行accept()调用,那么您可以取消跨进程接受互斥锁。