为什么会有I/O多路复用
首先,要知道I/O是什么,I/O == copy数据,由内往外,由外往内 来回Copy数据
copy 数据由 进程内存空间 —>操作系统内部 —> 外部设备 就是输出,反过来就是输入
进程在I/O操作时会发生什么?
但是由于I/O操作中外设的速度相对于内存的速度较慢, 进程不得不等待外部设备把数据拿过来,这会导致进程被挂起,整个程序停止执行,这样根本不可行。
如果使用线程来处理I/O可行吗?
在进行IO操作,线程同样会被阻塞,但不影响别的线程继续处理新的请求,整体上,程序依然可以正常执行,这样看上去是可行的,-但是面对大量的I/O请求?还行吗?可能你会说,可以创建更多的线程来处理,当然可以,但是,大量的线程是要耗费大量的内存(再加上很多线程都被阻塞,那一部分属于现成的内存,根本就没有使用到,白白浪费),同时维护和切换线程执行都会导致系统性能下降。
所以也不可行。
那该怎么办?
能不能实现一个线程 既能处理大量的I/O操作,又不会阻塞,继续接受新的请求呢?
当然是有的,答案就是通过I/O多路复用来实现。
一个线程在面对大量的I/O操作时,将对应的文件标识符交给操作系统,没有数据可读时,线程不会阻塞在这里,而是继续处理新来的请求,而操作系统会帮忙监控这这些文件标识符,如果有文件可读时,再去通知线程来处理。
Linux操作系统中,I/O多路复用的方式,主要有三种 select poll epoll,具体的实现细节不同,这里不再详细介绍,只做简单总结
select 支持有限的监控文件标识符的数量,使用数组存储,由进程空间拷贝到操作系统内核空间,有数据可读时,只是通知线程,但没有告诉线程是哪一个文件
poll 底层使用链表存储文件标识符,只解决了数量限制问题
epoll 通过共享内存方式,解决copy 文件标识符的问题,通过红黑树底层数据结构,通知到具体的线程以及需要操作的文件标识符(进行I/O处理的不止只有一个线程,因此还定位到了线程)
文件标识符:就是来标志文件的,通过文件标识符,线程能够读取,写入数据到对应的文件。这与Linux操作系统所宣扬的:一切皆文件,相对应。