操作系统与网络 (13. 典型IO/多路转接模型)

准确表达自己的诉求并且懂得拒绝,会在很大程度上减少生活中的烦恼

13. 典型IO/多路转接模型

13.1 典型IO
13.1.1 分类

(1) 阻塞IO

  • 在内核将数据准备好之前,系统调用会一直等待,所有的套接字默认都是阻塞方式。

(2)非阻塞IO

  • 如果内核还未将数据准备好,系统调用仍然会直接返回,并且返回EWOULDBLOCK错误码;非阻塞IO往往需要程序员循环的方式反复尝试读写文件描述符,这个过程称为轮询。

(3) 信号驱动IO

  • 内核将数据准备好的时候,使用SIGIO信号通知应用程序进行IO操作。

(4) 异步IO

  • 由内核在数据拷贝完成时,通知应用程序(而信号驱动是告诉应用程序何时可以开始拷贝数据)。

(5) 多路转接IO

  • 对大量的的IO事件是否就绪的监控, 并且可以告诉用户哪一个 IO 当前就绪,这个时候用户可以针对就绪的 IO 进行操作。
13.1.2 IO 流程
  • 等待IO就绪+IO数据拷贝

几种典型IO的操作对比

  • 这几种IO是一个IO的发展过程, 他们的使用效率以及对资源的利用率越来越高, 但是占用的资源越来越多, 并且流程控制越来越复杂.
13.1.3 阻塞、非阻塞、同步、异步

同步

  • 为了完成一个功能发起调用, 若当前不具备完成条件, 则进行进程等待, 直到完成功能;
  • 同步功能由进程自身完成, 通常都是阻塞的;

异步

  • 为了完成一个功能发起调用,进程自身并不完成,功能由操作系统完成后通知进程,同步与异步强调的是发起调用后,功能是否是由进程自身完成的;异步功能是由别人完成,通常有阻塞也有非阻塞。

异步阻塞

  • 等待别人完成功能。

异步非阻塞

  • 不等待别人完成,调用发起后则直接返回。

同步与异步优缺点

  • 同步流程更加简单,异步对资源的利用更加充分,但是流程控制比较复杂;发起调用之后,是否能够立即完成功能,并调用返回。
13.2 多路转接模型(多路复用)
13.2.1 select

(1)接口使用

  • int select(int nfds, fd_set *readfds, fd_set *writefds,
    fd_set *exceptfds, struct timeval *timeout);

(2)参数解析

  • nfds:maxfd+1 最大的描述符+1; 避免空遍历判断, 提高性能;
  • readfds/writefds/exceptfds:可读/可写/异常事件集合;
  • timeout: struct{tv_sec; tv_usec}为 select 的阻塞监控做一个超时返回;
  • NULL: 永久阻塞 内部时间为0则立即返回
  • fdset: 描述符集合–位图存储–大小默认为1024, 取决于_FD_SETSIZE这个宏
  • 返回值: >0 返回就绪的描述符个数 ==0 等待超时 <0 监控出错

(3)原理流程

  • 1.用户自己定义一个描述符集合fd_set, 然后将需要监控的描述符添加到集合中;
  • 2.将描述符集合拷贝到内核中;
  • 3.对集合中的的描述符进行轮询就绪判断:若没有描述符就绪, 则挂起等待, 在此进行轮询判断, 若超时, 则直接返回;若有描述符就绪, 则select调用返回, 并且返回就绪的描述符个数;
  • 4.调用返回之前, 将描述符集合中的非就绪描述符移除(集合中遗留的都是就绪描述符);
  • 5.进程判断哪个描述符在集合中, 这个描述符就是就是就绪的, 就而对其进行IO操作

(4)优缺点分析
优点

  • 1.select遵循posix标准, 可以跨平台;
  • 2.select监控超时等待时间, 可以精确到微秒;

缺点

  • 1.select所能监控的描述符数量有最大上限 – 1024(_FD_SETSIZE);
  • 2.select每次都需要将描述符集合拷贝到内核进行监控(用户态与内核之间的数据拷贝);
  • 3.select在内核中对所有描述符进行轮询遍历判断是否就绪(性能随着描述符增多而降低);
  • 4.select就绪后移除集合中非就绪描述符, 修改集合;每次监控都需要新添加描述符(编码复杂);
  • 5.select返回给用户就绪的描述符集合, 但是不会直接告诉用户哪些描述符就绪(需要用户进行遍历判断哪些描述符在集合中,才能找出就绪的描述符进而对其进行操作),性能随着描述符增多而降低;
13.2.2 poll

(1)接口使用

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
// pollfd结构
struct pollfd {
	int fd; /* file descriptor */
	short events; /* requested events */
	short revents; /* returned events */
};

(2)参数解析

  • fds 是一个 poll函数 监听的结构列表,每一个元素中,包含了三部分内容:文件描述符、监听的事件集合、返回的事件集合;
  • nfds表示 fds数组 的长度;
    timeout 表示 poll函数 的超时时间,单位是毫秒(ms);
    返回值::<0 表示出错;==0表示poll函数等待超时; >0返回就绪的描述符个数。

(3)原理流程

  • 1.用户Wie每一个关心的描述符定义事件结构(文件描述符/用户所关心的事件);
  • 2.将描述事件结构数组拷贝到内核中进行监控;
  • 3.轮询遍历事件数组中的描述符, 判断描述符是否就绪了某个事件:若没有描述符就绪用户所关心的事件, 则每隔一会遍历判断一次;若有描述符就绪用户所关心的事件, 则会将就绪的事件放到事件结构的revents中,并调用返回(性能随着描述符增多而降低);
  • 4.当调用返回后, 用户遍历事件结构数组, 判断结构中的revents事件中是否包含用户所关心的事件, 通过这种方式判断哪一个描述符就绪了, 进而对描述符进行相应操作。

(4)优缺点分析
优点

  • 1.poll采用事件结构的方式对描述符进行监控, 简化了多个描述符集合的监控编码流程;
  • 2.poll没有描述符数量上的限制;

缺点

  • 1.poll每次监控都需要将所有的事件结构信息拷贝到内核;
  • 2.在内核中poll进行就绪判断同样适用轮询遍历的判断(性能随着描述符增多而降低);
  • 3.描述符事件就绪后, poll修改描述符事件结构中的revents信息为当前就绪的事件;但是poll不会直接告诉用户哪一个描述符就绪, 需要用户对事件结构数组进行遍历, 判断哪一个结构中的revents是用户关心的事件, 则这个描述符是就绪的进入而对其进行操作;
  • 4.poll不能跨平台操作;
  • 5 poll相较于select实际上对大量描述符进行监控的原理并没有发生改变, 因此性能并没有多大提高;
13.2.3 epoll

(1)接口使用

  • int epoll_create(int size);
  • 创建epoll, 在内核中创建eventpoll结构体struct eventpoll{…红黑树rbr, …双向链表rdllist,…}
    返回一个文件描述符, 可以通过描述符找到内核中的eventpoll结构体;

(2)参数解析

  • size: 限制epoll所能监控的描述符数量上限, 自从Linux2.68之后被忽略,需要>0;
    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    它不同于select()是在监听事件时告诉内核要监听什么类型的事件, 而是在这里先注册要监听的事件类型.
    epfd: 是epoll_create()的返回值(epoll的句柄).
    op: 表示动作,用三个宏来表示,EPOLL_CTL_ADD,EPOLL_CTL_MOD,EPOLL_CTL_DEL;
    fd: 是需要监听的fd.
    *event: 告诉内核需要监听什么事.
struct epoll_event{
	uint32_t events;
	epoll_data_t data;
	_EPOLL_PACKED;
}
  • int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
    epfd: epoll_create返回的epoll操作句柄, 描述符;
    events: epoll_event结构体数组首地址, 用于接收就绪的描述符事件信息;
    maxevents: epoll_event结构体数组结点个数;
    timeout: epoll的超时等待时间(毫秒);
    返回值: >0就绪的描述符个数 ==0 等待超时 <0 监控出错

(3)原理流程

  1. 通过epoll_create创建eventpoll结构, 返回操作句柄;
  2. 通过操作句柄向内核中添加描述符的监控事件epoll_ctl;
  3. 通过epoll_wait开始监控,异步阻塞操作, 描述符的监控由操作系统完成;
    操作系统: 当描述符就绪了用户所关心的事件, 则将事件信息添加到eventpoll的双向链表中,每隔一段时间则会看eventpoll中双向链表是否为空, 判断是否有描述符就绪;
    若没有就绪, 则挂起等待, 隔一会继续判断, 若超时则返回;
    若有就绪返回时, epoll_wait将就绪的事件信息放置到事件数组events中,返回给用户;
  4. 用户通过epoll_wait传入的events获取到就绪的事件,直接遍历操作;

(4)优缺点分析
优点

  • 1.epoll不能跨平台;
  • 2.监控超时等待时间只能精细到毫秒;

缺点

  • 1.监控的描述符数量无最大上限;
  • 2.采用事件结构对描述符进行监控, 简化了多个描述符集合的监控操作流程;
  • 3.epoll的事件信息, 每条信息只需要向内核拷贝一次;
  • 4.epoll是一个异步阻塞操作, 操作系统对描述符事件进行监控, 而这个操作系统实现的监控采用事件回调的方式, 不需要进行轮询遍历描述符进行判断, 性能不会随着描述符增多而降低;
  • 5.当描述符就绪后将描述符事件信息添加到双向链表中,epoll_wait只需要隔会判断双向链表是否为空, 就可以判断出是否有就绪描述符事件就绪;
  • 6.当epoll_wait返回时, 直接将就绪的事件信息拷贝到用户给与的事件结构数组中,相当于直接告诉了哪些描述符就绪了(事件结构中包含描述符), 用户可以直接对描述符进行操作(避免非就绪描述事件遍历);
  • 说明
    IO多路转接模型, 可以实现对大量描述符就绪事件监控, 可以让进进程针对就绪描述符进行操作, 可以让进程,线程避免因为对非就绪描述符进行操作而阻塞, 可以让一个进程轮询对大量的描述符进行操作, 实现一个服务器与多个客户端进行数据通信, 因此IO多路转接模型是一种高并发模型;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值