天天看點

Python Select 解析

select poll epoll

首先列一下,sellect、poll、epoll三者的差別 

select 

select最早于1983年出現在4.2BSD中,它通過一個select()系統調用來監視多個檔案描述符的數組(在linux中一切事物皆檔案,塊裝置,socket連接配接等。),當select()傳回後,該數組中就緒的檔案描述符便會被核心修改标志位(變成ready),使得程序可以獲得這些檔案描述符進而進行後續的讀寫操作(select會不斷監視網絡接口的某個目錄下有多少檔案描述符變成ready狀态【在網絡接口中,過來一個連接配接就會建立一個'檔案'】,變成ready狀态後,select就可以操作這個檔案描述符了)。

【socketserver是通過多線程來處理多個請求,每個連接配接過來配置設定一個線程來處理,但是select是單程序的,一個程序執行代碼肯定就是串行的,但是現在就要通過一個程序來實作并發的效果,一個程序下隻有一個主線程,也就說說用一個線程實作并發的效果。為什麼要用一個程序實作多并發而不采用多線程實作多并發呢?==========答:因為一個程序實作多并發比多線程是實作多并發的效率還要高,因為啟動多線程會有很多的開銷,而且CPU要不斷的檢查每個線程的狀态,确定哪個線程是否可以執行。這個對系統來說也是有壓力的,用單程序的話就可以避免這種開銷和給系統帶來的壓力,那麼單程序是如何實作多并發的呢???========答:很巧妙的使用了生産者和消費者的模式(異步),生産者和消費者可以實作非阻塞,一個socketserver通過select接收多個連接配接過來(之前的socket一個程序隻能接收一個連接配接,當接收新的連接配接的時候産生阻塞,因為這個socket程序要先和用戶端進行通信,二者是彼此互相等待的【用戶端發一條消息,服務端收到,用戶端等着傳回....服務端等着接收.........】一直在阻塞着,這個時候如果再來一個連接配接,要等之前的那個連接配接斷了,這個才可以連進來。-----------也就是說用基本的socket實作多程序是阻塞的。為了解決這個問題采用每來一個連接配接産生一個線程,是不阻塞了,但是當線程數量過多的時候,對于cpu來說開銷和壓力是比較大的。)對于單個socket來說,阻塞的時候大部分的時候都是在等待IO操作(網絡操作也屬于IO操作)。為了避免這種情況,就出現了異步=============用戶端發起一個連接配接,會在服務端注冊一個檔案句柄,服務端會不斷輪詢這些檔案句柄的清單,主程序和用戶端建立連接配接而沒有啟動線程,這個時候主程序和用戶端進行互動,其他的用戶端是無法連接配接主程序的,為了實作主程序既能和已連接配接的用戶端收發消息,又能和新的用戶端建立連接配接,就把輪詢變的非常快(死循環)去刷用戶端連接配接進來的檔案句柄的清單,隻要用戶端發消息了,服務端讀取了消息之後,有另一個清單去接收給用戶端傳回的消息,也不斷的去刷這個清單,刷出來後傳回給用戶端,這樣和用戶端的這次通信就完成了,但是跟用戶端的連接配接還沒有斷,但是就進入了下一次的輪詢。。。。。。。。。。。】

select目前幾乎在所有的平台上支援,其良好跨平台支援也是它的一個優點,事實上從現在看來,這也是它所剩不多的優點之一。

select的一個缺點在于單個程序能夠監視的檔案描述符的數量存在最大限制,在Linux上一般為1024,不過可以通過修改宏定義甚至重新編譯核心的方式提升這一限制。

另外,select()所維護的存儲大量檔案描述符的資料結構,随着檔案描述符數量的增大,其複制的開銷也線性增長。同時,由于網絡響應時間的延遲

使得大量TCP連接配接處于非活躍狀态,但調用select()會對所有socket進行一次線性掃描,是以這也浪費了一定的開銷。

poll 

poll在1986年誕生于System V Release 3,它和select在本質上沒有多大差别,但是poll沒有最大檔案描述符數量的限制。

poll和select同樣存在一個缺點就是,包含大量檔案描述符的數組被整體複制于使用者态和核心的位址空間之間,而不論這些檔案描述符是否就緒,它的開銷随着檔案描述符數量的增加而線性增大。

另外,select()和poll()将就緒的檔案描述符告訴程序後,如果程序沒有對其進行IO操作,那麼下次調用select()和poll()

的時候将再次報告這些檔案描述符,是以它們一般不會丢失就緒的消息,這種方式稱為水準觸發(Level Triggered)。

epoll 

直到Linux2.6才出現了由核心直接支援的實作方法,那就是epoll,它幾乎具備了之前所說的一切優點,被公認為Linux2.6下性能最好的多路I/O就緒通知方法。

做有積累的事~~