select、poll、epoll使用小結

Linux上可以使用不同的I/O模型,我們可以通過下圖瞭解常用的I/O模型:同步和異步模型,以及阻塞和非阻塞模型,本文主要分析其中的異步阻塞模型。


一、select使用

這個模型中配置的是非阻塞I/O,然後使用阻塞select系統調用來確定一個I/O描述符何時有操作。使用select調用可以爲多個描述符提供通知,對於每個提示符,我們可以請求描述符的可寫,可讀以及是否發生錯誤。異步阻塞I/O的系統流程如下圖所示:


使用select常用的幾個函數如下:

FD_ZERO(int fd, fd_set* fds) 
FD_SET(int fd, fd_set* fds) 
FD_ISSET(int fd, fd_set* fds) 
FD_CLR(int fd, fd_set* fds) 
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) 
fd_set類型可以簡單的理解爲按bit位標記句柄的隊列。具體的置位、驗證可以使用FD_SET,FD_ISSET等宏實現。在select函數中,readfds、writefds和exceptfds同時作爲輸入參數和輸出參數,如果readfds標記了一個位置,則,select將檢測到該標記位可讀。timeout爲設置的超時時間。

下面我們來看如何使用select:

	SOCKADDR_IN addrSrv;
	int reuse = 1;
	SOCKET sockSrv,connsock;
	SOCKADDR_IN addrClient;
	pool pool;
	int len=sizeof(SOCKADDR);
	/*創建TCP*/
	sockSrv=socket(AF_INET,SOCK_STREAM,0);
	/*地址、端口的綁定*/
	
	addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=htons(port);
	
	if(bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR))<0)
	{
		fprintf(stderr,"Failed to bind");
		return ;
	}
	
	if(listen(sockSrv,5)<0)
	{
		fprintf(stderr,"Failed to listen socket");
		return ;
	}
	setsockopt(sockSrv,SOL_SOCKET,SO_REUSEADDR,(const char*)&reuse,sizeof(reuse));
	init_pool(sockSrv,&pool);
	while(1)
	{
		/*通過selete設置爲異步模式*/
		pool.ready_set=pool.read_set;
		pool.nready=select(pool.maxfd+1,&pool.ready_set,NULL,NULL,NULL);
		if(FD_ISSET(sockSrv,&pool.ready_set))
		{
			connsock=accept(sockSrv,(SOCKADDR *)&addrClient,&len);
			//loadDeal()/*連接處理*/
			//printf("test\n");
			add_client(connsock,&pool);//添加到連接池
		}
		/*檢查是否有事件發生*/
		check_client(&pool);
	}
上面是一個服務器代碼的關鍵部分,設置爲異步的模式,然後接受到連接將其添加到連接池中。監聽描述符上使用select,接受客戶端的連接請求,在check_client函數中,遍歷連接池中的描述符,檢查是否有事件發生。





二、poll使用

poll函數類似於select,但是其調用形式不同。poll不是爲每個條件構造一個描述符集,而是構造一個pollfd結構體數組,每個數組元素指定一個描述符標號及其所關心的條件。定義如下:

#include <sys/poll.h>
int poll (struct pollfd *fds, unsigned int nfds, int timeout);
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events to watch */
short revents; /* returned events witnessed */
};

每個結構體的events域是由用戶來設置,告訴內核我們關注的是什麼,而revents域是返回時內核設置的,以說明對該描述符發生了什麼事件。這點與select不同,select修改其參數以指示哪一個描述符準備好了。在《unix環境高級編程》中有一張events取值的表,如下:

POLLIN :可讀除高優級外的數據,不阻塞

POLLRDNORM:可讀普通數據,不阻塞

POLLRDBAND:可讀O優先數據,不阻塞

POLLPRI:可讀高優先數據,不阻塞

POLLOUT :可寫普數據,不阻塞

POLLWRNORM:與POLLOUT相同

POLLWRBAND:寫非0優先數據,不阻塞

其次revents還有下面取值

POLLERR :已出錯

POLLHUP:已掛起,當以描述符被掛起後,就不能再寫向該描述符,但是仍可以從該描述符讀取到數據。

POLLNVAL:此描述符並不引用一打開文件

對poll函數,nfds表示fds中的元素數,timeout爲超時設置,單位爲毫秒若爲0,表示不等待,爲-1表示描述符中一個已經準備好或捕捉到一個信號返回,大於0表示描述符準備好,或超時返回。函數返回值返回值若爲0,表示沒有事件發生,-1表示錯誤,並設置errno,大於0表示有幾個描述符有事件。

poll的使用和select基本類似。在此不再介紹。poll相對於是select的優勢是監聽的描述符數量沒有限制。

三、epoll學習

epoll有兩種模式,Edge Triggered(簡稱ET) 和 Level Triggered(簡稱LT).在採用這兩種模式時要注意的是,如果採用ET模式,那麼僅當狀態發生變化時纔會通知,而採用LT模式類似於原來的select/poll操作,只要還有沒有處理的事件就會一直通知.

1)epoll數據結構介紹:

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 */
};
常見的事件如下:

EPOLLIN:表示對描述符的可以讀

EPOLLOUT:表示對描述符的可以寫

EPOLLPRI:表示對描述符的有緊急數據可以讀

EPOLLERR:發生錯誤

EPOLLHUP:掛起

EPOLLET:邊緣觸發

EPOLLONESHOT:一次性使用,當監聽完這次事件之後,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到EPOLL隊列裏

2)函數介紹

epoll的三個函數

int epoll_creae(int size);
功能:該函數生成一個epoll專用的文件描述符

參數:size爲epoll上能關注的最大描述符數

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:用於控制某個epoll文件描述符時間,可以註冊、修改、刪除

參數:epfd由epoll_create生成的epoll專用描述符

    op操作:EPOLL_CTL_ADD 註冊   EPOLL_CTL_MOD修改  EPOLL_DEL刪除

            fd:關聯的文件描述符

    evnet告訴內核要監聽什麼事件

int epoll_wait(int epfd,struct epoll_event*events,int maxevents,int timeout);
功能:該函數等待i/o事件的發生。

參數:epfd要檢測的句柄

    events:用於回傳待處理時間的數組

    maxevents:告訴內核這個events有多大,不能超過之前的size

    timeout:爲超時時間

使用方法參考:https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c


epoll支持的FD上限是最大可以打開文件的數目(select面臨這樣的問題),IO效率不隨FD數目增加而線性下降(select、poll面臨的問題)使用mmap加速內核與用戶空間的消息傳遞。現在libevent封裝了幾種的實現,可以通過使用libevent來實現多路複用。


 本文參考:https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/

   http://www.ibm.com/developerworks/cn/linux/l-cn-edntwk/index.html?ca=drs-

             http://www.ibm.com/developerworks/cn/linux/l-async/









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