Select
Select用来实现多路复用输入/输出模型
1.Select系统调用是用来让我们程序监视多个文件描述符的状态变化
2.程序会停在select这里等待,直到被监视文件描述符有了一个或者多个发生了状态改变;
函数原型:1
2
int select(int nfds, fd_set* readfds,fd_set *writefds,fd_set* exceptfds,struct timeval *timeout);
1.参数ndfs是需要监视的最大的文件描述符值+1。
2.rdset,wrset,exset分别对应需要检测的可读文件描述符的集合,可写文件描述符集合及异常文件描述符集合。
3.参数timeout为结构timeval, 用来设置Select的等待时间。
4.关于fd_set结构:其实这个结果是一个整形数组,更严格的说,它是一个位图,使用位图对应的位来表示需要监视的文件描述符。
系统提供了一组fd_set调用接口,用来操作位图。
1 | void FD_CLR(int fd,fd_set * set);//用来清除描述词组set中的相关fd位 |
函数返回值:
1.执行成功返回文件描述符的改变个数。
2。如果返回0代表timewait时间超时,没有返回。
3.当返回-1,出现错误。
Select的特点:
1.可监控文件描述符个数sizeof(fd_set)的值 ,每个比特位表示监控一个文件描述符。
2.将fd加入select监控集的同时还要使用一个数据结构array保存放到select监控集中的fd.用于返回数据与源数据比较,二是select返回会将没事发生的文件描述符删除,但每次开始select需要重新设置,扫描array取得fd最大值maxfd,用于select的第一个参数。
Select的缺点:
1.每次调用select都需要手动设置fd集合,从接口使用角度来说也非常不便。
2.每次调用select,都需要从用户态拷贝到内核态,这个开销在fd很大时会很大。
3.每次的调用传入的select都需要在内核遍历传进来的所有fd,这个开销在fd很大时会很大。
poll
1 | int poll(struct poll* fds,nfds_t nfds,int timeout); |
参数说明:fds是一个poll函数监听的结构列表,每一个元素中,包含了三部分内容:文件描述符,监听的事件、返回事件的集合。
ndfs表示fds数组的长度。
timeout表示poll函数的超时时间,单位是毫秒(ms)。
返回值:小于0表示出错,等于0表示等待超时,大于0表示poll由于监听的文件描述符就绪而返回。
poll的优点:
1.不同与select使用3个位图来表示三个fd_set的方式,poll使用一个pollfd的指针实现。
2.pollfd结构包含了要监视的event和发生的envent,不再使用select参数-值传递的方式,接口使用比select更方便。
3.poll并没有最大数量限制。
poll的缺点:
当poll中监听的文件描述符数目增多时:
1.和select一样,poll返回后,需要轮询pollfd来获取就绪的描述符。
2.每次调用poll都需要把大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视描述符数量上升,其效率也会线性下降。
epoll
epoll是为了处理大量句柄,而对poll的改进。
epoll的相关调用.1
2
3
4int epoll_create(int size);
//创建一个epoll句柄
int epoll_creat(int size)//size常忽略
int epoll_ctl(int epfd,int op ,int fd,struct epoll_event *event);//epoll的时间注册函数
其中epoll_ctl第一个参数是 epoll_creat的返回值,第二个参数表示动作,用三个宏表示,第三个参数表示要监听的fd,第四个参数是要告诉内核需要监听什么事。
1 | int epoll_wait(int epfd,struct epoll_evnet* events,int maxevents,int timeout ); |
1.参数events是分配好的epoll_event数组。
2.epoll将会把发生的时间赋值到events数组中(events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮我们在用户态中分配内存)。
3.maxevents告诉enents有多大,这个maxevents的值不能大于创建epoll_creat()时的size。
4.参数timeout是超时时间。
5.如果调用函数成功,返回对应I/O上已经准备好的文件描述符数目,如果返回0表示已超时,返回小于0表示函数失败。
因此使用epoll的过程就是三部曲:
1.调用epoll_creat创建一个epoll句柄。
2.调用epoll_ctr,将要监控的文件描述符进行注册
3.调用epoll_wait,等待文件描述符就绪。
epoll的优点:
1.文件描述符数目无上限。
2.基于事件就绪通知方式:一旦被监听的某个文件描述符就绪,内核会采用类似于回掉函数,迅速激活文件描述符。
3.维护就绪队列:当文件描述符就绪,就会被放到把内核中的一个就绪队列,这样调用epoll_wait就绪文件描述符的时候,只取队列元素即可,操作的时间复杂度是O(1)。
4.内存映射机制:内核直接将就绪队列通过mmap的方式映射到用户态,避免了拷贝内存的开销。