IO多路服用

IO多路复用和线程池的区别,以及三种IO多路复用的介绍和优缺点。

IO多路复用和线程池

IO多路复用:指的同一线(进)程可以处理多个IO数据流。

多线程+线程池模型:指的是每个线程处理一个IO流。

IO多路复用的优势在于:

  • 当处理的消耗对比IO几乎可以忽略不计时,可以处理大量的并发IO,而不用消耗太多CPU和内存。这个就像是一个工作很高效的人,手上有一个todo list,他高效的依次处理每个任务;这个比每个任务单独安排给一个人节省(雇人要发工资)。
  • IO多路复用适合很多闲置的IO,因为IO socket的数量增加并不会带来线(进)程数量的增加,也不会带了堆栈内存,内核对象,切换时间的开。因此想长链接做通知的场景非常使用。
  • IO多路复用单个线(进)程的好处,就不会有并发编程的各种坑,比如nginx, redis里,编程实现都会简单很多。编程中处理并发冲突的一致性,原子性问题真的很男,极易出错。

三种IO多路复用技术

select

原理:

  • 建立服务端socket
  • 初始化文件描述符,并得到最大的文件描述符
  • 对文件描述进行清零,并将文件描述符跟位图的对应位绑定
  • 调用select阻塞,直到有一个或多个文件描述符接受到数据
  • 最后遍历每个文件描述符,看位图中对应是否为1,如果是1,表示接受到了数据,于是读取数据并进行处理。

优点:

将整个位图拷贝到内核态,将对应有数据的文件描述在位图中置1;而不是每次切换到内核态去判断位图哪个应该置为1。因为接受socket通信这些操作都是由内核态处理。

缺点:

  • 位图需要从用户态拷贝到内核态,然后从内核态拷贝到用户态
  • 位图大小有限制
  • 位图每次有需要清零
  • 遍历的复杂度为o(n)

poll

pollfd结构体,其中fd表示文件描述符,events文件描述符记录事件,包括POLLIN或者POLLOUT,或者两者的或。如果收到了数据,将对象结构体的revents置为POLLIN或POLLOUT。处理完成后,需要将revents置为0。

1
2
3
4
poll(polldfs, 5, 50000);
//polldfs:表示结构体数组
//5:数组大小
//50000:超时时间

原理:

跟select不同是,不再采用位图,而是采用结构体。这样的好处是,没有数量的限制。同时也不需要清零。

其他操作跟select相同。是一个阻塞函数。

优点:

  • 没有大小限制,数组的大小远不止2014
  • 不需要清零,每次对revents置位,因而可以重用

缺点:

  • 需要将结构体从用户态拷贝到内核态,然后从内核态拷贝到用户态
  • 遍历所有结构体,时间复杂度o(n)

epoll

epoll通过等待队列来管理接受到数据的文件描述符。这样的好处就是不用每次遍历所有结构体。

原理:

  • 利用epoll_create创建一个epfd,执行epoll_wait是需要使用,epfd相等于一个白板,参数没有意义
  • epoll_ctl对epfd(白板)进行操作,在上面添加(EPOLL_CTL_ADD)文件描述符和events对。
  • epoll_wait进行阻塞, 并收到数据时,

优点:

  • 效率提升:不是轮询的方式,不会随着文件描述符的增加而效率下降。只有活跃可用的文件描述符才会调用回调函数。
  • 没有最大并发数限制
  • 内存拷贝:利用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放在内核的一个事件表中,在用户空间和内核空间拷贝只需一次。mmap()文件映射内存与内核空间的消息创建,即epoll使用mmap减少复制开销。

参考资料:

[1] https://www.zhihu.com/question/306267779

[2] https://blog.csdn.net/baiye_xing/article/details/74353066

[3] https://blog.csdn.net/baiye_xing/article/details/76352935

[4] https://www.bilibili.com/video/BV1qJ411w7du?t=1628

zxp wechat
欢迎关注微信公众号!