色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

您好,歡迎來電子發燒友網! ,新用戶?[免費注冊]

您的位置:電子發燒友網>電子百科>網絡>復用器>

IO多路復用的幾種實現機制的分析

2018年03月07日 11:40 網絡整理 作者: 用戶評論(0

服務器端編程經常需要構造高性能的IO模型,常見的IO模型有四種:

(1)同步阻塞IO(Blocking IO):即傳統的IO模型。

(2)同步非阻塞IO(Non-blocking IO):默認創建的socket都是阻塞的,非阻塞IO要求socket被設置為NONBLOCK。注意這里所說的NIO并非Java的NIO(New IO)庫。

(3)IO多路復用(IO Multiplexing):即經典的Reactor設計模式,有時也稱為異步阻塞IO,Java中的Selector和Linux中的epoll都是這種模型。

(4)異步IO(Asynchronous IO):即經典的Proactor設計模式,也稱為異步非阻塞IO。

同步和異步的概念描述的是用戶線程與內核的交互方式:同步是指用戶線程發起IO請求后需要等待或者輪詢內核IO操作完成后才能繼續執行;而異步是指用戶線程發起IO請求后仍繼續執行,當內核IO操作完成后會通知用戶線程,或者調用用戶線程注冊的回調函數。

阻塞和非阻塞的概念描述的是用戶線程調用內核IO操作的方式:阻塞是指IO操作需要徹底完成后才返回到用戶空間;而非阻塞是指IO操作被調用后立即返回給用戶一個狀態值,無需等到IO操作徹底完成。

select和poll的實現比較相似,目前也有很多為人詬病的缺點,epoll可以說是select和poll的增強版。

多路復用

一、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方法返回時會返回一個描述讀寫操作是否就緒的mask掩碼,根據這個mask掩碼給fd_set賦值。

7、如果遍歷完所有的fd,還沒有返回一個可讀寫的mask掩碼,則會調用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

二、poll實現

poll的實現和select非常相似,只是描述fd集合的方式不同,poll使用pollfd結構而不是select的fd_set結構。其他的都差不多。

三、epoll實現

epoll既然是對select和poll的改進,就應該能避免上述的三個缺點。那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(利用schedule_timeout()實現睡一會,判斷一會的效果,和select實現中的第7步是類似的)。

說明一下這個回調機制的原理,其實很簡單,看一下select和epoll在把current加入fd對應的設備等待隊列時使用的代碼:

select:

static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,

poll_table *p)

{

struct poll_table_entry *entry = poll_get_entry(p);

if (!entry)

return;

get_file(filp);

entry-》filp = filp;

entry-》wait_address = wait_address;

init_waitqueue_entry(&entry-》wait, current);

add_wait_queue(wait_address, &entry-》wait);

}

其中init_waitqueue_entry實現如下:

static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)

{

q-》flags = 0;

q-》private = p;

q-》func = default_wake_function;

}

上面的代碼是說建立一個poll_table_entry結構entry,首先把current設置為entry-》wait的private成員,同時把default_wake_function設為entry-》wait的func成員,然后把entry-》wait鏈入到wait_address中(這個wait_address就是設備的等待隊列,在tcp_poll中就是sk_sleep)。

再看一下epoll:

/*

* This is the callback that is used to add our wait queue to the

* target file wakeup lists.

*/

static void ep_ptable_queue_proc(struct file *file, wait_queue_head_t *whead,

poll_table *pt)

{

struct epitem *epi = ep_item_from_epqueue(pt);

struct eppoll_entry *pwq;

if (epi-》nwait 》= 0 && (pwq = kmem_cache_alloc(pwq_cache, GFP_KERNEL))) {

init_waitqueue_func_entry(&pwq-》wait, ep_poll_callback);

pwq-》whead = whead;

pwq-》base = epi;

add_wait_queue(whead, &pwq-》wait);

list_add_tail(&pwq-》llink, &epi-》pwqlist);

epi-》nwait++;

} else {

/* We have to signal that an error occurred */

epi-》nwait = -1;

}

}

其中init_waitqueue_func_entry的實現如下:

static inline void init_waitqueue_func_entry(wait_queue_t *q,

wait_queue_func_t func)

{

q-》flags = 0;

q-》private = NULL;

q-》func = func;

}

可以看到,總體和select的實現是類似的,只不過它是創建了一個eppoll_entry結構pwq,只不過pwq-》wait的func成員被設置成了回調函數ep_poll_callback(而不是default_wake_function,所以這里并不會有喚醒操作,而只是執行回調函數),private成員被設置成了NULL。最后吧pwq-》wait鏈入到whead中(也就是設備等待隊列中)。這樣,當設備等待隊列中的進程被喚醒時,就會調用ep_poll_callback了。

再梳理一下,當epoll_wait時,它會判斷就緒鏈表中有沒有就緒的fd,如果沒有,則把current進程加入一個等待隊列(file-》private_data-》wq)中,并在一個while(1)循環中判斷就緒隊列是否為空,并結合schedule_timeout實現睡一會,判斷一會的效果。如果current進程在睡眠中,設備就緒了,就會調用回調函數。在回調函數中,會把就緒的fd放入就緒鏈表,并喚醒等待隊列(file-》private_data-》wq)中的current進程,這樣epoll_wait又能繼續執行下去了。

對于第三個缺點,epoll沒有這個限制,它所支持的FD上限是最大可以打開文件的數目,這個數字一般遠大于2048,舉個例子,在1GB內存的機器上大約是10萬左右,具體數目可以cat /proc/sys/fs/file-max察看,一般來說這個數目和系統內存關系很大。

多路復用

總結:

1、select,poll實現需要自己不斷輪詢所有fd集合,直到設備就緒,期間可能要睡眠和喚醒多次交替。而epoll其實也需要調用epoll_wait不斷輪詢就緒鏈表,期間也可能多次睡眠和喚醒交替,但是它是設備就緒時,調用回調函數,把就緒fd放入就緒鏈表中,并喚醒在epoll_wait中進入睡眠的進程。雖然都要睡眠和交替,但是select和poll在“醒著”的時候要遍歷整個fd集合,而epoll在“醒著”的時候只要判斷一下就緒鏈表是否為空就行了,這節省了大量的CPU時間。這就是回調機制帶來的性能提升。

2、select,poll每次調用都要把fd集合從用戶態往內核態拷貝一次,并且要把current往設備等待隊列中掛一次,而epoll只要一次拷貝,而且把current往等待隊列上掛也只掛一次(在epoll_wait的開始,注意這里的等待隊列并不是設備等待隊列,只是一個epoll內部定義的等待隊列)。這也能節省不少的開銷。

非常好我支持^.^

(1) 100%

不好我反對

(0) 0%

( 發表人:龔婷 )

      發表評論

      用戶評論
      評價:好評中評差評

      發表評論,獲取積分! 請遵守相關規定!

      ?
      主站蜘蛛池模板: 一本二卡三卡四卡乱码麻豆 | CHRISTMAS农村夫妻HO | 国产区精品综合在线 | 国产精品久久久久久AV免费不卡 | 果冻传媒APP免费网站在线观看 | 国产91无毒不卡在线观看 | 性生交片免费无码看人 | 欧美精品一区二区在线电影 | 国产精品一区二区AV97 | 午夜一级免费视频 | 亚洲午夜久久影院 | 天美麻豆成人AV精品 | 丰满少妇被猛烈进出69影院 | 韩国无遮羞禁动漫在线观看96 | 内射无码AV-区二区在线观看 | 久久99AV无色码人妻蜜 | 国产精品免费一区二区区 | 粉嫩极品国产在线观看 | 琪琪SEE色原网色原网站18 | 中文字幕福利视频在线一区 | 欧美最猛性XXX孕妇 欧美最猛性xxxxx亚洲精品 | 奶头从情趣内衣下露了出来AV | 李丽莎与土豪50分钟在线观看 | 国产传媒麻豆剧精品AV | 三级在线观看网站 | 日本不卡一二三 | 亚洲精品久久久一区 | 婷婷综合亚洲爱久久 | 肉肉的各种姿势高H细文 | 美女网站免费久久久久久久 | 吃寂寞寡妇的奶 | 欧美亚洲日韩国产在线在线 | 九九热伊人 | 高h 大尺度纯肉 np快穿 | 国产亚洲精品高清视频免费 | 色综合久久天天影视网 | 久久婷婷五月免费综合色啪 | 青年医生插曲 | 美女伊人网 | 穿白丝袜边走边尿白丝袜 | 亚洲中文字幕日产乱码2020 |