关键词:
poll | epoll | select | channel | Input | output |
---|---|---|---|---|---|
AIO | BIO | NIO | 多路复用 | 多线程 | 进程 |
用户态 | 内核态 | 字节流 | 字符流 | ||
IO种类分为: 阻塞IO,非阻塞IO,IO多路复用,信号驱动IO,异步IO 共5种
IO 全程 Input/Output,针对不同的数据存储媒介,大致可以分为网络 IO 和磁盘 IO 两种。一个完整的 IO 操作将经历一下两个阶段:用户空间 内核空间 设备空间
。
IO具体是什么操作?
干了什么事?
为什么有5种类型?
5种类型分别解决了什么问题?
只用阻塞IO可以吗?
IO牵扯到具体的指令有哪些?
IO读操作
真正的IO读操作:外部设备的数据经过总线进入RAM中系统空间的系统缓冲区。
用户看到的假IO操作:用户调用IO接口函数,从系统缓冲区读取数据读到自己的缓冲区或某个变量中
IO写操作
真正的IO写操作:RAM中系统空间的系统缓冲区中的数据经过总线进入外部设备。
用户看到的假IO操作:用户调用IO接口函数,把自己的某个变量或者自己的缓冲区写入系统缓冲区。
实际IO过程
用户调用IO接口函数后会询问内核缓冲区是否有目标数据,如果没有内核缓冲区在外部设备准备好后接收IO接口传来的数据,内核缓冲区读取外部设备的数据的过程是IO过程。
阻塞IO:
需要等待内核 IO 操作彻底完成后才返回到用户空间的 IO 操作。在 IO 操作过程中,发起 IO 请求的用户进程处于阻塞状态。
模型如下:
在这里插入图片描述
同步非阻塞IO
不需要等待内核 IO 操作彻底完成就能立即返回用户空间的 IO 操作。在 IO 操作过程中,发起 IO 请求的用户进程处于非阻塞状态。
模型:
在这里插入图片描述
IO多路复用
解决了 NIO 中的频繁轮询 CPU 的问题,并且引入一种新的 select 系统调用。
复用 IO 的基本思路就是通过 slect 调用来监控多 fd(文件描述符),来达到不必为每个 fd 创建一个对应的监控线程的目的,从而减少线程资源创建的开销。一旦某个描述符就绪(一般是内核缓冲区可读/可写),内核就能够将文件描述符的就绪状态返回给用户进程(或者线程),用户空间可以根据文件描述符的就绪状态进行相应的 IO 系统调用。
Linux中IO复用的实现方式主要有Select,Poll和Epoll:
Select:注册IO、阻塞扫描,监听的IO最大连接数不能多于FD_ SIZE(1024)。
Poll:原理和Select相似,没有数量限制,但IO数量大,扫描线性性能下降。
Epoll :事件驱动不阻塞,mmap实现内核与用户空间的消息传递,数量很大,Linux2.6后内核支持。
IO 多路复用(IO Multiplexing)属于一种经典的 Reactor 模式实现,有时也称为异步阻塞 IO。
poll和epoll的区别:
在Java中,epoll和poll是用于实现I/O多路复用的机制,但它们在底层实现和使用方式上有一些区别。
1.poll是传统的I/O多路复用机制,而epoll是Linux特有的高性能I/O多路复用机制。
2.poll使用的是轮询方式,它通过遍历文件描述符集合来检查是否有事件发生,效率相对较低。
3.epoll使用的是事件通知方式,它通过注册文件描述符和事件,并在事件发生时立即通知应用程序,避免了不必要的轮询,提高了效率。
在这里插入图片描述
信号驱动IO
信号驱动IO是异步阻塞IO,不需要等待IO的过程,但是从内核读取数据受到阻塞
在这里插入图片描述
异步IO
异步IO从图上看很像信号驱动IO,但是实际差别很大,异步IO指异步非阻塞IO。异步IO不需要建立信号处理程序,再把IO接口函数放进去。异步IO只需要调用IO接口函数,在里面加入数据处理函数即可,并且不存在任何阻塞
在这里插入图片描述
总结:
对比:
在这里插入图片描述
实际的多线程模型:
根据博客参考,实际的多线程模型有很多。在《操作系统》中介绍较少,理解起来并不是很全面。
多对一模型:进程下的多个用户级线程对应着一个核心级线程,这也就是《操作系统》中说这句话的默认情景。操作系统知道核心线程,不知到用户线程,当用户线程阻塞时会认为是核心级线程阻塞,那么操作系统会调度另一个核心级线程执行。
一对一模型:进程下的每个用户级线程对应着属于自己的核心级线程。当一个线程被阻塞时,操作系统能够让另一个核心级线程对应的用户线程继续执行。缺点是每个用户线程都会创建一个核心级线程,未免有些浪费。
多对多模型:进程下的每个用户级线程对应着多个核心级线程,每个核心级线程也对应着多个用户级线程。这个模型能完成前两个模型能完成的任务,但是实现复杂。