1、介绍
Linux 的内核将所有外部设备都看做一个文件来操作(一切皆文件),对一个文件的读写操作会调用内核提供的系统命令,返回一个file descriptor(fd,文件描述符)。而对一个socket的读写也会有响应的描述符,称为socket fd(socket文件描述符),描述符就是一个数字,指向内核中的一个结构体(文件路径,数据区等一些属性)。
根据UNIX网络编程对I/O模型的分类,UNIX提供了5种I/O模型。
1.1、阻塞I/O模型
最常用的I/O模型,默认情况下,所有文件操作都是阻塞的。
比如I/O模型下的套接字接口:在进程空间中调用recvfrom,其系统调用直到数据包到达且被复制到应用进程的缓冲区中或者发生错误时才返回,在此期间一直等待。
进程在调用recvfrom开始到它返回的整段时间内都是被阻塞的,所以叫阻塞I/O模型。
图示:
1.2、非阻塞I/O模型
recvfrom从应用层到内核的时候,就直接返回一个EWOULDBLOCK错误,一般都对非阻塞I/O模型进行轮询检查这个状态,看内核是不是有数据到来。
图示:
1.3、I/O复用模型
Linux提供select/poll,进程通过将一个或多个fd传递给select或poll系统调用,阻塞在select操作上,这样,select/poll可以帮我们侦测多个fd是否处于就绪状态。
select/poll是顺序扫描fd是否就绪,而且支持的fd数量有限,因此它的使用受到了一些制约。
Linux还提供一个epoll系统调用,epoll使用基于事件驱动方式代替顺序扫描,因此性能更高。当有fd就绪时,立即回调函数rollback。
图示:
1.4、信号驱动I/O模型
首先开启套接口信号驱动I/O功能,并通过系统调用sigaction执行一个信号处理函数(此系统调用立即返回,进程继续工作,非阻塞)。当数据准备就绪时,就为改进程生成一个SIGIO信号,通过信号回调通知应用程序调用recvfrom来读取数据,并通知主循环函数处理树立。
图示:
1.5、异步I/O
告知内核启动某个操作,并让内核在整个操作完成后(包括数据的复制)通知进程。
信号驱动I/O模型通知的是何时可以开始一个I/O操作,异步I/O模型有内核通知I/O操作何时已经完成。
图示:
2、I/O多路复用技术
I/O编程中,需要处理多个客户端接入请求时,可以利用多线程或者I/O多路复用技术进行处理。
正如前面的简介,I/O多路复用技术通过把多个I/O的阻塞复用到同一个select的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求。
与传统的多线程模型相比,I/O多路复用的最大优势就是系统开销小,系统不需要创建新的额外线程,也不需要维护这些线程的运行,降低了系统的维护工作量,节省了系统资源。
主要的应用场景:
- 服务器需要同时处理多个处于监听状态或多个连接状态的套接字。
- 服务器需要同时处理多种网络协议的套接字。
支持I/O多路复用的系统调用主要有select、pselect、poll、epoll。
而当前推荐使用的是epoll,优势如下:
- 支持一个进程打开的socket fd不受限制。
- I/O效率不会随着fd数目的增加而线性下将。
- 使用mmap加速内核与用户空间的消息传递。
- epoll拥有更加简单的API。
3、Java中的网络IO编程
如果只是做Java开发,以上内容只需了解即可,不必深究(随便说说而已)。
已专门出了文章介绍:Java 网络IO编程总结(BIO、NIO、AIO均含完整实例代码)