linux网络编程--select/poll/epoll 详解
目錄
參考鏈接
epoll函數(shù)
close
epoll event
EL/LT
ET Edge Trigger 邊沿觸發(fā)工作模式
LT Level Trigger 水平觸發(fā)工作模式
epoll 源碼解析
一道騰訊后臺開發(fā)面試題
ET/LT 比較
epoll 優(yōu)點
epoll 源碼解讀
select()
select()簡介
為什么需要select()?
select()函數(shù)
select 缺點
select() 文件描述符上限
poll()
fdarray
nfds
timeout
poll() 文件描述符上限
poll()/select()的區(qū)別
參考鏈接
epoll函數(shù)
注意: epoll不屬于任何namespace。
#include <sys/epoll.h>int epoll_create(int size); // return epollfd, 失敗return -1/* op: EPOLL_CTL_ADD EPOLL_CTL_MOD EPOLL_CTL_DEL 如果是delete的話, epoll_ctl的最后一個參數(shù)event可以是NULL */ int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); // 成功return0, 失敗return -1 int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout); // 成功return nready. 失敗return -1//epoll_event /* 其實這個epoll_data只是給用戶自行使用的,epoll不關(guān)心里面的內(nèi)容。 這個dta回隨著epoll_data 返回的epoll_event一并返回 */ typedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64; } epoll_data_t;struct epoll_event {uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */ };close
其實在外面關(guān)閉一個fd之后,就可以不用再在epoll list里面刪除了,但是為了安全起見,還是用EPOLL_CTL_DEL刪掉吧。詳情可以看 epoll(7) man page FAQ。
epoll event
EL/LT
有關(guān)ET/LT 阻塞/非阻塞的操作,網(wǎng)絡(luò)上基本都是錯的,只要你安排的好,既可以用阻塞,也可以用非阻塞。(linux man page上也讓你用阻塞)
ET Edge Trigger 邊沿觸發(fā)工作模式
LT Level Trigger 水平觸發(fā)工作模式
epoll 源碼解析
https://blog.csdn.net/wangyin159/article/details/48895287
epoll_wait
- 缺省的工作模式
一道騰訊后臺開發(fā)面試題
Q:使用Linux?epoll模型,水平(LT)觸發(fā)模式,當socket可寫時,會不停的觸發(fā)socket可寫的事件,如何處理?
- 需要向socket寫數(shù)據(jù)的時候才把socket加入epoll,等待可寫事件。接受到可寫事件后,調(diào)用write或者send發(fā)送數(shù)據(jù)。當所有數(shù)據(jù)都寫完后,把socket移出epoll(用EPOLLONESHOT也行)。
- 這種方式的缺點是,即使發(fā)送很少的數(shù)據(jù),也要把socket加入epoll,寫完后在移出epoll,有一定操作代價。
- 開始不把socket加入epoll,需要向socket寫數(shù)據(jù)的時候,直接調(diào)用write或者send發(fā)送數(shù)據(jù)。如果返回EAGAIN(緩沖區(qū)滿了,后面還需要繼續(xù)發(fā)),把socket加入epoll,在epoll的驅(qū)動下寫數(shù)據(jù),全部數(shù)據(jù)發(fā)送完畢后,再移出epoll。
- 這種方式的優(yōu)點是:數(shù)據(jù)不多的時候可以避免epoll的事件處理,提高效率。
ET/LT 比較
epoll 優(yōu)點
對應(yīng)select()的缺點, epoll都有解決的方法
- 使用epoll_ctl()函數(shù),只有在注冊、修改、刪除的時候才會對內(nèi)核進行操作。
- epoll的解決方案不像select或poll一樣每次都把current輪流加入fd對應(yīng)的設(shè)備等待隊列中,而只在epoll_ctl時把current掛一遍(這一遍必不可少)并為每個fd指定一個回調(diào)函數(shù),當設(shè)備就緒,喚醒等待隊列上的等待者時,就會調(diào)用這個回調(diào)函數(shù),而這個回調(diào)函數(shù)會把就緒的fd加入一個就緒鏈表)。epoll_wait的工作實際上就是在這個就緒鏈表中查看有沒有就緒的fd(利用schedule_timeout()實現(xiàn)睡一會,判斷一會的效果,和select實現(xiàn)中的第7步是類似的)
- epoll沒有這個限制,它所支持的FD上限是最大可以打開文件的數(shù)目,這個數(shù)字一般遠大于2048,舉個例子,我的1GB內(nèi)存阿里云ECS是999999,具體數(shù)目可以cat /proc/sys/fs/file-max察看,一般來說這個數(shù)目和系統(tǒng)內(nèi)存關(guān)系很大。
poll每次返回整個文件描述符數(shù)組, 用戶需要遍歷數(shù)組已找到哪些文件描述符上有IO事件。 而epoll_wait(2)返回的是活動fd的列表,需要遍歷的數(shù)組通常會小很多,在并發(fā)連接數(shù)較大而活動連接比例不高時,epoll(4)比epoll(2)更高效。
epoll 源碼解讀
當某一進程調(diào)用epoll_create方法時,Linux內(nèi)核會創(chuàng)建一個eventpoll結(jié)構(gòu)體,這個結(jié)構(gòu)體中有兩個成員與epoll的使用方式密切相關(guān)。eventpoll結(jié)構(gòu)體如下所示:
struct eventpoll{..../*紅黑樹的根節(jié)點,這顆樹中存儲著所有添加到epoll中的需要監(jiān)控的事件*/struct rb_root rbr;/*雙鏈表中則存放著將要通過epoll_wait返回給用戶的滿足條件的事件*/struct list_head rdlist;....};每一個epoll對象都有一個獨立的eventpoll結(jié)構(gòu)體,用于存放通過epoll_ctl方法向epoll對象中添加進來的事件。這些事件都會掛載在紅黑樹中,如此,重復(fù)添加的事件就可以通過紅黑樹而高效的識別出來(紅黑樹的插入時間效率是lgn,其中n為樹的高度)。
而所有添加到epoll中的事件都會與設(shè)備(網(wǎng)卡)驅(qū)動程序建立回調(diào)關(guān)系,也就是說,當相應(yīng)的事件發(fā)生時會調(diào)用這個回調(diào)方法。這個回調(diào)方法在內(nèi)核中叫ep_poll_callback,它會將發(fā)生的事件添加到rdlist雙鏈表中。
struct epitem{struct rb_node rbn;//紅黑樹節(jié)點struct list_head rdllink;//雙向鏈表節(jié)點struct epoll_filefd ffd; //事件句柄信息struct eventpoll *ep; //指向其所屬的eventpoll對象struct epoll_event event; //期待發(fā)生的事件類型}當調(diào)用epoll_wait檢查是否有事件發(fā)生時,只需要檢查eventpoll對象中的rdlist雙鏈表中是否有epitem元素即可。如果rdlist不為空,則把發(fā)生的事件復(fù)制到用戶態(tài),同時將事件數(shù)量返回給用戶。
select()
select()簡介
為什么需要select()?
select()函數(shù)
#include <sys/select.h> #include <sys/time.h>int select(int maxfdp1,fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);返回: 若有描述符就緒,則返回就緒描述符的數(shù)量,若超時則為0, 若出錯則為1
1. timeout-->timeval
struct timeval {long tv_sec; // secondslog tv_usec; // microseconds }- 用于指定timeout的秒數(shù)和微秒數(shù)
- 如果輸入為0,那么select函數(shù)會一直等下去一直到某個描述符準備好
- 如果輸入這個參數(shù),那么最長等待時間就確定了
- 如果輸入這個結(jié)構(gòu),但是其中的兩個值為0,那么就不等待-->輪詢機制
延伸 gettimeofday()
- 用gettimeofday() 可以獲得微秒(us)級別的時間。
- 會把目前的時間tv所指的結(jié)構(gòu)返回,當?shù)貢r區(qū)的信息則放到tz所指的結(jié)構(gòu)中。
- 1970年1月1日到現(xiàn)在的時間
- 調(diào)用兩次gettimeofday(), 前后做減法,從而達到計算時間的目的。
2. readset, writeset, exceptset
#include <sys/select.h>struct fd_set myset; //四個相關(guān)的宏函數(shù)void FD_ZERO(fd_set *fdset); // clean all bits at fdset void FD_SET(int fd, fd_set *fdset); // turn on the bit for fd in fdset void FD_CLR(int fd, fd_set *fdset); // turn on the bit for fd in fdset void FD_ISSET(int fd, fd_set *fdset); //is the bit for fd on in fdset? 如果set了,返回13. maxfdp1
例子
select\strcliselect01.c
void str_cli(FILE *fp, int sockfd) {int maxfdp1;fd_set rset;char sendline[MAXLINE], recvline[MAXLINE];FD_ZERO(&rset);for ( ; ; ) {FD_SET(fileno(fp), &rset);FD_SET(sockfd, &rset);maxfdp1 = max(fileno(fp), sockfd) + 1;Select(maxfdp1, &rset, NULL, NULL, NULL);if (FD_ISSET(sockfd, &rset)) { /* socket is readable */if (Readline(sockfd, recvline, MAXLINE) == 0)err_quit("str_cli: server terminated prematurely");Fputs(recvline, stdout);}if (FD_ISSET(fileno(fp), &rset)) { /* input is readable */if (Fgets(sendline, MAXLINE, fp) == NULL)return; /* all done */Writen(sockfd, sendline, strlen(sendline));}} }select 缺點
select() 文件描述符上限
這個問題的關(guān)鍵其實要先理解select關(guān)于文件描述符上限的原因
soft limit可以修改,但是不能超過hard limit
ulimit -Hn //結(jié)果65535poll()
#include <poll.h> #include <limits.h> /* for OPEN_MAX */ // 描述了poll的最大數(shù)量int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);返回: 若有描述符就緒,則返回就緒描述符的數(shù)量,若超時則為0, 若出錯則為1
fdarray
指向一個數(shù)組結(jié)構(gòu)第一個元素的指針,沒一個元素都是一個pollfd結(jié)構(gòu),使用這個結(jié)構(gòu),避免了select中使用一個參數(shù)既表示我們關(guān)心的值,又表示結(jié)果。
struct pollfd {int fd; // 描述符short events; // 我們關(guān)心的狀態(tài)short revents; // 返回的結(jié)果 }nfds
第一個參數(shù)中的數(shù)組元素的個數(shù)
timeout
| INFTIM | 永遠等待 |
| 0 | 立即返回,不阻塞進程 |
| >?0 | 等待指定的毫秒數(shù) |
poll() 文件描述符上限
poll雖然不像select一樣受到select() 中FD_SETSIZE 的限制,但是仍然受到ulimit中設(shè)定的一個進程所能打開的最大文件描述符的限制
ulimit -n //結(jié)果65535poll()/select()的區(qū)別
本篇文章由一文多發(fā)平臺ArtiPub自動發(fā)布
總結(jié)
以上是生活随笔為你收集整理的linux网络编程--select/poll/epoll 详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cmake使用方法(详细)
- 下一篇: 程序员如何用糖果实现盈利 - [别人家的