Chapter1 Java I/O演进之路

1.1.1Linux网络I/O模型

Linux 内核将所有外部设备看做一个文件操作,对一个文件的读写操作会返回一个file descrptor(fd,文件描述符)。而对一个socket的读写也会有描述符scoketfd,描述符就是一个数字,指向内核中的一个结构体(文件路径、数据区等一些属性)。根据UNIX网络编程对I/O模型的分类,UNIX提供了5种I/O模型 :
( 1 ) 同步阻塞I/O模型 : 最常用的I/O模型就是阻塞I/O模型,默认情况下所有文件操作都是阻塞的。以套接字接口为例:在用户进程空间中执行系统调用recvfrom,其系统调用直到数据包到达且从内核缓冲区复制到用户进程或发生错误才返回,此过程中调用的线程处于阻塞的等待响应不消费cpu,也不处理其他网络I/O。在调用recv()/recvfrom()函数时,等待内核中数据准备和复制到用户空间的大致流程如下 :


同步阻塞IO.png

( 2 )同步非阻塞I/O模型 :recvfrom从应用层到内核的时候,如果该缓冲区没有数据的话,就直接返回一个EWOULDBLOCK错误,不进入阻塞状态,线程可以处理其他业务此时,流程如下图 :


同步非阻塞IO.png

( 3 )多路复用IO模型 :Linux提供select/poll/epoll,进程通过 select/poll/epoll 是顺序扫描fd是否就绪的,而不是轮询使用recvfrom去查询数据是否准备好。select轮询相对非阻塞的轮询的区别在于---select可以等待多个socket,能实现同时对多个IO端口进行监听,当其中任何一个socket的数据准好了,就能返回进行可读,然后进程再进行recvform系统调用。I/O复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时(注意不是全部数据可读或可写),才真正调用I/O操作函数。对于多路复用,也就是轮询多个socket。多路复用既然可以处理多个IO,也就带来了新的问题,多个IO之间的顺序变得不确定了,当然也可以针对不同的编号。具体流程,如下图所示:
图片.png

( 4 )信号驱动I/O模型 : 开启套接字Socket信号驱动功能,通过系统调用sigaction执行一个信号处理函数,此调用非阻塞立即返回,进程继续执行其他工作。当数据准备好时,进程会收到一个SIGIO信号回调通知,然后进程再调用recvfrom读取并处理数据,过程如下图所示:
信号驱动I/O模型 .png

( 5 )异步非阻塞I/O模型 :相对于同步IO,异步IO不是顺序执行。用户进程进行aio_read系统调用之后,无论内核数据是否准备好,都会直接返回给用户进程,然后用户态进程可以去做别的事情。等到socket数据准备好了,内核直接复制数据给进程,然后从内核向进程发送通知。IO两个阶段,进程都是非阻塞的,流程如下 :


异步非阻塞I/O模型.png

五种模型的不同比较 :
五种模型的比较.png

你可能感兴趣的:(Chapter1 Java I/O演进之路)