Netty学习笔记 - Unix的五种网络通信模型

文件描述符

Linux将所有外部设备都看作一个文件,对文件的操作会产生文件描述符(fd,file descriptor)。对socket的读写描述符称为socket描述符(socketfd)
描述符是一个数字,指向内存中的一个结构体。

Unix的五种网络通信模型

阻塞I/O

默认情况下,所有文件操作都是阻塞I/O。在进程空间执行读取数据的recvfrom,系统调用会等到数据包达到并复制到用户缓冲区或发生错误才返回,在此期间一直等待。因此被称为阻塞I/O。
下图摘自《Netty权威指南》
Netty学习笔记 - Unix的五种网络通信模型

非阻塞I/O

执行recvfrom,进入内核层的时候,如果缓冲区没有数据可读,会返回EWOULDBLOCK错误而不是阻塞等待。一般非阻塞I/O会轮询检查这个状态,看内核中是否有数据可读。
下图摘自《Netty权威指南》
Netty学习笔记 - Unix的五种网络通信模型

I/O复用模型

Linux提供了select/poll系统调用,进程可以将一个或多个fd传递给select/poll,然后阻塞在select操作上。select/poll系统调用会自动检测fd是否处于就绪状态。当有fd就绪时,将执行回调函数callback。在回调中可以执行recvfrom系统调用读取数据。
Linux还提供了epoll系统调用,性能更好。
几种复用的系统调用区别在后面。
下图摘自《Netty权威指南》
Netty学习笔记 - Unix的五种网络通信模型

信号驱动I/O模型

首先要开启套接口信号处理功能。然后通过sigaction注册一个信号处理函数,注册完成立即返回,不阻塞进程。当数据就绪时,会向该进程发送一个SIGIO信号,并通过信号回调函数执行recvfrom读取数据。然后通知主循环函数处理数据。
下图摘自《Netty权威指南》
Netty学习笔记 - Unix的五种网络通信模型

异步I/O

通过sio_read通知内核读取数据,内核在将数据复制到用户缓冲区后通知进程执行回调。与信号驱动的区别是,信号驱动I/O是内核在数据就绪时通知进程,进程仍要在回调中读取数据。而异步I/O则是内核在将数据读取并拷贝到用户缓冲区后通知进程,此时I/O操作已经完成。
下图摘自《Netty权威指南》
Netty学习笔记 - Unix的五种网络通信模型

I/O多路复用

I/O多路复用是在处理多个客户端连接请求时,将多个客户端连接的I/O阻塞到一个系统调用上,实现用单线程处理多个阻塞的客户端连接的能力。
由于使用单线程,系统不需要维护更多的进程和线程,开销更小。
常用的支持多路复用的系统调用用select\pselect\poll\epoll。

select\poll

select\poll通过轮询fd的方式检测是否有数据可读。select是轮询遍历全部fd,不论有几个fd是可读状态,都会遍历全部。而且select支持的fd个数有上线,是1024个,除非重新编译内核,否则无法修改。poll支持的fd个数没有上线,但也是轮询遍历全部fd。

epoll

epoll是对于select\poll的优化,在Linux2.6内核提出,基于事件驱动I/O的方式实现。当socket就绪时,将fd放入内核的事件表,只处理就绪的fd,因此epoll不需遍历全部fd。
epoll不设置接受的fd上限,仅受操作系统的最大文件句柄数影响。在1G的机器上大约10W,可通过“cat /proc/sys/fs/file-max”查看。
epoll不像select\poll一样遍历全部fd。epoll在内核实现中是基于fd的回调函数实现的,这样只有socket就绪(有新连接、可读、可写)时,才会触发回调函数放入内核事件队列。因此也可以认为epoll实现了一个伪AIO。如果fd均处于活跃状态,epoll与select\poll的效率相差不大,反而因为有回调函数放入事件队列的操作,可能表现低于poll。