2009-08-18 22 views
16

我正在研究一些基於Apache的MPM prefork服務器的Python代碼。我更像是一個應用程序員,而不是網絡程序員,從我讀史蒂文斯已經有10年了,所以我正在努力加快理解代碼。accept()與在多個進程之間共享的套接字(基於Apache預執行)

我發現了how Apache's prefork code works, by Sander Temme的簡短說明。

通常以root身份運行的父進程綁定到套接字 (通常是端口80或443)。它產生子代,繼承套接字的開放 文件描述符,並將uid和gid更改爲非特權用戶和組。孩子們構造了監聽器文件描述符(如果有多個監聽器) 的輪詢集 並監視其上的活動。如果找到活動,孩子在活動套接字上調用 accept()並處理連接。當它與 完成時,它返回到觀看pollset(或聽衆文件 描述符)。

由於多個子項都處於活動狀態,並且它們都繼承了相同的套接字文件描述符,所以它們將觀察同一個pollset。 一個接受互斥體只允許一個孩子實際觀看民意調查組, ,一旦發現一個活動的套接字,它將解鎖互斥體,所以 下一個孩子可以開始觀看民意調查組。如果只有一個 偵聽器,則不會使用接受互斥量,並且所有子級都將在 accept()中掛起。

這幾乎是我看到的代碼的工作方式,但我不明白一些事情。

1)「孩子」和「聽衆」之間有什麼區別?我認爲每個孩子都是一個聽衆,這對我所看到的代碼來說是真實的,但在Temme的描述中,可以有「單一的聽衆」和「孩子」。孩子什麼時候會有多個聽衆?

2)(與1相關)這是每進程互斥鎖還是系統互斥鎖?對於這個問題,爲什麼有一個互斥體?不接受(2)對所有聽衆都做自己的互斥嗎?我的研究表明,我確實需要一個互斥體,並且互斥體必須貫穿整個系統。 (羊羣,旗語等)

Temme接着說:在共享內存 區(記分牌)

兒童紀錄,當他們最後 服務的請求。空閒的孩子可能會被 父母程序殺死,致 滿足MaxSpareServers。如果太少, 孩子空閒,父母會 產卵孩子滿足 MinSpareServers。

3)是否有一個很好的參考代碼(最好在Python中)?我發現Perl的Net::Server::Prefork,它使用管道而不是共享內存作爲記分板。我發現了一篇Randal Schwartz的文章,它只執行preforking,但沒有記分牌。

pre-fork example from the Perl Cookbook沒有任何類型的鎖定選擇,Chris Siebenmann's Python example表示它基於Apache,但爲記分板使用配對套接字,而不是共享內存,並使用套接字控件,將給定子控件包含到'接受。這完全不符合Apache描述。

+0

你是否使用'mod_wsgi'作爲Apache和Python之間的接口?如果是這樣,它應該爲你處理所有這些。 – 2009-08-18 13:04:02

+0

這是一個純Python預執行WSGI服務器。我的客戶希望爲不需要Apache和mod_wsgi的地方提供輕量級解決方案,或者同等的解決方案。我找到的唯一一個只有Python的WSGI服務器是Spawning,它需要eventlet。 ......雖然現在我發現flup有一個像Siebenmann's的實現,它使用記分板的管道而不是共享內存,並且具有可接受的許可證給我的客戶端。 – 2009-08-18 13:23:13

回答

15

就(1)而言,監聽器僅僅是對接受連接的套接字的存在的引用。由於Apache可以同時接受多個套接字上的連接,例如,80/443,因此有多個偵聽套接字。每個子進程都需要在所有這些套接字上進行監聽。由於accept()一次只能在一個套接字上完成,因此它在poll/select之前,因此知道應在哪個偵聽器套接字上執行接受。

關於(2),它是一個全局或跨進程互斥體。也就是說,鎖定它的一個進程將阻止嘗試獲取相同鎖的其他進程。儘管accept()在技術上將序列化進程,但是多個偵聽器套接字的存在意味着你不能依賴它,因爲你不知道在哪個套接字上執行接受。即使在單個偵聽器套接字的情況下,接受互斥鎖的原因是,如果有大量進程處理請求,則操作系統喚醒所有進程以查看哪些接受了accept()返回,這可能會非常昂貴。由於Apache在prefork模式下可能有100多個進程,這可能會導致問題。因此,如果你只有一個監聽套接字,並且知道你只有幾個進程想要執行accept()調用,那麼你可能會取消跨進程接受互斥體。

+0

謝謝格雷厄姆!我完全沒有想過如何能有多個聽衆,以及這將如何影響一切。我的理解現在更清晰了,無論是爲什麼我使用的代碼有效,爲什麼它看起來沒有用。 – 2009-10-19 15:40:29