五种 IO 模型
1、阻塞式 IO
- 阻塞 IO 是最流行的 IO 模型
- 在网络 IO 的时候,进程发起 recvfrom 系统调用,然后进程就被阻塞了,什么也不干,直到数据准备好,并且将数据从内核复制到用户进程,最后进程再处理数据,在等待数据到处理数据的两个阶段,整个进程都被阻塞。不能处理别的网络 IO
这就好比我们去钓鱼,抛竿之后就一直在岸边等,直到等待鱼上钩。然后再一次抛竿,等待下一条鱼上钩,等待的时候,什么事情也不做,大概会胡思乱想
优缺点:
优点:开发简单,容易入门。在阻塞等待期间,用户线程挂起,在挂起期间不会占用 CPU 资源
缺点:一个线程维护一个 IO ,不适合大并发,在并发量大的时候需要创建大量的线程来维护网络连接,内存、线程开销非常大
2、非阻塞式 IO
- 非阻塞的 recvfrom 系统调用之后,进程并没有被阻塞,内核马上返回给进程,如果数据还没准备好,此时会返回一个错误码。进程在返回之后,可以干点别的事情,然后再发起 recvfrom 系统调用
- 内核在没有准备好数据的时候,会循环往复的进行 recvfrom 系统调用来检查内核数据是否准备好,这个过程通常被称之为轮询
- 轮询检查内核数据,直到数据准备好,再拷贝数据到进程,进行数据处理。需要注意,拷贝数据整个过程,进程仍然是属于阻塞的状态
我们再用钓鱼的方式来类别,当我们抛竿入水之后,就看下鱼漂是否有动静,如果没有鱼上钩,就去干点别的事情,比如再挖几条蚯蚓。然后不久又来看看鱼漂是否有鱼上钩。这样往返的检查又离开,直到鱼上钩,再进行处理。
优缺点:
同步非阻塞 IO 优点:每次发起 IO 调用,在内核等待数据的过程中可以立即返回,用户线程不会阻塞,实时性好
同步非阻塞 IO 缺点:多个线程不断轮询内核是否有数据,占用大量 CPU 资源,效率不高。一般 Web 服务器不会采用此模式
3、多路复用 IO
- 类似于非阻塞 IO ,只不过轮询不是由用户线程去执行,而是由内核去轮询,内核监听程序监听到数据准备好后,调用内核程序复制数据到用户态
- select 轮询相对非阻塞的轮询的区别在于 --- 前者可以等待多个 socket,当其中任何一个socket 的数据准好了,就能返回进行可读,然后进程再进行 recvfrom 系统调用,将数据由内核拷贝到用户进程,当然这个过程是阻塞的
- 多路复用包括 select、poll 和 epoll
- select:线性扫描所有监听的文件描述符,不管他们是不是活跃的。有最大数量限制(32 位系统 1024,64 位系统 2048)
- poll:同 select,不过数据结构不同,需要分配一个pollfd结构数组, 维护在内核中。它没有大小限制,不过需要很多复制操作
- epoll:用于代替 polI 和 select, 没有大小限制。使用一个文件描述符管理多个文件描述符,使用红黑树存储。同时用事件驱动代替了轮询。epoll. ctI 中注册的文件描述符在事件触发的时候会通过回调机制激活该文件描述符。epoll. wait 便会收到通知。最后,epoll 还采用了 mmap 虚拟内存映射技术减少用户态和内核态数据传输的开销
钓鱼的时候,我们雇了一个帮手,他可以同时抛下多个钓鱼竿,任何一杆的鱼一上钩,他就会拉杆。他只负责帮我们钓鱼,并不会帮我们处理,所以我们还得在一边等着,等他把收杆。我们再处理鱼。
优缺点:
优点:系统不必创建维护大量线程,只使用一个线程、一个选择器就可同时处理成千上万连接,大大减少了系统开销
缺点:本质上,select 和 epoll 的系统调用是阻塞式的,属于同步 IO,需要在读写时间就绪后,由系统调用进行阻塞的读写
详细 “IO 多路复用” 请参考:IO多路复用
4、信号驱动 IO
- 使用信号,内核在数据准备就绪时通过信号来进行通知
- 首先开启信号驱动 IO 套接字,并使用 sigaction 系统调用来安装信号处理程序,内核直接返回,不会阻塞用户态
- 数据准备好时,内核会发送 SIGIO 信号,收到信号后进行 IO 操作
5、异步 IO
- 用户进程进行 aio_read 系统调用之后,无论内核数据是否准备好,都会直接返回给用户进程,然后用户态进程可以去做别的事情。等到 socket 数据准备好了,内核直接复制数据给进程,然后从内核向进程发送通知。IO 两个阶段,进程都是非阻塞的
- 异步 IO 和前面 IO 模型不同的是:前面的都是数据准备阶段的阻塞与非阻塞,异步 IO 模型通知的是 IO 操作已经完成,而不是数据准备完成
- 异步 IO 才是真正的非阻塞(两个阶段全是非阻塞),主程序只负责做自己的事情,等 IO 操作完成(数据成功从内核缓存区复制到应用程序缓冲区)时通过回调函数对数据进行处理
比之前的钓鱼方式不一样,这一次我们雇了一个钓鱼高手。他不仅会钓鱼,还会在鱼上钩之后给我们发短信,通知我们鱼已经准备好了。我们只要委托他去抛竿,然后就能跑去干别的事情了,直到他的短信。我们再回来处理已经上岸的鱼。
6、五种 IO 模型对比
- 前面四种 IO 模型的主要区别在第一阶段,他们在第二阶段是一样的:数据从内核缓冲区复制到调用者缓冲区期间都被阻塞住!
- 前面四种 IO 都是同步 IO :IO 操作导致请求进程阻塞,直至 IO 操作完成
- 异步 IO:IO 操作不导致请求进程堵塞
- 只有同步 IO 模型才考虑阻塞和非阻塞,因为异步 IO 肯定是非阻塞
参考文献
1)