原型:
int select (int maxfd, fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
struct timeval *timeout);
maxfd
:代表要监控的最大文件描述符fd+1
writefds
:监控可写fd
readfds
:监控可读fd
exceptfds
:监控异常fd
timeout
:超时时长select
函数监控3类文件描述符,调用select
函数后会阻塞,直到描述符fd
准备就绪(有数据可读、可写、异常)或者超时,函数便返回。 当select
函数返回后,可通过遍历描述符集合,找到就绪的描述符。
Linux
上一般为1024,可以通过修改宏定义增大上限,但同样存在效率低的弱势;IO
随着监控的描述符数量增长,其性能会线性下降;原型:
int poll (struct pollfd *fds, unsigned int nfds, int timeout);
其中pollfd
表示监视的描述符集合,如下
struct pollfd {
int fd; //文件描述符
short events; //监视的请求事件
short revents; //已发生的事件
};
pollfd
结构包含了要监视的event
和发生的event
,并且pollfd
并没有最大数量限制。 和select
函数一样,当poll
函数返回后,可以通过遍历描述符集合,找到就绪的描述符。
select
和poll
都需要在返回后,通过遍历文件描述符来获取已经就绪的socket
。同时连接的大量客户端在同一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其性能会线性下降。
select
和 poll
的功能基本相同,不过在一些实现细节上有所不同。
select
会修改描述符,而 poll
不会;select
的描述符类型使用数组实现,FD_SETSIZE
大小默认为 1024,因此默认只能监听 1024 个描述符。如果要监听更多描述符的话,需要修改 FD_SETSIZE
之后重新编译;而 poll
的描述符类型使用链表实现,没有描述符数量的限制;poll
提供了更多的事件类型,并且对描述符的重复利用上比 select
高。select
或者 poll
,另一个线程关闭了该描述符,会导致调用结果不确定。select
和 poll
速度都比较慢。
select
和 poll
每次调用都需要将全部描述符从应用进程缓冲区复制到内核缓冲区。select
和 poll
的返回结果中没有声明哪些描述符已经准备好,所以如果返回值大于 0 时,应用进程都需要使用轮询的方式来找到 I/O
完成的描述符。epoll
是在内核2.6中提出的,是select
和poll
的增强版。相对于select
和poll
来说,
epoll
更加灵活,没有描述符数量限制。epoll
使用一个文件描述符管理多个描述符,将用户空间的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy
只需一次。epoll
机制是Linux
最高效的I/O
复用机制,在一处等待多个文件句柄的I/O
事件。
select/poll
都只有一个方法,epoll
操作过程有3个方法,分别是epoll_create()
, epoll_ctl()
,epoll_wait()
。
int epoll_create(int size);
功能:用于创建一个epoll
的句柄,size
是指监听的描述符个数, 现在内核支持动态扩展,该值的意义仅仅是初次分配的fd
个数,后面空间不够时会动态扩容。 当创建完epoll
句柄后,占用一个fd
值.
ls /proc/<pid>/fd/ //可通过终端执行,看到该fd
使用完epoll
后,必须调用close()
关闭,否则可能导致fd
被耗尽。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:用于对需要监听的文件描述符(fd)
执行op
操作,比如将fd
加入到epoll
句柄。
epfd
:是epoll_create()
的返回值;op
:表示op
操作,用三个宏来表示,分别代表添加、删除和修改对fd
的监听事件;
fd
:需要监听的文件描述符;epoll_event
:需要监听的事件,struct epoll_event
结构如下:
struct epoll_event { __uint32_t events; /* Epoll事件 */ epoll_data_t data; /*用户可用数据*/ };
events
可取值:(表示对应的文件描述符的操作)
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
功能:等待事件的上报
epfd
:等待epfd
上的io
事件,最多返回maxevents
个事件;events
:用来从内核得到事件的集合;maxevents:events
数量,该maxevents
值不能大于创建epoll_create()
时的size
;timeout
:超时时间(毫秒,0会立即返回)。该函数返回需要处理的事件数目,如返回0表示已超时。
在 select/poll
中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll
事先通过epoll_ctl()
来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback
的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()
时便得到通知。(此处去掉了遍历文件描述符,而是通过监听回调的的机制。这正是epoll
的魅力所在。
FD
上限是最大可以打开文件的数目,具体数目可以cat /proc/sys/fs/file-max
查看,一般来说这个数目和系统内存关系很大,以3G的手机来说这个值为20-30万。IO
性能不会随着监视fd
的数量增长而下降。epoll
不同于select
和poll
轮询的方式,而是通过每个fd
定义的回调函数来实现的,只有就绪的fd
才会执行回调函数。epoll
的效率并不会比select/poll
高很多。但当遇到大量的空闲连接的场景下,epoll
的效率大大高于select/poll
。select/poll/epoll
都是IO
多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作。本质上select/poll/epoll
都是同步I/O
,即读写是阻塞的。