標籤:
基本概念:子進程繼承父進程環境和內容相關的大部分內容的拷貝,其中就包括檔案描述符表。
父進程fork出來的子進程,複製父進程的檔案描述符。這些檔案描述符fd是獨立的,但是檔案描述符指向的系統檔案表項是唯一的,即是 struct file本身唯一。
同理,fork得到的子進程和父進程共用同一個socket(通訊端代表檔案)。fd與檔案關聯,通過綁定struct sockaddr通訊端地址空間,跟特定的ip和連接埠綁定在一起。
所以在子進程中accept(listen,....),雖然listen在不同進程中,代表不同的進程的檔案描述符,但是這個檔案描述符對應的通訊端是一樣的。又因為通訊端指定了通訊端地址,所以可以監聽來自用戶端的串連。
驚群的伺服器的模型:
父進程listen之後,子進程堵塞在accept函數這裡,這就是驚群發生的根本原因
驚群現象:
當父進程綁定一個連接埠監聽socket,然後fork出多個子進程,子進程們開始迴圈處理(比如accept)這個socket。每當使用者發起一個TCP串連時,多個子進程同時被喚醒,然後其中一個子進程accept新串連成功,餘者皆失敗,重新休眠。
驚群現象的危害:
在較老的unix系統中,當有串連到來時,accept()在每個阻塞在這的進程裡被喚醒。但是,只有這些進程中的一個能夠真正的accept這個串連,其他的進程accept將返回EAGAIN,驚群造成的結果是系統對使用者進程/線程頻繁的做無效的調度、環境切換,系統系能大打折扣。
解決:
我們不能只用一個進程去accept新串連嗎?然後通過訊息佇列等同步方式使其他子進程處理這些建立的串連,這樣驚群不就避免了?沒錯,驚群是避免了,但是效率低下,因為這個進程只能用來accept串連。對多核機器來說,僅有一個進程去accept,這也是程式員在自己創造accept瓶頸。所以,我仍然堅持需要多進程處理accept事件。
其實,在linux2.6核心上,accept系統調用已經不存在驚群了(至少我在2.6.18核心版本上已經不存在)。大家可以寫個簡單的程式試下,在父進程中bind,listen,然後fork出子進程,所有的子進程都accept這個監聽控制代碼。這樣,當新串連過來時,大家會發現,僅有一個子進程返回建立的串連,其他子進程繼續休眠在accept調用上,沒有被喚醒。(沒有被喚醒,繼續休眠)
解決方案:
linux驚群