1. CPU是如何处理IO操作的
- 在计算机底层,本质上有两种IO实现方法:一种是利用特定的IO机器指令;另外一种是复用内存读写指令。就像CPU内部有寄存器一样,设备也有自己的寄存器-设备寄存器(这些寄存器包括用于存放数据的寄存器,以及存放控制信息和状态信息的寄存器),获取设备产生的数据以及对设备进行控制都是通过读写这些寄存器来实现的。
- IO机器指令:使用特定的IO指令,如x86中的IN和OUT机器指令
- 内存映射IO:复用内存操作指令LOAD/STORE,把地址空间的一部分分配给设备,从而可以像读写内存一样,操作设备。
硬件设备,例如磁盘,其硬件组成部分就包括一个设备控制器(在其中包含以上的寄存器),操作系统中的驱动程序,便主要与该控制器打交道。
2. 中断驱动式IO
- 当CPU处理IO操作时,会挂起发起IO请求的线程,并将线程放入阻塞队列。
- 待IO操作完成,硬件触发IO中断,调用系统IO中断处理函数,进而恢复对应线程。
CPU执行机器指令的过程可以被划分为几个典型的阶段,如取值、解码、执行、回写。CPU在最后一个阶段需要去检测是否有硬件产生的终端信号
3. 直接存储器访问:DMA
之前尽管设备控制器能够将数据从磁盘读取到自己的buffer中,但是此后CPU仍然需要亲自执行数据传输指令,将设备控制器中的数据复制到内存中。对于CPU来说,亲自复制数据是一件极其浪费计算资源的事情,为此,后来设计了一种新的机制,可以在没有CPU参与的情况下直接在设备和内存之间传输数据,这种机制就是DMA(Direct Memory Access)
4. 高级IO技术:IO多路复用和MMAP
1. IO多路复用
-
场景:服务器需同时处理多个网络访问,与成千上万的客户端进行通信。这时你需要处理的就不再是一个文件描述符这么简单,而有可能要处理成千上万个文件描述符。对于文件描述符的IO操作,都是阻塞式的。为了提高处理效率,我们可以采用多线程,为每个客户端开启一个线程,但这种方法的问题在于随着线程数的增加,线程调度的开销会增加,这显然无法很好应对高并发场景。
-
三剑客:select、 poll、epoll: select、poll、epoll都是同步IO多路复用技术,当调用这些函数时,如果所需要监控的文件描述符都没有我们需要处理的事件出现时,那么调用线程会被阻塞而暂停运行,直到有文件描述符产生这样的事件时该函数才会返回。
-
select: 监控的文件描述符有数量限制,通常不超过1024个。当任何一个被监听文件描述符出现可读或者可写事件时,会换下对应线程。这里的问题是,当进程被唤醒后不知道哪个文件描述符可读写,需要自行遍历检查,因此select比较低效。
-
poll: 功能同select,仅解决了select监控文件描述符的数量限制。
-
epoll: 线程被唤醒后,可直接获取对应文件描述符,性能更好。
-
2. MMAP
-
简述:像读写内存一样去读写磁盘文件
-
优势:我们常用的IO函数,如read/write函数,其底层涉及系统调用。使用这些函数读写文件时,都需要将数据从内核态拷贝至用户态(或者从用户态拷贝至内核态),显然,这些操作都是有开销的。mmap则没有数据从内核态到用户太的拷贝过程,提高了IO效率。
-
开销:为实现mmap,操作系统内核中需要有特定的数据结构来维护进程地址空间与文件映射关系,这会带来一定的性能开销。此外还有缺页问题,当出现缺页中断时,相应的终端处理函数会把真正的文件加载进内存。
-
应用:系统动态库加载
网友评论