(3)同时每次调用select都需要在内核进行线性遍历,时间复杂度为O(N),这个开销在fd很多时也很大
poll的缺点:
它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制(也就是没有select的缺点1,却有它的缺点2和3)。
poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。
epoll的优点主要是以下几个方面:
1. 监视的描述符数量不受限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左 右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。 (我2G内存的机器上是239545)。select的最大缺点就是进程打开的fd是有数量限制的。
2. IO的效率不会随着监视fd的数量的增长而下降。epoll不同于select和poll轮询的方式,而是通过每个fd定义的回调函数来实现的。只有就绪的fd才会执行回调函数,时间复杂度为O(1)。
3. 支持电平触发和边沿触发两种方式(具体区别,看下面的2.2epoll详解-工作模式),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。
4. mmap加速内核与用户空间的信息传递。epoll是通过内核于用户空间mmap同一块内存,避免了无谓的内存拷贝。
通过表格来看一下更清楚。
|
select |
poll |
epoll |
支持最大连接数 |
1024(2048) |
无上限 |
无上限 |
IO效率 |
每次调用进行线性遍历,时间复杂度为O(N) |
每次调用进行线性遍历,时间复杂度为O(N) |
使用“事件”通知方式,每当fd就绪,系统注册的回调函数就会被调用,将就绪fd放到rdllist里面,这样epoll_wait返回的时候我们就拿到了就绪的fd。时间发复杂度O(1) |
fd拷贝 |
每次select都拷贝 |
每次poll都拷贝 |
调用epoll_ctl时拷贝进内核并由内核保存,之后每次epoll_wait不拷贝 |
#include <sys/epoll.h> int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
第二个参数op表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中,struct epoll_event { __uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; typedef union epoll_data { void *ptr; int fd; __uint32_t u32; __uint64_t u64; } epoll_data_t;
三.epoll的例子:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/epoll.h> #include <fcntl.h> #define MAX_EPOLL 1000 #define BUF_SIZE 1024 #define PORT 6000 int setnonblocking( int fd ) { if( fcntl( fd, F_SETFL, fcntl( fd, F_GETFD, 0 )|O_NONBLOCK ) == -1 ) { printf("Set blocking error : %d\n", errno); return -1; } return 0; } int main( int argc, char ** argv ) { int listen_fd; int conn_fd; int epoll_fd; int nread; int i; struct sockaddr_in servaddr; struct sockaddr_in cliaddr; struct epoll_event ev; //监听fd struct epoll_event events[MAX_EPOLL]; char buf[BUF_SIZE]; socklen_t len = sizeof( struct sockaddr_in ); bzero( &servaddr, sizeof( servaddr ) ); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl( INADDR_ANY ); servaddr.sin_port = htons( PORT ); if( ( listen_fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { printf("socket error...\n" , errno ); exit( EXIT_FAILURE ); } if( setnonblocking( listen_fd ) == -1 ) { printf("setnonblocking error : %d\n", errno); exit( EXIT_FAILURE ); } if( bind( listen_fd, ( struct sockaddr *)&servaddr, sizeof( struct sockaddr ) ) == -1 ) { printf("bind error : %d\n", errno); exit( EXIT_FAILURE ); } if( listen( listen_fd, 10 ) == -1 ) { printf("Listen Error : %d\n", errno); exit( EXIT_FAILURE ); } epoll_fd = epoll_create( MAX_EPOLL ); //1.epoll_create ev.events = EPOLLIN | EPOLLET; ev.data.fd = listen_fd; if( epoll_ctl( epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev ) < 0 ) //2.epoll_ctl { printf("Epoll Error : %d\n", errno); exit( EXIT_FAILURE ); } while( 1 ) { int ready_counts = 0; if( ( ready_counts = epoll_wait( epoll_fd, events, MAX_EPOLL, -1 ) ) == -1 ) //3.epoll_wait,就绪的event在events里面 { printf( "Epoll Wait Error : %d\n", errno ); exit( EXIT_FAILURE ); } for( i = 0; i < ready_counts; i++ ) { if( events[i].data.fd == listen_fd) //监听端口有就绪事件 { if( ( conn_fd = accept( listen_fd, (struct sockaddr *)&cliaddr, &len ) ) == -1 ) { printf("Accept Error : %d\n", errno); exit( EXIT_FAILURE ); } printf( "Server get from client !\n"/*, inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port */); ev.events = EPOLLIN | EPOLLET; ev.data.fd = conn_fd; if( epoll_ctl( epoll_fd, EPOLL_CTL_ADD, conn_fd, &ev ) < 0 ) { printf("Epoll Error : %d\n", errno); exit( EXIT_FAILURE ); } } else { nread = read( events[i].data.fd, buf, sizeof( buf ) ); if( nread <= 0 ) { close( events[i].data.fd ); epoll_ctl( epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, &ev ); continue; } write( events[i].data.fd, buf, nread ); } } } close( listen_fd ); return 0; }
既然epoll有这么多优点,是不是可以取代select和poll。
什么情况下用select/poll而不用epoll呢?
原文地址:http://blog.csdn.net/majianfei1023/article/details/45772269