select poll epoll使用示例
2016-03-17 19:59
357 查看
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。他的速度更快,系统开销更小。
相关文章推荐
- 第一次见python
- Java集合框架:EnumMap
- iOS开发系列--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook系统服务开发汇总
- 今日面试不会的题目
- leetcode:Move Zeroes 【Java】
- Java集合框架:EnumMap
- uboot与Linux内核机器码
- android项目bug及解决方法小记录
- {error,{bad_return,{{xxapp,start,[normal,[]]},ok}}}
- New Concepts (Mathmatic one)
- opencv中图像异或函数bitwise_xor
- 前三次复利计算程序的总结
- 可以正确显示表格线的Grid item view
- HDU 4704 Sum 费马小定理
- Problem F
- Problem F
- 获得getLocalIP
- CF629D Babaei and Birthday Cake
- 关于C++指针类型所占大小的测试
- Bootstrap 模态弹窗