Linux下一切皆文件,连外部设备都当作文件;对文件的读写操作会调用内核系统命令,返回文件描述符,简称fd;而对socket的读写操作也会有描述符,socketfd。描述符是一个数字,指向内核中的结构体(文件路径、数据区)。IO操作其实是针对这些内核数据区的读写。
阻塞IO
所有的IO操作都是阻塞的,比如recvfrom操作,指到数据包到达并且从内核数据区拷贝到应用数据区或者发生错误才会返回。期间整个线程会被阻塞。
非阻塞IO
跟阻塞IO相反,所有的IO操作都立即返回,但是不保证每次都能顺利完成IO操作(比如recvfrom操作,如果没有数据可读,也会立即返回),因此需要通过轮询的方式确保IO操作完成。
IO复用
不管是上述的阻塞还是非阻塞,都是针对单描述符来说的,都会导致当前进程/线程的占用(阻塞或者轮询消耗),不能处理其他描述符。而IO复用就是解决整个问题出现的,将多个IO的阻塞复用到同一个select阻塞上。
select/poll:其实就是维护一个fd列表,通过顺序扫描的方式来找出就绪的fd并作对应的IO操作。select缺陷是fd列表大小有限制,取决于内核的FD_SETSIZE,默认是1024;而且在fd列表很大的情况下,顺序扫描的性能也堪忧。
epoll:复用的原理跟select/poll类似,但采用了事件驱动的方式,当fd就绪,则直接调用对应的回调。通过这种方式,来取代顺序扫描下,fd列表大小的增加导致性能直线下降;并且epoll的fd数目是操作系统的最大文件描述符数,而不是FD_SETSIZE。
信号驱动IO
开启socket信号驱动IO功能,通过系统调用sigaction注册信号处理函数,当IO就绪了,通过信号触发信号处理,通知应用程序进行IO操作。
异步IO
跟信号驱动IO类似,不同之处:异步IO是IO操作完成(数据拷贝到应用数据区)之后,通知应用程序;而信号驱动IO还需要应用程序去进行IO操作。