Python Select 解析

首先列一下,sellect、poll、epoll三者的區別 
select 
select最先於1983年出如今4.2BSD中,它經過一個select()系統調用來監視多個文件描述符的數組(在linux中一切事物皆文件,塊設備,socket鏈接等。),當select()返回後,該數組中就緒的文件描述符便會被內核修改標誌位(變成ready),使得進程能夠得到這些文件描述符從而進行後續的讀寫操做(select會不斷監視網絡接口的某個目錄下有多少文件描述符變成ready狀態【在網絡接口中,過來一個鏈接就會創建一個'文件'】,變成ready狀態後,select就能夠操做這個文件描述符了)。linux

【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)。socket

epoll 
直到Linux2.6纔出現了由內核直接支持的實現方法,那就是epoll,它幾乎具有了以前所說的一切優勢,被公認爲Linux2.6下性能最好的多路I/O就緒通知方法。性能

epoll能夠同時支持水平觸發和邊緣觸發(Edge Triggered,只告訴進程哪些文件描述符剛剛變爲就緒狀態,它只說一遍,若是咱們沒有采起行動,那麼它將不會再次告知,這種方式稱爲邊緣觸發),理論上邊緣觸發的性能要更高一些,可是代碼實現至關複雜。線程

epoll一樣只告知那些就緒的文件描述符,並且當咱們調用epoll_wait()得到就緒文件描述符時,返回的不是實際的描述符,而是一個表明 就緒描述符數量的值,你只須要去epoll指定的一個數組中依次取得相應數量的文件描述符便可,這裏也使用了內存映射(mmap)技術,這樣便完全省掉了 這些文件描述符在系統調用時複製的開銷。

另外一個本質的改進在於epoll採用基於事件的就緒通知方式。在select/poll中,進程只有在調用必定的方法後,內核纔對全部監視的文件描 述符進行掃描,而epoll事先經過epoll_ctl()來註冊一個文件描述符,一旦基於某個文件描述符就緒時,內核會採用相似callback的回調 機制,迅速激活這個文件描述符,當進程調用epoll_wait()時便獲得通知。

相關文章
相關標籤/搜索