linux高性能服務器編程筆記 第9章I/O複用

2020.05.15

三種常見I/O複用函數比較

#include<sys/select.h>
int select(int fds, fd_set *readfds, fd_set *writefds, fd_set *exceptionfds, struct timeval *temiout);
//nfds指定被監聽的文件描述符總數,通常被設置爲select監聽的文件描述符的最大值+1;
//readfds,writefds,exceptfds分別指可讀、可寫、異常等事件對應的文件描述符集合,通過這三個參數傳入感興趣的文件描述符,select返回時,內核將修改它們來通知應用程序那些文件描述符已就緒

#include<poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int temeout);
struct pollfd{
	int fd;			//文件描述符
	short events;	//註冊時間
	short revents;	//實際發生的事件,由內核填充
};
//fds指定感興趣的文件描述符上發生的可讀、可寫、異常事件
//nfds指定被監聽事件集合fds的大小

#include<sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
struct epoll_event{
	__uint32_t events;
	epoll_data_t data;
};
typedef union epoll_data{
	void *ptr;
	int fd;
	uint32_t u32;
	uint64_t u64;
}epoll_data_t;
//根據參數op,對epfd所標識的epoll時間表進行事件註冊、修改、刪除
int epoll_wait(int eplfd, struct epoll_event *events, int maxevents, int timeout);
  • 事件集對比
  1. select並沒有將事件與文件描述符綁定,因此select需要三個fd_set類型的參數來分別傳入和輸出可讀、可寫、異常事件,這使得select不能處理更多類型的事件;另一方面由於內核直接對fd_set進行修改,應用程序在下次調用select前不得不重置fd_set;
  2. poll的參數pollfd同時把文件描述符和事件定義在其中,使得任何事件被統一處理;而且內核修改的是revents成員,是的應用程序不需重置事件集合;
  3. epoll不同於select和poll,epoll通過在內核註冊一個事件表,通過獨立函數epoll_ctl來管理事件表,每次epoll_wait調用都將直接從內核事件表中取得用戶註冊的事件,而無需反覆的從用戶空間讀入這些事件。

由於每次select和poll調用都返回整個用戶註冊的事件集合,所以每次應用程序索引就緒文件描述符的時間複雜度都爲O(n),而epoll_wait調用的events參數僅用來返回就緒的事件,使得應用程序索引就緒文件描述符的複雜度達到O(1)。

  • 最大支持文件描述符數
  1. select允許監聽的最大文件描述符有限,雖然用戶可以修改,但是會導致不可預期的結果;
  2. poll和epoll分別使用nfds和maxevents指定最多監聽多少個文件描述符和事件,兩個參數的最大值都是65535,16位。
  • 工作模式
    select和poll只支持相對低效的LT模式,而epoll支持ET模式,而且epoll還支持EPOLLONESHOT事件,可以進一步減少可讀、可寫、異常等時間被觸發的次數;

  • 實現原理

  1. select和poll都採用輪詢的方式,每次調用都要掃描整個註冊文件描述符集合,並將其中就緒的返回給用戶程序,因此其檢測就緒事件的複雜度是O(n);
  2. epoll採用回調的方式,內核檢測到就緒的文件描述符時,將觸發回調函數,回調函數就將該文件描述符上對應的事件插入內核就緒事件隊列。內核最後在適當的時機將就緒隊列中的內容拷貝到用戶空間,而epoll_wait無須輪詢整個文件描述符集合來檢測那些事件已經就緒,其時間複雜度是O(1)。

但是當活動鏈接較多時,epoll_wait的效率不一定比select和poll高,因爲此時回調函數被觸發得過於頻繁,所以epoll_wait適用於;連接數量多,但活動鏈接數量少的情況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章