一,、select
select的核心是不停的遍歷文件描述符,看看是否就緒,。一般最多同時(shí)支持1024個(gè)文件描述符,。
1.1 select函數(shù)
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
- nfds:最大的文件描述符加1
- readfds:監(jiān)聽(tīng)可讀集合
- writefds:監(jiān)聽(tīng)可寫集合
- exceptfds:監(jiān)聽(tīng)異常集合
- timeout:超時(shí)時(shí)間
1.2 select流程
喚醒 當(dāng)前進(jìn)程的過(guò)程通常是在所監(jiān)測(cè)文件的設(shè)備驅(qū)動(dòng)內(nèi)實(shí)現(xiàn)的,驅(qū)動(dòng)程序維護(hù)了針對(duì)自身資源讀寫的等待隊(duì)列,。當(dāng)設(shè)備驅(qū)動(dòng)發(fā)現(xiàn)自身資源變?yōu)榭勺x寫并且有進(jìn)程睡眠在該資源的等待隊(duì)列上時(shí),,就會(huì)喚醒這個(gè)資源等待隊(duì)列上的進(jìn)程。
1.3 select問(wèn)題
-
每次調(diào)用時(shí)要重復(fù)地從用戶態(tài)讀入?yún)?shù),。
-
每次調(diào)用時(shí)要重復(fù)地掃描文件描述符,,遍歷所有文件描述符。
-
每次在調(diào)用開(kāi)始時(shí),,要把當(dāng)前進(jìn)程放入各個(gè)文件描述符的等待隊(duì)列,。在調(diào)用結(jié)束后,,又把進(jìn)程從各個(gè)等待隊(duì)列中刪除。
-
對(duì)文件描述符的數(shù)量有限制(一般為1024個(gè))
-
用同一個(gè)參數(shù)來(lái)表示入?yún)⒑妥罱K的結(jié)果,,內(nèi)核直接修改入?yún)ⅲㄐ枰O(jiān)控的fd),。
所以下次調(diào)用得重置入?yún)d,因?yàn)楫?dāng)事件發(fā)生后,,入?yún)⒁呀?jīng)被操作系統(tǒng)內(nèi)核給改了,。
再次把全量fd傳送給操作系統(tǒng)。
select隨著文件描述符數(shù)量的上升,,性能會(huì)急劇下降
二、poll
poll的整體流程和select差不多,,也是每次遍歷所有文件描述符,,性能不好,不過(guò)poll解決了select文件描述符數(shù)量的限制問(wèn)題,。
int poll ( struct pollfd * fds, unsigned int nfds, int timeout);
相比select,,poll定義了一個(gè)pollfd用來(lái)指定我們感興趣的文件描述符以及上面發(fā)生的事件。
pollfd結(jié)構(gòu)
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 等待的事件 */
short revents; /* 實(shí)際發(fā)生了的事件 */
} ;
這里還有一個(gè)核心的改變:操作系統(tǒng)沒(méi)有直接修改入?yún)ⅲ╡vents),,而是把實(shí)際ready的fd通過(guò)另外一個(gè)參數(shù)來(lái)返回(revents),,這就減少了每次調(diào)用poll之前再次重置入?yún)ⅲ╡vents)的開(kāi)銷。
但是還是要重復(fù)地從用戶態(tài)讀入?yún)?shù)
三,、epoll
select和poll運(yùn)行效率的兩個(gè)瓶頸已經(jīng)找出,現(xiàn)在的問(wèn)題是怎么改進(jìn),。
- 首先,,如果要監(jiān)聽(tīng)1000個(gè)fd,每次poll都要把1000個(gè)fd 拷入內(nèi)核,,這是極大的浪費(fèi),,內(nèi)核干嘛不自己保存已經(jīng)拷入的fd呢?**epoll就是自己保存拷入的fd **,,它的API就已經(jīng)說(shuō)明了這一點(diǎn),,不是 epoll_wait的時(shí)候才傳入fd,而是通過(guò)epoll_ctl把所有fd傳入內(nèi)核再一起"wait",,這就省掉了不必要的重復(fù)拷貝,。
- 其次,在 epoll_wait時(shí),,也不是把current輪流的加入fd對(duì)應(yīng)的設(shè)備等待隊(duì)列,,而是在設(shè)備等待隊(duì)列醒來(lái)時(shí)調(diào)用一個(gè)回調(diào)函數(shù)(當(dāng)然,這就需要“喚醒回調(diào)”機(jī)制),把產(chǎn)生事件的fd歸入一個(gè)鏈表,,然后返回這個(gè)鏈表上的fd,。
- 另外,epoll機(jī)制實(shí)現(xiàn)了自己特有的文件系統(tǒng)eventpoll filesystem
3.1 epoll 函數(shù)
epoll總共提供了三個(gè)函數(shù)
-
int epoll_create(int size);
epoll_create是為了創(chuàng)建一個(gè)epoll文件描述符,,新創(chuàng)建的epoll文件描述符帶有一個(gè)struct eventpoll結(jié)構(gòu),,eventpoll結(jié)構(gòu)如下。
struct eventpoll {
spinlock_t lock;
struct mutex mtx;
wait_queue_head_t wq; /* Wait queue used by sys_epoll_wait() ,調(diào)用epoll_wait()時(shí), 我們就是"睡"在了這個(gè)等待隊(duì)列上*/
wait_queue_head_t poll_wait; /* Wait queue used by file->poll() , 這個(gè)用于epollfd本事被poll的時(shí)候*/
struct list_head rdllist; /* List of ready file descriptors, 所有已經(jīng)ready的epitem都在這個(gè)鏈表里面*/
struct rb_root rbr; /* RB tree root used to store monitored fd structs, 所有要監(jiān)聽(tīng)的epitem都在這里*/
epitem *ovflist; /*存放的epitem都是我們?cè)趥鬟f數(shù)據(jù)給用戶空間時(shí)監(jiān)聽(tīng)到了事件*/.
struct user_struct *user; /*這里保存了一些用戶變量,比如fd監(jiān)聽(tīng)數(shù)量的最大值等*/
};
這個(gè)結(jié)構(gòu)上再掛一個(gè)紅黑樹(shù),,而這個(gè)紅黑樹(shù)就是每次epoll_ctl時(shí)fd存放的地方,。
-
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
此函數(shù)是添加、刪除,、修改事件
-
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
此函數(shù)是等待條件滿足,,返回值為準(zhǔn)備就緒的事件數(shù)
3.2 epoll 流程
3.3 LT和ET模式
LT(Level Trigger):
應(yīng)用程序地哎呦epoll_wait后如果有事件產(chǎn)生,epoll將事件通知應(yīng)用程序,,但是應(yīng)用程序可以不處理,。應(yīng)用程序下次調(diào)用epoll_wait后,epoll將再次通知應(yīng)用程序,,一直到改事件被處理,。
ET(Edge Trigger):
epoll通知事件后,應(yīng)用程序必須立即處理改事件,,后續(xù)epoll將不再再次向應(yīng)用程序通知這一事件,。
ET很大成都上減少了同一個(gè)epoll事件被多次重復(fù)觸發(fā)的次數(shù),效率比LT高,。
四,、select,、poll,、epoll對(duì)比
【參考】
1.https://www.cnblogs.com/Anker/p/3265058.html
2.https://blog.csdn.net/weixin_42462202/article/details/95315926
3.https://blog.csdn.net/turkeyzhou/article/details/8609360
4.http://blog./uid-20643761-id-1594860.html
5.http://blog./uid-28541347-id-4236779.html
6.http://blog./uid-28541347-id-4238524.html
|