select poll epoll使用示例
select,poll,epoll都是IO多路複用的機制。I/O多路複用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進行相應的讀寫操作。
這三個方法都要調用驅動中的struct file_operations中的.poll方法。poll是一個函數指針,申明如下
unsigned int (*poll) (struct file *, struct poll_table_struct *);
在驅動中poll的實現方法如下:
.....
static unsigned int Test_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = 0;
.........
poll_wait(filp, &(int_queue), wait);
if(read ready)
{
mask |=POLLIN | POLLRDNORM;
}
if(write ready)
{
mask |=return POLLOUT | POLLWRNORM;
}
............
return mask ;
}
static const struct file_operations Test_fops = {
.owner = THIS_MODULE,
.......
.poll = Test_poll ,
.......
};
注意:.poll本身是不會阻塞的,不論讀寫條件是否滿足,都會立即返回。阻塞是在調用 .poll的時候實現的,poll返回0的時候,將阻塞,等待int_queue。
int_queue是一個 類型爲wait_queue_head_t結構的等待隊列。
在驅動初始化的時候調用init_waitqueue_head(&(int_queue));初始化等待隊列
當讀寫條件滿足的時候,調用wake_up_interruptible(&( int_queue));喚醒等待隊列
poll方法返回的數據如下:
常量 說明
POLLIN 普通或優先級數據可讀
POLLRDNORM 普通數據可讀
POLLRDBAND 優先級數據可讀
POLLPRI 高優先級數據可讀
POLLOUT 普通數據可寫
POLLWRNORM 普通數據可寫
POLLWRBAND 優先級數據可寫
POLLERR 發生錯誤
POLLHUP 發生掛起
POLLNVAL 描述字不是一個打開的文件
1 select的實現示例
select相關函數原型如下:
int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout);
maxfd是需要監視的最大的文件描述符值+1,例如如果我們分別要監視3個文件描述符fd1 fd2 fd3 ,數值分別爲100,500,1000,那麼maxfd就必須寫1001。
rdset,wrset,exset分別對應於需要檢測的可讀文件描述符的集合,可寫文件描述符的集 合及異常文件描述符的集合。
timeout 爲設置的超時時間,如果爲NULL,則一直等待,直到監視的資源可用。
函數返回值:小於0 函數執行出錯 ,超時將返回0 。大於0 表示有資源可用的數目,例如我們分別監視了fd1 fd2 fd3 那麼如果fd1 發的同時可以寫了則返回2。
對fd_set的操作可以通過下面的宏來操作:
FD_ZERO(fd_set*); 用來清空fd_set集合,即讓fd_set集合不再包含任何文件句柄。
FD_SET(int ,fd_set *); 用來將一個給定的文件描述符加入fd_set集合之中
FD_CLR(int ,fd_set*); 用來將一個給定的文件描述符從集合中刪除
FD_ISSET(int ,fd_set*);檢測fd在fdset集合中是否存在,存在返回真,否則,返回假(也可以認爲集合中指定的文件描述符是否可以讀寫)。
這裏需要注意的是select執行的時候,rdset、wrset、exset、timeout、會被改變,所以每次執行select前一定要重新設置下。例如rdset分別監視fd1 fd2 fd3,當fd3可以讀都,select返回,那麼這個時候rdset就只設置了fd3,如果我們沒有重新設置,那fd1 fd2就監視不到了。同樣timeout也是一樣,當超時返回後,timeout變爲0,如果這個時候沒有重新設置,那麼select函數就會立即返回了,無論是否資源可用。
下面是示例代碼:
void * Test_select( void* arg )
{
fd_set fdsi;
fd_set fdso;
int maxfdp ;
int ret;
struct timeval timeout;
int fd ,fd2;
fd = open(DEV_FILENAME, O_RDWR );
if(fd<0)
{
printf("open device error\r\n");
return NULL ;
}
fd2 = open(DEV_FILENAME2, O_RDWR );
if(fd<0)
{
printf("open device error\r\n");
return NULL ;
}
maxfdp = fd>fd2?fd+1:fd2+1 ;
while(exitFlag==0)
{
timeout.tv_sec=1;//1秒超時
timeout.tv_usec = 0;
FD_ZERO(&fdsi);
FD_SET(fd,&fdsi);
FD_SET(fd2,&fdsi);
FD_ZERO(&fdso);
FD_SET(fd,&fdso);
FD_SET(fd2,&fdso);
ret = select(maxfdp,&fdsi,&fdso,NULL,&timeout);
//如果不需要超時可以改爲 ret = select(maxfdp,&fdsi,&fdso,NULL,NULL);
if(ret==0)
{
printf("select timeout\r\n");
continue;
}
else if(ret<0)
{
printf("select error\r\n");
exitFlag= 1;
}
else
{
if(FD_ISSET(fd,&fdsi))
{
//read data;
}
else if(FD_ISSET(fd,&fdso))
{
//write data;
}
if(FD_ISSET(fd2,&fdsi))
{
//read data;
}
else if(FD_ISSET(fd2,&fdso))
{
//write data;
}
}
}
close(fd );
close(fd2 );
return NULL;
}
說明:在系統內核中select是採用輪詢來處理的,如果需要監視的文件描述符越多,就需要消耗更多的資源。所以如果要監視很多文件描述符,最好使用epoll(epoll在後面會介紹)。
select監視的最大數量有限制,通常是1024。
2 poll的實現示例
poll實現比select簡單,只有一個函數就可以了,函數原型如下
int poll (struct pollfd *fds, nfds_t nfds, int timeout);
參數fds :pollfd 結構體的指針,這個結構描述了需要監視的文件描述符和我們關心的事件。這個函數也可以監視多個文件描述符。
struct pollfd結構描述如下
struct pollfd
{
int fd; /* poll 的文件描述符. */
short int events; /* fd 上感興趣的事件(需要等待的事件). */
short int revents; /* fd 上實際發生的事件. 也就是poll返回時 的狀態*/
};
參數 nfds:fds的個數,也就是要監視的文件描述符的個數。
參數 timeout : 超時的毫秒數,如果爲-1則表示永遠等待。
函數返回值 返回0表示超時 返回值小於0函數出錯,大於0 實際可用的資源數
下面是示例代碼:
void * Test_poll( void* arg )
{
struct pollfd Events[2] ;
int ret;
int fd ,fd2;
fd = open(DEV_FILENAME, O_RDWR );
if(fd<0)
{
printf("open device error\r\n");
return NULL ;
}
fd2 = open(DEV_FILENAME2, O_RDWR );
if(fd<0)
{
printf("open device error\r\n");
return NULL ;
}
Events[0].fd = fd;
Events[0].events = POLLIN | POLLERR; /*關心讀取和出錯事件*/
Events[1].fd = fd2;
Events[1].events = POLLIN | POLLERR; /*關心讀取和出錯事件*/
while(exitFlag==0)
{
ret = poll ((struct pollfd *)&Events[0], 2, -1);//如果要設置超時可以把-1改爲要設置的超時的毫秒數
if(ret==0)
{
printf("poll timeout\r\n");
continue;
}
else if(ret<0)
{
printf("poll error\r\n");
exitFlag= 1;
}
else
{
if (Events[0].revents & POLLERR)
{
printf ("device error!\n");
}
if (Events[1].revents & POLLERR)
{
printf ("device error!\n");
}
if (Events[0].revents & POLLIN) {
//read data;
}
if (Events[1].revents & POLLIN) {
//read data;
}
}
}
close(fd );
close(fd2 );
return NULL;
}
說明:poll函數的實現方法和select類似,所以等待的文件描述符越多,越消耗資源。
3 epoll實現說明
epoll實現很簡單,就三個函數就解決了,下面是函數申明:
int epoll_create(int size);
這個函數的功能創建一個epoll的句柄,當創建好epoll句柄後,它就是會佔用一個fd值所以使用完epoll後一定要close掉,避免佔用資源。
參數size:用來告訴內核這個監聽的數目一共有多少。 自從Linux 2.6.8開始,size參數被忽略,但是依然要大於0。
返回值:成功返回epoll的句柄,失敗返回-1。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
參數 epfd: epoll的句柄,是epoll_create()的返回值
參數 op:操作碼。用三個宏來表示的
EPOLL_CTL_ADD:註冊新的fd到epfd中;
EPOLL_CTL_MOD:修改已經註冊的fd的監聽事件;
EPOLL_CTL_DEL:從epfd中刪除一個fd;
參數 fd:要監視的文件描述符。
參數event :要監視的事件的描述。
struct epoll_event的結構說明如下:
struct epoll_event {
__uint32_t events; /* 需要監視的事件,如EPOLLIN、EPOLLOUT等等 */
epoll_data_t data; /* 用戶數據*/
};
typedef union epoll_data
{
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
返回值:0成功,小於0 失敗
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
該函數等待事件產生。
參數epfd: epoll的句柄
參數events:從內核得到的事件集合
參數maxevents:events的大小。events的大小取決於可能同時產生的事件的數量。例如我們雖然同時監視了fd1 fd2 fd3 三個文件描述符,但是這3個之間是不可能同時可讀或者可寫的,那麼將maxevents設置爲1也沒有問題,但是如果三個事件可能會同時產生,那麼最好就設置爲3。如果設置爲1,3個事件同時產生,epoll_wait第一次返回的時候就會只返回第一事件了。(不過在此調用這個函數的時候,會馬上返回。例如當fd1 fd2 fd3同時可讀,但是maxevents爲1時,那麼第一次調用epoll_wait返回值爲1,報告fd1的時間,第二次調用,馬上返回1,調用fd2的事件,第三次調用馬上返回1,報告fd3的事件.而如果maxevents爲3的話,當fd1 fd2 fd3同時可讀,epoll_wait返回值爲3,同時報告fd1 fd2 fd3的事件)。
參數timeout:超時的毫秒數,如果爲-1表示一直等待直到事件產生。
返回值:超時返回0 ,返回小於0 函數執行失敗,大於0,返回值爲實際的事件數目。
下面是示例代碼:
static int epoll_register( int epoll_fd, int fd )
{
struct epoll_event ev;
int ret, flags;
/*****************************************/
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
//資料上說需要設置爲非阻塞的方式,我試了下,似乎不設置爲非阻塞的方式也不會出錯
/**********************************************/
ev.events = EPOLLIN;
ev.data.fd = fd;
ret = epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &ev );
return ret;
}
void * Test_epoll( void* arg )
{
int epoll_fd ,ret ;
int count =0;
struct epoll_event events[2];
int ne, nevents;
int fd ,fd2;
fd = open(DEV_FILENAME, O_RDWR );
if(fd<0)
{
printf("open device error\r\n");
return NULL ;
}
fd2 = open(DEV_FILENAME2, O_RDWR );
if(fd<0)
{
printf("open device error\r\n");
return NULL ;
}
epoll_fd = epoll_create(1);
epoll_register( epoll_fd, fd );
epoll_register( epoll_fd, fd2 );
while(exitFlag==0)
{
nevents = epoll_wait( epoll_fd, events, 2, -1 );
if (nevents < 0)
{
exitFlag =1;
printf("epoll_wait() unexpected error: %s", strerror(errno));
}
for (ne = 0; ne < nevents; ne++)
{
if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0)
{
}
if ((events[ne].events & EPOLLIN) != 0)
{
if( fd == events[ne].data.fd)
{
//read data;
}
else if( fd2 == events[ne].data.fd)
{
//read data;
}
}
}
}
epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL );
epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd2, NULL );
ret=close(epoll_fd);
return NULL;
}
最後總結:
(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內部定義的等待隊列)。這也能節省不少的開銷。
所以最好儘量使用epoll。他的速度更快,系統開銷更小。
這三個方法都要調用驅動中的struct file_operations中的.poll方法。poll是一個函數指針,申明如下
unsigned int (*poll) (struct file *, struct poll_table_struct *);
在驅動中poll的實現方法如下:
.....
static unsigned int Test_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = 0;
.........
poll_wait(filp, &(int_queue), wait);
if(read ready)
{
mask |=POLLIN | POLLRDNORM;
}
if(write ready)
{
mask |=return POLLOUT | POLLWRNORM;
}
............
return mask ;
}
static const struct file_operations Test_fops = {
.owner = THIS_MODULE,
.......
.poll = Test_poll ,
.......
};
注意:.poll本身是不會阻塞的,不論讀寫條件是否滿足,都會立即返回。阻塞是在調用 .poll的時候實現的,poll返回0的時候,將阻塞,等待int_queue。
int_queue是一個 類型爲wait_queue_head_t結構的等待隊列。
在驅動初始化的時候調用init_waitqueue_head(&(int_queue));初始化等待隊列
當讀寫條件滿足的時候,調用wake_up_interruptible(&( int_queue));喚醒等待隊列
poll方法返回的數據如下:
常量 說明
POLLIN 普通或優先級數據可讀
POLLRDNORM 普通數據可讀
POLLRDBAND 優先級數據可讀
POLLPRI 高優先級數據可讀
POLLOUT 普通數據可寫
POLLWRNORM 普通數據可寫
POLLWRBAND 優先級數據可寫
POLLERR 發生錯誤
POLLHUP 發生掛起
POLLNVAL 描述字不是一個打開的文件
1 select的實現示例
select相關函數原型如下:
int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout);
maxfd是需要監視的最大的文件描述符值+1,例如如果我們分別要監視3個文件描述符fd1 fd2 fd3 ,數值分別爲100,500,1000,那麼maxfd就必須寫1001。
rdset,wrset,exset分別對應於需要檢測的可讀文件描述符的集合,可寫文件描述符的集 合及異常文件描述符的集合。
timeout 爲設置的超時時間,如果爲NULL,則一直等待,直到監視的資源可用。
函數返回值:小於0 函數執行出錯 ,超時將返回0 。大於0 表示有資源可用的數目,例如我們分別監視了fd1 fd2 fd3 那麼如果fd1 發的同時可以寫了則返回2。
對fd_set的操作可以通過下面的宏來操作:
FD_ZERO(fd_set*); 用來清空fd_set集合,即讓fd_set集合不再包含任何文件句柄。
FD_SET(int ,fd_set *); 用來將一個給定的文件描述符加入fd_set集合之中
FD_CLR(int ,fd_set*); 用來將一個給定的文件描述符從集合中刪除
FD_ISSET(int ,fd_set*);檢測fd在fdset集合中是否存在,存在返回真,否則,返回假(也可以認爲集合中指定的文件描述符是否可以讀寫)。
這裏需要注意的是select執行的時候,rdset、wrset、exset、timeout、會被改變,所以每次執行select前一定要重新設置下。例如rdset分別監視fd1 fd2 fd3,當fd3可以讀都,select返回,那麼這個時候rdset就只設置了fd3,如果我們沒有重新設置,那fd1 fd2就監視不到了。同樣timeout也是一樣,當超時返回後,timeout變爲0,如果這個時候沒有重新設置,那麼select函數就會立即返回了,無論是否資源可用。
下面是示例代碼:
void * Test_select( void* arg )
{
fd_set fdsi;
fd_set fdso;
int maxfdp ;
int ret;
struct timeval timeout;
int fd ,fd2;
fd = open(DEV_FILENAME, O_RDWR );
if(fd<0)
{
printf("open device error\r\n");
return NULL ;
}
fd2 = open(DEV_FILENAME2, O_RDWR );
if(fd<0)
{
printf("open device error\r\n");
return NULL ;
}
maxfdp = fd>fd2?fd+1:fd2+1 ;
while(exitFlag==0)
{
timeout.tv_sec=1;//1秒超時
timeout.tv_usec = 0;
FD_ZERO(&fdsi);
FD_SET(fd,&fdsi);
FD_SET(fd2,&fdsi);
FD_ZERO(&fdso);
FD_SET(fd,&fdso);
FD_SET(fd2,&fdso);
ret = select(maxfdp,&fdsi,&fdso,NULL,&timeout);
//如果不需要超時可以改爲 ret = select(maxfdp,&fdsi,&fdso,NULL,NULL);
if(ret==0)
{
printf("select timeout\r\n");
continue;
}
else if(ret<0)
{
printf("select error\r\n");
exitFlag= 1;
}
else
{
if(FD_ISSET(fd,&fdsi))
{
//read data;
}
else if(FD_ISSET(fd,&fdso))
{
//write data;
}
if(FD_ISSET(fd2,&fdsi))
{
//read data;
}
else if(FD_ISSET(fd2,&fdso))
{
//write data;
}
}
}
close(fd );
close(fd2 );
return NULL;
}
說明:在系統內核中select是採用輪詢來處理的,如果需要監視的文件描述符越多,就需要消耗更多的資源。所以如果要監視很多文件描述符,最好使用epoll(epoll在後面會介紹)。
select監視的最大數量有限制,通常是1024。
2 poll的實現示例
poll實現比select簡單,只有一個函數就可以了,函數原型如下
int poll (struct pollfd *fds, nfds_t nfds, int timeout);
參數fds :pollfd 結構體的指針,這個結構描述了需要監視的文件描述符和我們關心的事件。這個函數也可以監視多個文件描述符。
struct pollfd結構描述如下
struct pollfd
{
int fd; /* poll 的文件描述符. */
short int events; /* fd 上感興趣的事件(需要等待的事件). */
short int revents; /* fd 上實際發生的事件. 也就是poll返回時 的狀態*/
};
參數 nfds:fds的個數,也就是要監視的文件描述符的個數。
參數 timeout : 超時的毫秒數,如果爲-1則表示永遠等待。
函數返回值 返回0表示超時 返回值小於0函數出錯,大於0 實際可用的資源數
下面是示例代碼:
void * Test_poll( void* arg )
{
struct pollfd Events[2] ;
int ret;
int fd ,fd2;
fd = open(DEV_FILENAME, O_RDWR );
if(fd<0)
{
printf("open device error\r\n");
return NULL ;
}
fd2 = open(DEV_FILENAME2, O_RDWR );
if(fd<0)
{
printf("open device error\r\n");
return NULL ;
}
Events[0].fd = fd;
Events[0].events = POLLIN | POLLERR; /*關心讀取和出錯事件*/
Events[1].fd = fd2;
Events[1].events = POLLIN | POLLERR; /*關心讀取和出錯事件*/
while(exitFlag==0)
{
ret = poll ((struct pollfd *)&Events[0], 2, -1);//如果要設置超時可以把-1改爲要設置的超時的毫秒數
if(ret==0)
{
printf("poll timeout\r\n");
continue;
}
else if(ret<0)
{
printf("poll error\r\n");
exitFlag= 1;
}
else
{
if (Events[0].revents & POLLERR)
{
printf ("device error!\n");
}
if (Events[1].revents & POLLERR)
{
printf ("device error!\n");
}
if (Events[0].revents & POLLIN) {
//read data;
}
if (Events[1].revents & POLLIN) {
//read data;
}
}
}
close(fd );
close(fd2 );
return NULL;
}
說明:poll函數的實現方法和select類似,所以等待的文件描述符越多,越消耗資源。
3 epoll實現說明
epoll實現很簡單,就三個函數就解決了,下面是函數申明:
int epoll_create(int size);
這個函數的功能創建一個epoll的句柄,當創建好epoll句柄後,它就是會佔用一個fd值所以使用完epoll後一定要close掉,避免佔用資源。
參數size:用來告訴內核這個監聽的數目一共有多少。 自從Linux 2.6.8開始,size參數被忽略,但是依然要大於0。
返回值:成功返回epoll的句柄,失敗返回-1。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
參數 epfd: epoll的句柄,是epoll_create()的返回值
參數 op:操作碼。用三個宏來表示的
EPOLL_CTL_ADD:註冊新的fd到epfd中;
EPOLL_CTL_MOD:修改已經註冊的fd的監聽事件;
EPOLL_CTL_DEL:從epfd中刪除一個fd;
參數 fd:要監視的文件描述符。
參數event :要監視的事件的描述。
struct epoll_event的結構說明如下:
struct epoll_event {
__uint32_t events; /* 需要監視的事件,如EPOLLIN、EPOLLOUT等等 */
epoll_data_t data; /* 用戶數據*/
};
typedef union epoll_data
{
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
返回值:0成功,小於0 失敗
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
該函數等待事件產生。
參數epfd: epoll的句柄
參數events:從內核得到的事件集合
參數maxevents:events的大小。events的大小取決於可能同時產生的事件的數量。例如我們雖然同時監視了fd1 fd2 fd3 三個文件描述符,但是這3個之間是不可能同時可讀或者可寫的,那麼將maxevents設置爲1也沒有問題,但是如果三個事件可能會同時產生,那麼最好就設置爲3。如果設置爲1,3個事件同時產生,epoll_wait第一次返回的時候就會只返回第一事件了。(不過在此調用這個函數的時候,會馬上返回。例如當fd1 fd2 fd3同時可讀,但是maxevents爲1時,那麼第一次調用epoll_wait返回值爲1,報告fd1的時間,第二次調用,馬上返回1,調用fd2的事件,第三次調用馬上返回1,報告fd3的事件.而如果maxevents爲3的話,當fd1 fd2 fd3同時可讀,epoll_wait返回值爲3,同時報告fd1 fd2 fd3的事件)。
參數timeout:超時的毫秒數,如果爲-1表示一直等待直到事件產生。
返回值:超時返回0 ,返回小於0 函數執行失敗,大於0,返回值爲實際的事件數目。
下面是示例代碼:
static int epoll_register( int epoll_fd, int fd )
{
struct epoll_event ev;
int ret, flags;
/*****************************************/
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
//資料上說需要設置爲非阻塞的方式,我試了下,似乎不設置爲非阻塞的方式也不會出錯
/**********************************************/
ev.events = EPOLLIN;
ev.data.fd = fd;
ret = epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &ev );
return ret;
}
void * Test_epoll( void* arg )
{
int epoll_fd ,ret ;
int count =0;
struct epoll_event events[2];
int ne, nevents;
int fd ,fd2;
fd = open(DEV_FILENAME, O_RDWR );
if(fd<0)
{
printf("open device error\r\n");
return NULL ;
}
fd2 = open(DEV_FILENAME2, O_RDWR );
if(fd<0)
{
printf("open device error\r\n");
return NULL ;
}
epoll_fd = epoll_create(1);
epoll_register( epoll_fd, fd );
epoll_register( epoll_fd, fd2 );
while(exitFlag==0)
{
nevents = epoll_wait( epoll_fd, events, 2, -1 );
if (nevents < 0)
{
exitFlag =1;
printf("epoll_wait() unexpected error: %s", strerror(errno));
}
for (ne = 0; ne < nevents; ne++)
{
if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0)
{
}
if ((events[ne].events & EPOLLIN) != 0)
{
if( fd == events[ne].data.fd)
{
//read data;
}
else if( fd2 == events[ne].data.fd)
{
//read data;
}
}
}
}
epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL );
epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd2, NULL );
ret=close(epoll_fd);
return NULL;
}
最後總結:
(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內部定義的等待隊列)。這也能節省不少的開銷。
所以最好儘量使用epoll。他的速度更快,系統開銷更小。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.