I/O复用
来源:互联网 发布:linux搭建测试环境步骤 编辑:程序博客网 时间:2024/05/22 12:52
I/O多路复用技术通过把多个I/O的阻塞复用到同一个select的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求。与传统的多线程/多进程模型比,I/O多路复用的最大优势是系统开销小,系统不需要创建新的额外进程或者线程,也不需要维护这些进程和线程的运行,降底了系统的维护工作量,节省了系统资源。
网络程序在以下情况下需要使用I/O技术:
(1)客户端程序要同时处理多个socket
(2)客户端程序要同时处理用户输入和网络连接
(3)TCP服务器既要处理监听socket和连接socket
(4)服务器要同时处理TCP请求和UDP请求
(5)服务器要同时监听多个端口,或者处理多种服务
I/O复用的三个系统调用:select、poll、epoll
- select
系统调用原型:#include<sys/select.h>int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval* timeout);//nfds为被监听文件描述符的总数//三个fd_set* 的参数分别指向可读、可写、异常事件的文件描述符集合//timeout设置select的等待时间返回值:成功时返回就绪文件描述符的总数
既然有三个参数都是fd_set的指针,就先来看看fd_set的真面目!
简化版#include<typesizes.h>#define _FD_SETSIZE 1024typedef long int _fd_mask; //4#undef _NFDBITS#define _NFDBITS (8*(int)sizeof(_fd_mask)) //32typedef struct{ _fd_mask fds_bits[_FD_SETSIZE/_NFDBITS]; 1024/32=32}fd_set;
由上可见,fd_set结构体仅包含一个整形数组,该数组的每一个元素的每一位标记一个文件描述符。fd_set能容纳的文件描述符有FD_SETSIZE决定,而FD_SETSIZE的大小与系统版本有关。
select的底层通过sys_select实现,它将用户的数据(fd_set)拷贝到内核态,select中的每一个描述符都对应一个位,通过给每个事件分配一个bitmap,来对用户的读写,异常事件进行监听,然后由do_select去完成链表的建立,回调函数的设置,最后将就绪事件返回给了sys_select,由sys_select把发生就绪事件的文件描述符返回给用户。
select的参数类型fd_set没有将文件描述符与事件绑定,它仅仅是一个文件描述符集合。因此select需要提供3个种类型的参数来分别传入和输出可读、可写、异常事件,所以select能处理的事件类型有限。另外由于内核对于fd_set是在线修改,应用程序下次调用select前需要重置fs_set集合,select采用的是轮询,每次调用都要扫描整个注册的文件描述符集合,并将其中就绪的文件描述返回给用户程序,时间复杂度为O(n).
- poll
#include<poll.h>int poll(struct pollfd fds[],nfds_t nfds,int timeout);//fds为pollfd结构的数组,nfds指定被监听事件集合fds的大小,timeout指定poll的超时值
struct pollfd{ int fd; //指定成员描述符 short events; //告诉poll监听fd上的哪些事件,是一系列事件的按位或 short revents; //由内核修改,通知应用程序fd上发生了哪些事件,如果当某个文件描述符有状态变化时,revents的值就不为空。}
poll的参数类型pollfd,将文件描述符与和事件都绑定在一起,内核每次只会修改结构体中revents的值,而event的值保持不变,因此下次调用poll时应用程序无须重置pollfd类型的事件集参数,支持的最大文件描述符数为65535个,和select一样,也是用的轮询,时间复杂度为O(n)。
poll的使用
int ret = poll(fds,MAX_EVENT_NUMBER,-1);for(int i = 0 ;i < MAX_EVENT_NUMBER;i++){ if(fds[i].revent & POLLIN) { int socket = fds[i].fd; }}
- epoll
epoll使用一组函数来完成任务,而不是单个函数,epoll把用户关心的文件描述符上的事件都存放在内核的一个事件表中,而不是像select和poll那样,每次调用都重复传入文件描述符或事件集。但epoll需要一个额外的文件描述符来唯一标识这个事件表。epoll_create函数可以创建这个内核事件表的文件描述符。
#include<sys/epoll.h>int epoll_create(int size);
在这个函数中,size只起到了一个参数检查的作用,没有实际分配大小的作用,该函数返回的该事件表的文件描述符。
int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event)//fd是要操作的文件描述符,op指定操作类型,event指定事件op的类型有3种:EPOLL_CTL_ADD 往事件表中注册fd上的事件EPOLL_CTL_MOD 修改fd上的注册事件EPOLL_CTL_DEL 删除fd上的注册事件/*struct epoll_event{ _uint32_t events; //epoll事件 epoll_data_t data; //用户事件,一个联合体(用来存放内核拷贝的就绪事件)}typedef union epoll_data{ void* ptr; int fd; uint32_t u32; uint64_t u64;}epoll_data_t;*/成功时返回0,失败返回-1
int epoll_wait(int epfd,struct epoll_event* event,int maxevents,int timeout)/*maxevent指定最多监听多少个事件,必须大于0 event用来存放内核拷贝的就绪事件,提高epoll的效率*/
int ret = epoll(epollfd,events,MAX_EVENT_NUMBER,-1);for(int i = 0;i<ret;i++){ int sockfd = event[i].data.fd;}
不得不说到epoll的两种模式ET和LT,LT是默认的工作模式,当往内核表中注册一个文件描述符上的EPOLLET事件时,epoll将以ET模式工作
对于采用LT模式下的文件描述符,当epoll_wait检测到有事件发生时,会将事件通知应用程序,应用程序可以不立即处理该事件。当应用程序下一次在调用epoll_wait时,会再一次通知应用程序,直到事件被处理为止。but在ET模式下, 当检测到事件发生时通知应用程序,应用程序必须立即处理,因为后续的调用epoll_wait将不会在通知此事件。所以ET模式在很大程度上,减少了同一个事件被重复触发的次数,这就是LT模式比ET模式高效的原因。
epoll在内核中维护一张事件表,提供了一个独立的系统调用epoll_ctl来控制往其中添加、修改、删除事件,这样,每次epoll_wait调用都是从内核时间表中取得用户注册的事件,而无须每次都从用户空间读入这些事件。epoll_wait系统调用中的events参数用来返回就绪事件,这使得应用程序索引就绪文件的时间复杂度为O(1)。
epoll能监听的最大文件描述符数目能达到65535个。
select、poll都是采用轮询的方式,每次调用都要扫描整个文件描述符集合,并将其中就绪的文件描述符返回给用户程序,但是epoll采用的是回调,内核检测到有就绪的文件描述符时,将触发回调函数,回调函数会将文件描述符上对应的事件插入内核就绪事件队列上,内核在适当的时机会把就绪事件队列上的内容拷贝到用户空间。
- I/O模型以及I/O复用
- epoll I/O复用
- I/O复用
- I/O 复用
- Linux I/O复用
- I/O复用-epoll
- I/O复用
- I/O复用
- I/O复用
- I/O复用poll
- I/O复用epoll
- I/O复用--《APUE1》
- I/O复用
- I/O复用
- I/O复用
- I/O复用
- I/O复用------select
- I/O复用------poll
- Gradle version 和 Android Plugin Version
- HBase协处理器加载过程(1.2)
- 很多不错的选业创业公共服务云平台
- BZOJ2301 HAOI2011 Problem b
- java实现图片与base64字符串之间的转换
- I/O复用
- 什么是网站性能优化技术?怎样提升移动端的用户体验?
- C程序设计基础——计算字符串中的字符数
- python : 修改boundingbox , 修改jpg
- Linux 回溯之前输入过的命令
- 黑客成长技术清单
- Javascript设计模式-16-中介者模式
- controller-service-dao-entity
- bzoj2654: tree