高级IO

目录

1. 五种典型的IO模型

1.1 阻塞IO

1.2 非阻塞IO

1.3 信号驱动IO

1.4 异步IO

1.5 多路转接IO

 1.5.1 select模型

 1.5.2 poll模型

1.5.3 epoll模型-Linux下最好用的多路转接模型


1. 五种典型的IO模型

  • IO完成过程:1. 等待IO就绪(满足IO的条件) 2. 进行数据拷贝。

1.1 阻塞IO

  • 发起IO调用,若IO条件不具备,则一直等待。
  • 优点:流程非常简单,一个IO完成后进行下一个。
  • 缺点:无法充分利用资源,效率较为低下。

1.2 非阻塞IO

  • 发起IO调用,若IO条件不具备,则立即报错返回,可以做其他事情,完毕后循环回来重新发起IO请求。
  • 优点:相较于阻塞IO,对资源的利用较为充分,可以利用就绪等待时间做其他事情或者发起另一个IO调用。
  • 缺点:相较于阻塞IO,流程稍微复杂,IO调用需要循环发起,IO不够实时。

1.3 信号驱动IO

  • 定义IO就绪信号处理方式,收到信号则认为IO就绪,发起IO调用。
  • 优点:相较于非阻塞IO,更加实时,资源利用更加充分。
  • 缺点:流程更加复杂,需要定义信号处理,既有主控流程又有信号处理流程,涉及到信号是否可靠的问题。

1.4 异步IO

  • IO顺序不确定,IO过程(等待+数据拷贝)由系统完成。定义IO完成信号处理方式自定义,发起异步IO调用,告诉系统要完成什么功能,剩下的IO功能完全由系统完成,完成后通过信号通知进程。
  • 优点:对资源利用最充分,以高效率进行任务处理。
  • 缺点:资源消耗比较高,流程最复杂。

以上这四种IO,其实就是处理效率逐步增加,对资源的利用更加充分,流程越来越复杂的进化过程。

  • 阻塞:为了完成一个功能发起调用,若不具备完成功能的条件,则调用一直等待。
  • 非阻塞:为了完成一个功能发起调用,若不具备完成功能的条件,则立即报错返回。
  • 阻塞与非阻塞的区别:常用于讨论调用函数是否阻塞,表示这个函数无法立即完成功能时是否立即返回。
  • 同步:功能完成的流程通常是顺序化的,并且功能是进程自身完成的。
  • 异步:功能完成的流程通常是不确定的,且功能不是由进程自身完成的,由系统完成。
  • 异步的种类:异步阻塞->等待别人完成功能  /  异步非阻塞->不等待别人完成功能。
  • 同步与异步的区别:主要用于讨论功能的完成方式,表示一个功能是否是顺序化且有进程自身完成。同步处理流程简单,同一时间占用资源少,但是异步处理效率高,同一时间占用资源多。

1.5 多路转接IO

  • 主要用于进行大量的IO就绪事件监控,能够让我们的进程只针对指定就绪事件的IO进行操作,能够避免阻塞和提高效率。
  • 可读事件:一个描述符当前是否有数据可读。
  • 可写事件:一个描述符当前是否可以写入数据。
  • 异常事件:一个描述符是否发生了异常。

操作系统提供了三种模型(select模型,poll模型,epoll模型)实现多路转接IO。

1.5.1 select模型

使用流程,接口介绍及原理理解。

1. 用户定义想要监控事件的描述符集合。例如,定义可读事件的描述符集合,将哪些描述符添加到这个集合中,就表示要对这个描述符监控可读事件。因此,想要监控什么事件就定义什么集合。

  • 1. 定义指定事件的集合
  • 2. 初始化集合
  • 3. 将需要监控事件的描述符添加到这个集合中
  • 集合:fd_set结构体,结构体中只有一个成员,就是一个数组->作为位图进行使用。向集合中添加一个描述符,描述符就是一个数字,添加描述符其实就是这个数字对应的比特位置1,表示这个描述符被添加到集合中。
  • 这个数组中有多少个比特位或者说select最多能监控多少个描述符,取决于宏_FD_SETSIZE,默认是1024.
  • fd_set rfds;
  • void FD_ZERO(fd_set*  set); 清空指定的描述符集合
  • void FD_SET(int fd, fd_set* set);将fd描述符添加到set集合中。

2. 发起调用,将集合中数据拷贝到内核中进行监控,监控采用轮询遍历判断方式进行。

  • int select(int nfds, fd_set* readfds, fd_set* writefds, fe_set* exceptfds, struct timeval* timeout);
  • nfds:所有集合中最大的那个描述符的数值+1,为了减少内核中的遍历次数。
  • readfds:可读事件集合
  • writefds:可写事件集合
  • exceptfds:异常事件集合
  • timeout:select默认是一个阻塞操作(struct timeval{tv_sec; tv_usec}), 若timeout=NULL表示永久阻塞直到有描述符就绪在返回;若timeout中的数据为0,则表示非阻塞,没有就绪则立即返回;若timeout有数据,若指定时间内没有描述符就绪则超时返回。
  • 返回值:返回值小于0,表示监控出错;返回值等于0,表示没有描述符就绪;返回值大于0,表示就绪的描述符个数。
  • select会在返回之前做一件事情:将所有集合中没有就绪的描述符都从集合中移除出去(调用返回后,集合中保存的是就绪的描述符)

3. 程序员遍历判断哪个描述符还在哪个集合中,确定哪个描述符就绪了哪个事件,进而进行相应操作。

其他操作:

  • void FD_CLR(int fd, fd_set* set); 从集合中移除fd描述符;
  • int FD_ISSET(int fd, fd_set* set);判断fd描述符是否在set集合中。

select模型优点:

select模型缺点:

 1.5.2 poll模型

接口及流程如下:

  • int poll(struct pollfd* fds, nfds_t nfds, int timeout);
  • fds:要监控的描述符事件结构体;
  • nfds:实际第一个参数描述符事件结构体数量;
  • timeout:超时等待时间->毫秒。
  • struct pollfd{int fd;//要监控的描述符; short events;//描述符想要监控的事件;short revents;//实际就绪的事件}
  • events:POLLIN-可读/POLLOUT-可写
  • revents:当poll接口调用返回的时候,这个描述符实际就绪的事件就会被写入revents中,程序员就是通过这个成员进行判断的。
  • 返回值:返回值大于0,表示就绪的描述符个数;返回值等于0,表示监控超时;返回值小于0,表示监控出错。

1. 定义描述符事件结构体数组,将需要监控的描述符以及对应的事件信息填充到数组中。例如:struct pollfds fds[10]; fds[0].fd=0; fds[0].events=POLLIN| POLLOUT; 对标准输入监控可读事件以及可写事件。

2. 发起监控调用poll,将数组中数据拷贝到内核中进行轮询遍历监控,有描述符就绪或者等待超时后返回,返回时将这个描述符实际就绪的事件填充到对应结构体的revents中(如果描述符没有就绪,则revents中数据为0) int poll(struct pollfd* fds, nfds_t nfds, int timeout);

3. 监控调用返回后,程序员在程序中遍历数组中每个节点的revents,确定当前节点的描述符就绪了什么事件,进而进行对应操作。

poll模型优点:

poll模型缺点:

1.5.3 epoll模型-Linux下最好用的多路转接模型

接口及流程如下:

1. 在内核中创建eventpoll结构体,返回一个描述符作为代码中的操作句柄。

  • int epoll_creat(int size);
  • size:要监控的描述符最大数量,但是在Linux2.6.8之后被忽略,只要大于0即可。

2. 对需要监控的描述符组织事件结构体,将描述符以及对应事件结构添加到内核中的eventpoll结构体中

  • int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
  • epfd:epoll操作句柄;
  • op:EPOLL_CTL_ADD/EPOLL_CTL_MOD/EPOLL_CTL_DEL;
  • fd:要监控的描述符;
  • event:监控描述符对应的事件结构体信息
  • struct epoll_event{
  • uint32_t events;//表示要监控的事件,以及监控调用返回后实际就绪的事件
  • union epoll_data{int fd;//描述符; void* ptr;}data; data.fd/data.ptr}
  • 每一个需要监控的描述符都会有一个对应的事件结构,当描述符就绪了监控的事件后,就会将这个事件结构体返回给程序员。

3. 开始监控,当有描述符就绪或者等待超时后监控调用返回

  •  只需要对evs数组进行遍历,逐个判断events是什么事件,然后对fd描述符进行操作即可。
  • 异步操作:完成流程不固定,实际功能并不由自身完成。
  • epoll_wait异步阻塞操作:只是发起监控请求,实际监控过程由操作系统完成->异步操作。
  • 操作系统进行监控就是,当描述符就绪了指定事件,则将描述符对应事件信息放到双向链表中(系统对每个描述符就绪事件做回调函数做的事情)
  • epoll_wait调用发起后,调用并没有立即返回,而是等待双向链表不为空或者超时后返回->阻塞操作。

 IO事件就绪

epoll的事件触发模式:如何触发IO就绪事件

  •  边缘触发,是有新数据到来就会触发事件。

多路转接模型进行服务器并发处理与多进程/多线程并发并行处理有什么区别?

epoll模型优点:

epoll模型缺点: 

udp能不能使用epoll?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值