高级IO

5种IO模型

第一步等待 第二步拷贝

阻塞式IO:一直等待 有数据及时处理 阻塞式等待 自己调自己等
非阻塞式IO:反复调用是否有数据 轮询 如果没有直接返回 有数据进行处理 自己调 自己等 非阻塞等待
信号驱动式IO:recvfrom非程序员主动调用 利用信号捕捉方式自定制捕捉 捕捉成功递交SIGIO信号 recvfrom进行处理读数据期间,进入一个阻塞式状态 一般不用
IO多路复用:一个线程等待所有文件描述符 select、poll、epoll多路转接函数只负责等,io事件就绪的通知机制
异步IO:由内核拷贝数据完成时,通知应用程序什么时候拷贝 发起io条件 事件结束获得io结果 别人等别人搬迁,调用aio_read
前4种统称为同步io,等待方式不同,

今天我们主要谈一谈IO多路复用,I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的

select

系统提供select函数用来实现多路复用输入/输出模型,select系统调用是用来监视多个文件描述符的状态变化的,程序会停在select这里的等待,直到被监视的文件描述符有一个或多个状态发生了变化,只负责等,数据搬迁由recvfrom、io函数负责
这里写图片描述

nfds:是需要监视的最大的文件描述符值+1;
rdset,wrset,exset:分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集合及异常文件描述符的集合;
timeout:结构timeval,用来设置select()的等待时间
    取值:NULL:则表.示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了事件;
          0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。
          特定的时间值:如果在指定的时间段.里没有事件发生,select将超时返回。

其实fd_set这个结构就是一个整数数组, 更严格的说, 是一个 “位图”. 使用位图中对应的位来表示要监视的文件描述符.
提供了一组操作fd_set的接口, 来比较方便的操作位图.

void FD_CLR(int fd, fd_set *set); // 用来清除描述词组set中相关fd 的位
int FD_ISSET(int fd, fd_set *set); // 用来测试描述词组set中相关fd 的位是否为真
void FD_SET(int fd, fd_set *set); // 用来设置描述词组set中相关fd的位
void FD_ZERO(fd_set *set); // 用来清除描述词组set的全部位
例如:假设fd_set长度为1字节,1字节的fd_set最大可对应8个fd
fd_set set;
FD_ZERO(&set);  //set用位表示为00000000
fd=2;
FD_SET(fd,&set); //set用位表示为00000010
fd=3;
FD_SET(fd,&set); //set用位表示为00000110
select(5,&set,0,0,0);//如果fd=2发生可读事件,select返回,此时的fd=3被清空,set变为00000010

函数返回值:
1)执行成功则返回文件描述词状态已改变的个数
2)如果返回0代表在描述词状态改变前已超过timeout时间,没有返回
3)当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds, exceptfds和timeout的值变成不可预测。
特点:
1、select的句柄数目受限,在linux/posix_types.h头文件有这样的声明:#define __FD_SETSIZE 1024 表示select最多同时监听1024个fd
2、将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,用于再select 返回后,array作为源数据和fdset进行FDISSET判断。另外select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前都要重新从array取得fd逐一加.入(FD_ZERO最先)

poll

在select的基础上改变了两个点,其一poll没有最大文件描述符数量的限制,其二输入型描述参数和输出型描述参数分开了。
poll的机制与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll没有最大文件描述符数量的限制。poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。
这里写图片描述
这里写图片描述

 每一个pollfd结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示poll()监视多个文件描述符。每个结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域。revents域是文件描述符的操作结果事件掩码,内核在调用返回时设置这个域。events域中请求的任何事件都可能在revents域中返回。合法的事件如下:

  POLLIN         有数据可读。

  POLLRDNORM      有普通数据可读。

  POLLRDBAND      有优先数据可读。

  POLLPRI         有紧迫数据可读。

  POLLOUT       写数据不会导致阻塞。

  POLLWRNORM      写普通数据不会导致阻塞。

  POLLWRBAND      写优先数据不会导致阻塞。

  POLLMSGSIGPOLL     消息可用。

  此外,revents域中还可能返回下列事件:
  POLLER   指定的文件描述符发生错误。

  POLLHUP   指定的文件描述符挂起事件。

  POLLNVAL  指定的文件描述符非法。

这些事件在events域中无意义,因为它们在合适的时候总是会从revents中返回。


fds:是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集
合, 返回的事件集合.
nfds:表示fds数组的长度.
timeout:表示poll函数的超时时间, 单位是毫秒(ms).

返回值
1) <0, 表示出错;
2) =0, 表示poll函数等待超时;
3) >0, 表示poll由于监听的文件描述符就绪而返回.

epoll

为了处理大批量句柄而作了改进的poll
epoll相关的系统调用
创建一个epoll句柄
这里写图片描述
创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

epoll事件的注册函数
这里写图片描述

epfd:epoll_create()的返回值
op:表示动作,用3个宏来表示
    1)EPOLL_CTL_ADD :注册新的fd到epfd中;
    2)EPOLL_CTL_MOD :修改已经注册的fd的监听事件;
    3)EPOLL_CTL_DEL :从epfd中删除一个fd;
fd:需要监听的事件
events:需要监听什么事

这里写图片描述
events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

收集在epoll监控的事件中已经发送的事件:
这里写图片描述

 等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。
工作模式

  epoll对文件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger)。LT模式是默认模式,LT模式与ET模式的区别如下:

  LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。

  ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。

  ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值