I/O复用技术
来源:互联网 发布:steam mac 游戏推荐 编辑:程序博客网 时间:2024/05/29 14:09
Unix下5种IO模型:
阻塞式IO;
非阻塞式IO;
IO复用;
信号驱动IO;
异步IO;
阻塞IO会使调用进程在IO数据未准备好时阻塞。
非阻塞IO在数据未准备好时不是阻塞调用进程,而是返回一个错误。
IO复用使调用进程阻塞在IO复用函数上,而不是阻塞在真正IO系统调用上,其优势在于可以等待多个描述符就绪。
信号驱动IO首先要开启套接字的信号驱动IO功能,并通过sigaction系统调用安装一个信号处理函数,它不会阻塞进程,当数据准备好的时候,内核就为该进程产生一个SIGIO信号,然后就可以在信号处理函数中去处理IO操作,或者通知主循环处理IO操作。
异步IO是由内核通知我们IO操作何时完成,而信号驱动IO是内核通知我们何时可以启动一个IO操作。
下面介绍IO复用的函数
select函数
#include <sys/select.h>#include <sys/time.h>int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);返回:若有就绪描述符则为其数目,若超时则为0,若出错则为-1
struct timeval{ long tv_sec; //秒 long tv_usec; //微秒};
timeout参数:
1、NULL —— 永远等待下去
2、指定秒数和微秒数 —— 等待一段固定时间
3、定时器的值为0 —— 根本不等待
readset, writeset, exceptset指定要让内核测试读、写和异常条件的描述符。
四个宏:
void FD_ZERO(fd_Set *fdset); //清除fdset中所有bits
void FD_SET(int fd, fd_set *fdset); //在fdset中为fd turn on位
void FD_CLR(int fd, fd_set *fdset); //在fdset中为fd turn off位
int FD_ISST(int fd, fd_set *fdset); //测试fdset中的fd描述符
注意:描述符集内任何与未就绪描述符对应的位返回时均清成0,因此每次重新调用select函数时,我们都得再次把所有描述符集内所关心的位均置为1。
maxfdp1是待测试的最大描述符加1。
poll函数
#include <poll.h>int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);返回:如有就绪描述符则为其数目,若超时则为0,若出错则为-1。
struct pollfd{ int fd; //待检查的描述符 short events; //在fd上感兴趣的事件 short revents; //在fd上发生的事件}
事件标志:
POLLIN 普通或者优先级带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRI 高优先级带数据可读
POLLOUT 普通或优先级带数据可写
POLLWRNORM 普通数据可写
POLLWRBAND 优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述符不是一个打开的文件
nfds指定结构数组个数
timeout参数指定poll函数返回前等待多长时间,其值是一个应等待毫秒数的正直,如果值为INFTIM就永远等待,如果为0就立即返回。
epoll
epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外(LT),还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率(即高速模式ET)。传统的select/poll另一个致命弱点就是当你拥有一个很大的socket集合,不过由于网络延时,任一时间只有部分的socket是“活跃”的,但是select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。但是epoll不存在这个问题,它只会对“活跃”的socket进行操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。那么,只有“活跃”的socket才会主动的去调用 callback函数,其他idle状态socket则不会。另外,epoll使用mmap加速内核与用户空间的消息传递,无论是select, poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核与用户空间mmap同一块内存实现的。epoll有两种工作方式:LT 和 ET。LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表。ET (edge-triggered)是高速工作方式,只支持non-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。
epoll的接口
epoll操作过程需要三个接口,分别如下:
#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);
(1) int epoll_create(int size);
创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
(2)int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
(3) int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。
- I/O复用技术
- Epoll多路I/O复用技术
- linux系统I/O复用技术之一:select()
- LINUX系统I/O复用技术之二:poll()
- linux系统I/O复用技术之三:epoll()
- C++I/O技术
- I/O操作技术
- 重叠I/O技术
- I/O技术
- I/O流技术
- I/O多路复用技术
- I/O多路复用技术
- Unix网络编程(六)高级I/O技术之复用技术 select
- Unix网络编程(六)高级I/O技术之复用技术 select
- I/O复用和I/O模型
- I/O复用简述
- I/O复用机制
- I/O复用模型
- HttpGet与HttpPost区别
- 评估假设
- 第14周实践 矩阵乘法
- javaWeb核心技术一
- setTimeout()和setInterval()的区别和转换
- I/O复用技术
- FPGA管脚分配文件的保存
- [jQuery知识]jQuery之知识五-DOM节点操作
- mysql_索引
- Yii学习碎片
- document.all 在各浏览器中的支持不同--JavaScript
- Android布局控件
- docker 上面安装redis 主从复制
- chrome浏览器的跨域设置