1. 程式人生 > >UNIX網路程式設計2 理解select、poll、epoll原理

UNIX網路程式設計2 理解select、poll、epoll原理

三者實現原理對比分析 select, poll, epoll都是IO多路複用的機制,上文中提到的多路複用主要是以select為例,select和poll大同小異,因為select和poll的實現有明顯的缺點,所以在Linux2.5.44中引入了新的處理大批量控制代碼的API——epoll,被公認為Linux2.6下效能最好的多路I/O就緒通知方法。這裡還是要強調,I/O多路複用機制的目的是應對大量的描述符,並不是所有情況下效能都是最好的(相比於直接的blocking、non-blocking、asynchronous),select、poll、epoll本質上都是同步I/O,因為它們都需要在讀寫事件就緒後自己負責進行讀寫,也就是說這個讀寫過程是阻塞的(上文也得出了這個結論,epoll使用mmap機制省去了核心空間到使用者空間的資料拷貝,似乎可以認為是非同步的)。 select和poll的實現比較相似,重點還是以select為例,epoll可以說是select和poll的增強版,優化了幾個方面的效能。 1. select實現原理 1)使用copy_from_user從使用者空間拷貝fd_set到核心空間 2)註冊回撥函式__pollwait 3)遍歷所有fd,呼叫其對應的poll方法(對於socket這個poll方法是sock_poll,sock_poll根據情況會呼叫到tcp_poll、udp_poll或者datagram_poll) 4)以tcp_poll為例,其核心實現就是__pollwait,也就是上面註冊的回撥函式 5)__pollwait的主要工作就是把current(當前程序)掛到裝置的等待佇列中,不同的裝置有不同的等待佇列,對於tcp_poll來說,其等待佇列是sk->sk_sleep(把程序掛到等待佇列中並不代表程序已經睡眠)。在裝置收到一條訊息或填寫完檔案資料後,會喚醒裝置等待佇列上的程序,這時current便被喚醒了 6)poll方法返回時會返回一個描述讀寫操作是否就緒的掩碼,根據這個掩碼給fd_set賦值 7)如果遍歷完所有的fd,還沒有返回一個可讀寫的掩碼,則會呼叫schedule_timeout使呼叫select的程序(也就是current)進入睡眠。當裝置驅動發現自身資源可讀寫後,會喚醒其等待佇列上睡眠的程序。如果超過一定的超時時間(schedule_timeout指定)還是沒被喚醒,則呼叫select的程序會重新被喚醒獲得CPU,進而重新遍歷fd,判斷有沒有就緒的fd 8)把fd_set從核心空間拷貝到使用者空間 select 的幾個缺點: 1)每次呼叫select,都需要把fd集合從使用者空間拷貝到核心空間,這個開銷在fd很多時會很大 2)每次呼叫select都需要在核心遍歷傳遞進來的所有fd,這個開銷在fd很多時也會很大 3)select支援的檔案描述符數量太小了,預設是1024 2. poll的實現 和select非常相似,只是描述fd集合的方式不同,poll使用pollfd結構而不是select的fd_set結構。其他差不多。 select回撥機制,把current加入fd對應的裝置等待佇列時使用的程式碼:
  1. static
     void
     __pollwait(struct file *filp, wait_queue_head_t *wait_address,  
  2.                 poll_table *p)  
  3. {  
  4.     struct poll_table_entry *entry = poll_get_entry(p);  
  5.     if (!entry)  
  6.         return;  
  7.     get_file(filp);  
  8.     entry->filp = filp;  
  9.     entry->wait_address = wait_address;  
  10.     //設定等待的程序為current,回撥函式為default_wake_function
  11.     init_waitqueue_entry(&entry->wait, current);  
  12.     //新增到等待佇列中
  13.     add_wait_queue(wait_address, &entry->wait);  
  14. }  

其中init_waitqueue_entry實現如下:

  1. static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)  
  2. {  
  3.     q->flags = 0;  
  4.     q->private = p;  
  5.     q->func = default_wake_function;  
  6. }  
上面的程式碼是說建立一個poll_table_entry結構entry,首先把current設定為entry->wait的private成員,同時把default_wake_function設為entry->wait的func成員,然後把entry->wait鏈入到wait_address中(這個wait_address就是裝置的等待佇列,在tcp_poll中就是sk_sleep)。 3. epoll實現 我們重點看一下epoll是克服select/poll的三個明顯缺點的。 在呼叫介面上,select和poll都只提供了一個函式——select或者poll函式。而epoll提供了三個函式:epoll_create、epoll_ctl和epoll_wait。epoll_create是建立一個epoll控制代碼,epoll_ctl是註冊要監聽的事件型別,epoll_wait是等待事件的產生。 對於第一個缺點,epoll的解決方案在epoll_ctl函式中。每次註冊新的事件到epoll控制代碼中時(在epoll_ctl中指定EPOLL_CTL_ADD),會把所有的fd拷貝進核心,而不是在epoll_wait的時候重複拷貝。epoll保證了每個fd在整個過程中只會拷貝一次。 對於第二個缺點,epoll的解決方案不像select或poll一樣每次都把current輪流加入fd對應的裝置等待佇列中,而只在epoll_ctl時把current掛一遍,併為每個fd指定一個回撥函式,當裝置就緒,喚醒等待佇列上的等待者,就會呼叫這個回撥函式,而這個回撥函式會把就緒的fd加入一個就緒連結串列。epoll_wait的工作實際上就是在這個就緒連結串列中檢視有沒有就緒的fd(就緒連結串列是否為空)。 對於第三個缺點,epoll沒有這個限制,它所支援的FD上限是最大可以開啟檔案的數目,這個數字一般遠大於2048,具體可以cat /proc/sys/fs/file-max檢視,在1GB記憶體的機器上大約是10萬左右。 epoll回撥機制:
  1. /* 
  2.  * This is the callback that is used to add our wait queue to the 
  3.  * target file wakeup lists. 
  4.  */  
  5. static void ep_ptable_queue_proc(struct file *file, wait_queue_head_t *whead,  
  6.                  poll_table *pt)  
  7. {  
  8.     struct epitem *epi = ep_item_from_epqueue(pt);  
  9.     struct eppoll_entry *pwq;  
  10.     if (epi->nwait >= 0 && (pwq = kmem_cache_alloc(pwq_cache, GFP_KERNEL))) {  
  11.         init_waitqueue_func_entry(&pwq->wait, ep_poll_callback);  
  12.         pwq->whead = whead;  
  13.         pwq->base = epi;  
  14.         add_wait_queue(whead, &pwq->wait);  
  15.         list_add_tail(&pwq->llink, &epi->pwqlist);  
  16.         epi->nwait++;  
  17.     } else {  
  18.         /* We have to signal that an error occurred */  
  19.         epi->nwait = -1;  
  20.     }  
  21. }  

其中init_waitqueue_func_entry的實現如下:

  1. static inline void init_waitqueue_func_entry(wait_queue_t *q,  
  2.                     wait_queue_func_t func)  
  3. {  
  4.     q->flags = 0;  
  5.     q->private = NULL;  
  6.     q->func = func;  
  7. }  
可以看到,總體上和select實現是類似的,只不過它是建立了一個epoll_entry結構pwq,pwq->wait的func成員被設定成了回撥函式ep_poll_callback(而不是default_wake_function,所以這裡並不會有喚醒操作而只是執行回撥函式),private成員被設定成了NULL。最後把pwq->wait鏈入到whead中(也就是裝置等待佇列中)。這樣,當裝置等待佇列中的程序被喚醒時,就會呼叫ep_poll_callback了。 重新梳理一下epoll的流程: 當epoll_wait時,它會判斷就緒連結串列中有沒有就緒的fd,如果沒有,則把current程序加入到一個等待佇列(file->private_data->wq)中,並在一個while(1)迴圈中判斷就緒佇列是否為空,並結合schedule_timeout實現睡一會。如果current程序在睡眠中,裝置就緒了,就會呼叫回撥函式。在回撥函式中,會把就緒的fd放到就緒連結串列,並喚醒等待佇列(file->private_data->wq)中的current程序,這樣epoll_wait又能繼續執行下去了。 總結: 1)epoll呼叫epoll_wait輪詢就緒連結串列,不像select、poll一樣要輪詢所有fd集合。在epoll中,裝置就緒時,呼叫回撥函式,就把就緒fd放入就緒連結串列中,並喚醒在epoll_wait中進入睡眠的程序。雖然都要睡眠和喚醒交替,但select和poll在醒著的時候要遍歷整個fd集合,而epoll在醒著的時候只要判斷一下就緒連結串列是否為空就行了,節省了大量的CPU時間,這就是回撥機制帶來的效能提升。 2)select、poll每次呼叫都要把fd集合從使用者空間往核心空間拷貝一次,並且要把current往裝置等待佇列中掛一次,而epoll只要一次拷貝,而且把current往等待佇列上掛也只是掛一次(在epoll_wait的開始,注意這裡的等待佇列並不是裝置等待佇列,只是一個epoll內部定義的等待佇列)。這也能節省不少的開銷。 參考資料:

相關推薦

UNIX網路程式設計2 理解selectpollepoll原理

三者實現原理對比分析 select, poll, epoll都是IO多路複用的機制,上文中提到的多路複用主要是以select為例,select和poll大同小異,因為select和poll的實現有明顯的缺點,所以在Linux2.5.44中引入了新的處理大批量控制代碼的API

網路程式設計-多路轉接之pollepoll模型

首先,還是需要理解io過程:io過程總體來看分兩步,第一步就是等,第二步才是資料搬遷。而如果要想提高io的效能與效率,就要減少等的比重。 可以假想一個場景: 你去釣魚,但是你只有一個魚竿。你的同伴也和你一起去釣魚,但是他帶了100個魚竿。假設每條魚上

樸素SelectPollEpoll網路程式設計模型實現和分析——樸素模型

        做Linux網路開發,一般繞不開標題中幾種網路程式設計模型。網上已有很多寫的不錯的分析文章,它們的基本論點是差不多的。但是我覺得他們講的還不夠詳細,在一些關鍵論點上缺乏資料支援。所以我決定好好研究這幾個模型。(轉載請指明出於breaksoftware的csdn

樸素SelectPollEpoll網路程式設計模型實現和分析——Select模型

        和樸素模型一樣,我們首先要建立一個監聽socket,然後呼叫listen去監聽伺服器埠。不同的是,我們要對make_socket方法傳遞1,因為我們要建立一個非同步的socket。 listen_sock = make_socket(1); if (

網路通訊 :IO多路複用之selectpollepoll詳解

 目前支援I/O多路複用的系統呼叫有 select,pselect,poll,epoll,I/O多路複用就是通過一種機制,一個程序可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程式進行相應的讀寫操作。但select,pselect,poll,epoll

UNIX環境高階程式設計UNIX網路程式設計(卷一)環境搭建

最近學習這兩本書,在直接編譯書本源程式時,出現標頭檔案“apue.h”(UNIX環境高階程式設計)及“unp.h”(UNIX網路程式設計)錯誤,在這裡坐下配置的筆記。 首先需要安裝Linux系統。(在VirtualBox虛擬機器裡安裝Centos6.7的Linux系統) 一

網路程式設計中阻塞與非阻塞同步與非同步I/O模型的理解

1. 概念理解      在進行網路程式設計時,我們常常見到同步(Sync)/非同步(Async),阻塞(Block)/非阻塞(Unblock)四種呼叫方式:同步:所謂同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。也就是必須一件一件事做,等前一件做完了才能做下一件事。 例如

Unix網路程式設計》卷1:套接字聯網API(第3版):簡介傳輸層套接字程式設計

全書共31章+附錄。 計劃安排:吃透這本書,一天三章+原始碼,並實測程式碼做當天筆記,CSDN見。時間安排:計劃時間1.5個月 == 6個週末 == 12天。 2017.08.05    第01-03章:TCP/IP簡介、傳輸層、套接字程式設計簡介2017.08.06  

UNIX網路程式設計01》 第十一章 高階名字與地址轉換 gethostbyname_rgethostbyaddr_r

gethostbyname_r、gethostbyaddr_r #include<unistd.h> #include<netdb.h> int main() { struct hostent hostbuf,*res = NULL; ch

Linux----網路程式設計(IO複用中selectpollepoll之間的區別)

前面學習了select、poll和epoll三組IO複用系統呼叫,現在從向核心傳遞檔案描述符數、核心實現、檢索就緒描述符方式、工作模式和時間複雜度等五個方面比較其中的區別,以明確在實際應用中應該選擇使用哪個。 由於select與poll的特性相似,所以把它們聯絡在一起與ep

【Socket程式設計】篇六之IO多路複用——selectpollepoll

在上一篇中,我簡單學習了 IO多路複用的基本概念,這裡我將初學其三種實現手段:select,poll,epoll。 I/O 多路複用是為了解決程序或執行緒阻塞到某個 I/O 系統呼叫而出現的技術,使程序或執行緒不阻塞於某個特定的 I/O 系統呼叫。 select()

UNIX網路程式設計》實驗環境搭建unp.h

學Linux下的網路程式設計,stevens的《UNIX網路程式設計 卷一》不可不看。經典中的戰鬥機。 本文記錄實驗環境的搭建過程。 本人linux用的是Centos 6.4 。 1、下載原始碼 得到原始碼包:unpv13e.tar.gz 2、解壓 tar -zxvf

selectpollepoll

time 應用 使用場合 seconds const 方式 文件描述符 div inux I/O復用:   在一個進程或者多個進程的需要多個I/O,不能阻塞在一個I/O上而停止不前,而是用到I/O復用。進程預先告知內核需要哪些I/O描述符,內核一旦發現指定的一個或多個I/O

Linux IO模式及selectpollepoll詳解

注:本文來自博友文章,出處https://segmentfault.com/a/1190000003063859。總結不錯,略作收藏,感謝分享。 同步IO和非同步IO,阻塞IO和非阻塞IO分別是什麼,到底有什麼區別?不同的人在不同的上下文下給出的答案是不同的。所以先限定一下本文的上

一張圖比較selectpollepoll多路複用

名稱 select poll epoll 資料結構 陣列(fd) 陣列(fd)+連結串列(就緒fd) 紅黑樹(fd)+雙向連結串列(就緒fd)

Linux網路程式設計---深刻理解5種基本IO模型

Linux五種IO模型 理解這五種I/O模型之前,我們得先清楚一個IO事件發生,它會經歷哪些步驟: 對於一個網路IO(network IO) (這裡我們以read舉例),它會涉及到兩個系統物件,一個是呼叫這個IO的process (or thread),另一個就是系統核心(kerne

Unix網路程式設計關於time_wait狀態的解釋

面試網路程式設計的同學基本上都會被問到對time_wait狀態的理解,今天記錄一下。 TIME_WAIT狀態解釋:     毫無疑問,TCP中有關網路程式設計最不容易理解的是它的TIME_WAIT狀態,我們可以知道 是執行主動關閉的那端經歷了這個狀態。該端點停留在這個狀

selectpollepoll總結

一、select總結   select本質上是通過設定或者檢查存放fd標誌位的資料結構來進行下一步處理。它僅僅知道了,有I/O事件發生了,卻並不知道是哪那幾個流(可能有一個,多個,甚至全部),我們只能無差別輪詢所有流,找出能讀出資料,或者寫入資料的流,對他們進行操作。所以sel

[轉帖]select提高併發,selectpollepoll的區別(雜)

同步IO和非同步IO,阻塞IO和非阻塞IO分別是什麼,到底有什麼區別?不同的人在不同的上下文下給出的答案是不同的。所以先限定一下本文的上下文。 https://www.2cto.com/kf/201611/561895.html 一 概念說明 在進行解釋之前,首先要說明幾個概念:- 使用者空間和核心空間

一次讀懂 SelectPollEpoll IO複用技術

“ 閱讀本文大概需要 6 分鐘。” 我們之前採用的多程序方式實現的伺服器端,一次建立多個工作子程序來給客戶端提供服務。其實這種方式是存在問題的。 可以打個比方:如果我們先前建立的幾個程序承載不了目前快速發展的業務的話,是不是還得增加程序數?我們都知道系統建立程序是需