关于IO多路复用的简单整理
并发: 一段时间内,有几个程序在同一个cpu上运行,但是任意时刻只有一个程序在一个cpu上运行
并行: 任意时刻有多个程序同时运行在多个cpu上
同步、异步,与阻塞、非阻塞不相关
函数或方法被调用的时候,调用者是否得到最终结果
同步: 直接得到最终结果的,就是同步调用。同步就是一直要执行到返回最终结果
异步: 不直接得到最终结果的,就是异步调用。异步就是直接返回了,但是返回的不是最终结果
函数或方法被调用的时候,是否立刻返回,调用者是否还能干其他事
阻塞: 不立即返回就是阻塞调用
非阻塞: 立即返回就是非阻塞调用
C10K问题,是在1999年被提出来的一个技术挑战
如何在一颗1GHzCPU、2G内存、1gbps网络环境下,让单台服务器同时为1万个客户端提供ftp服务
unix中的五种IO模型:
- 阻塞式IO
- 非阻塞式IO
- IO多路复用(select、poll、epoll)
- 信号驱动式IO
- 异步IO(posix的aio_系列函数)
IO过程分两个阶段
1、数据准备阶段,数据到达后被复制到内核的缓冲区中
2、进程从内核复制数据,将数据从内核空间复制到用户空间
IO多路复用就是通过一种机制,一个进程或可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),就通知程序进行相应的读写操作。
但select、poll、epoll本质上都是同步IO,因为在就绪后它们都需要自己负责进行读写,这个读写过程是阻塞的。
而异步IO无需自己负责进行读写,异步IO的实现会负责把数据从内核空间拷贝到用户空间。
select几乎所有操作系统平台都支持,poll是对的select的升级。
epoll,Linux系统内核2.6开始支持,对select和poll的增强,在监视的基础上,增加回调机制。BSD、Mac平台有kqueue,Windows有iocp。
select
select函数监视的文件描述符分三类,分别是writefds、readfds、和exceptfds。
调用select()会阻塞,直到有描述符就绪、或者超时,函数才返回。
当select函数返回后,可以通过遍历events来找到就绪的描述符。
select几乎支持所有平台,其良好的跨平台支持也是它的一个优点。
缺点在于单个进程能够监视的文件描述符的数量存在上限,在linux上一般为1024,可以通过修改修改这个限制,但是这样也可能会造成效率降低,不建议修改。
poll
select使用三个位图来表示三个fdset,不同于select,poll使用pollfd的一个指针实现。
pollfd结构包含了要监视的event和发生了的event,不再使用select的'参数-值'传递方式。
pollfd使用链表,没有最大数量限制,同select一样,poll返回后,需要遍历pollfd来获取就绪的描述符。
select和poll都需要在返回后通过遍历来获取已经就绪的socket。
实际上,同时连接的大量客户端在同一时刻可能只有很少的处于就绪状态,随着监视的描述符数量的增长,其效率也会线性下降。
epoll
epoll出现在内核2.6之后,是select和poll的增强版本。
相对于select和poll来说,epoll更加灵活,没有描述符数量限制,增加了回调机制,使用事件通知,不需要遍历,效率很高。
epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样,从内核空间到用户空间的copy只需一次。
epoll的查询使用了红黑树数据结构,效率很高,不过红黑树本身很复杂。
select/poll还要从内核空间复制数据到用户空间,而epoll通过内核空间和用户空间共享一块内存来减少复制。
C10M问题
如何利用8核心CPU、64G内存,在10gbps的网络上保持1000W并发连接。
异步IO
进程发起异步IO请求,立即返回。内核完成IO的两个阶段之后,即数据从内核空间复制到用户空间之后,内核才给进程发一个信号。
Linux的aio的系统调用,内核从版本2.6开始支持。
参考:
https://docs.python.org/3/library/asyncore.html
https://blog.csdn.net/twostyle/article/details/73967648
https://www.cnblogs.com/nulige/p/6297829.html