多路复用

5种IO模型

  1. 阻塞IO

    ​ 调用者调用某个函数,不停的去检查这个函数有没有返回,必须等这个函数返回才能进行下一步动作

  2. 非阻塞IO

    ​ 非阻塞等待,每隔一段时间就去检测IO事件是否就绪。没有就绪就可以做其他事

  3. 信号驱动IO

    linux用套接口进行信号驱动IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO时间就绪,进程收到SIGIO信号。然后处理IO事件(将数据从内核态复制到用户态,这部分是阻塞的)

  4. 异步IO

    调用aio_read函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方式,然后立即返回,当内核将数据拷贝到缓冲区后(相比信号驱动更加彻底,数据已经从内核态切换到用户态),再通知应用程序

  5. IO复用/多路转接IO

    linux用select/poll函数实现IO复用模型,这两个函数也会使进程阻塞,但是和阻塞IO所不同的是这两个函数可以同时阻塞多个IO操作。而且可以同时对多个读操作、写操作的IO函数进行检测。知道有数据可读或可写时,才真正调用IO操作函数

IO多路复用

  • I/O多路复用和阻塞I/O不同点在于可以同时处理多个connection。
  • 因为使用两个system call (select 和 recvfrom),连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好。

IO复用优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接

IO multiplexing Model中,对于每一个socket一般为non-blocking。process是被select这个函数block,而不是被socket IO给block。

IO多路复用为什么比多线程效率高

本质:

IO多路复用还是需要多线程,但是所需要的线程数相对单纯多线程大大减少了,只需要相对少量的线程处理就绪的网络IO

结果:

需要的线程数大大减少,减少了内存开销和上下文切换的CPU开销

IO多路复用阻塞

  • 只在调用select、poll、epoll这些调用的时候才会阻塞
  • 收发客户消息是不会阻塞的,整个进程或者线程就被充分利用起来

select

用结构体fd_set来告诉内核监听多个文件描述符,该结构体被称为描述符集。

轮询方式查询每个fd对应的设备状态,没有发现就绪设备,则挂起进程,直到设备就绪或者超时,唤醒后又再次遍历轮询。

select机制
  • copy_from_user从用户空间拷贝fd_set到内核空间
  • 遍历所有fd,调用对应的poll方法,核心是__pollwait回调函数
  • __pollwait把当前进程挂到设备的等待队列中,在设备就绪后(收到数据或者写完数据),会唤醒等待队列上睡眠的当前进程
  • poll方法返回时会返回一个代表是否就绪的mask掩码,根据mask掩码给fd_set复制
  • 如果遍历完所有fd,没有返回一个可读写mask掩码,则select进入睡眠
  • 当驱动设备就绪后会唤醒等待队列上睡眠的进程
  • 如果timeout后还没人唤醒,那么调用select的进程重新获得cpu,重新遍历fd
  • 将fd_set从内核空间拷贝到用户空间
select缺点
  1. select的最大文件数受限与FD_SIZE;

  2. 每次调用select前都要重新初始化描述符集,将fd从用户态拷贝到内核态,每次调用select后,都需要将fd从内核态拷贝到用户态;

  3. 轮询(内核态一次、用户态一次)排查当文件描述符个数很多时,效率很低。

poll

poll本质上和select没有区别,基于链表存储保存描述符的结构体,解决了select文件描述符数量受限问题。

  1. 数组遍历轮询排查未解决
  2. 描述符集在用户态和内核态之间拷贝未解决
  3. 水平触发

epoll

epoll机制
  • 调用epoll_create方法,内核创建eventpoll结构体
  • eventpoll结构体
    • 红黑树根节点,保存epitem结构体
      • 每一个epitem结构体对应着一个监控事件。
      • 红黑树有着良好的插入删除查询性能logn,便于对监控事件的添加删除修改
    • 双向链表节点,保存已经满足条件的就绪事件
      • epoll_wait调用时,仅仅观察这个rdllist双向链表里有没有数据,有就返回,没有就sleep阻塞,直到timeout返回
  • 所有添加到epoll的事件都会和设备驱动程序建立回调关系,响应事件发生时,会把事件放到双向链表
调用方式
  • epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)
  • 调用epoll_ctl向epoll对象中添加这100万个连接的套接字
  • 调用epoll_wait收集发生的事件的连接
epoll优点
* 只返回状态发生变化的文件描述符,避免了内核态到用户态大量文件描述符拷贝,避免了用户态轮询方式查询所有就绪事件
* epoll中的事件都会与设备(如网卡)驱动程序**建立回调关系**,不需要每次去轮询是否就绪
* epoll没有最大连接数限制,而select有
* epoll性能不会随着fd数量的增减导致性能线性下降,而slect和poll会下降
* epoll利用mmap()文件映射内存,实现内核和用户空间共享一块内存减少了复制开销。而select和poll需要在内核、用户之间来回拷贝
  1. LT模式
  • 缺省工作模式
  • 同时支持block和no-block socket.
  • 只要有数据都会触发,缓冲区剩余未读尽的数据会导致epoll_wait返回。
  1. ET模式
  • 高速工作方式
  • 只支持no-block socket
  • 只有数据到来才触发不管缓存区中是否还有数据,缓冲区剩余未读尽数据不会导致epoll返回
  1. ET使用场景
  • LT模式下,如果大量不需要读写的就绪文件描述符,每次调用epoll_wait都会被返回,降低程序检索关心的就绪文件描述符的效率
  • ET模式可以避免这种情况
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值