1 Linux 网络IO模型
读取网络数据过程
/** 获取SocketChannel**/
SocketChannel socketChannel = serverSocketChannel.accept();
/** 读取客户端发送数据 **/
ByteBuffer byteBuffer = ByteBuffer.allocate(100);
socketChannel.read(byteBuffer);
System.out.println(new String(byteBuffer.array()));
image
-
1、应用程序发起读数据操作,JVM会发起read()系统调用。
-
2、这时操作系统OS会进行一次上下文切换(把用户空间切换到内核空间)
-
3、通过磁盘控制器把数据copy到内核缓冲区中,这里的就发生了一次DMA Copy
-
4、然后内核将数据copy到用户空间的应用缓冲区中,发生了一次CPU Copy
-
5、read调用返回后,会再进行一次上下文切换(把内核空间切换到用户空间)
对于一次IO访问,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的缓冲区,最后交给进程。所以说,当一个read操作发生时,它会经历两个阶段:
- 1. 等待数据准备(从网络设备拷贝到操作系统缓冲区)
- 2. 将数据从内核拷贝到进程中(从操作系统缓冲区拷贝到用户进程缓冲区)
正式因为这两个阶段,linux系统产生了下面五种网络模式的方案: 阻塞 I/O(blocking IO),非阻塞 I/O(nonblocking IO), I/O 多路复用( IO multiplexing), 信号驱动 I/O( signal driven IO),异步 I/O(asynchronous IO)。
阻塞IO
同步阻塞IO通常表示当用户进程(java进程)底层调用操作系统recv()/recvfrom()函数接收数据。用户进程会在内核空间准备数据,内核空间拷贝到用户空间这两个过程都是阻塞的。
image流程
-
用户进程调用read,发起system call,用户进程阻塞。
-
开始第一阶段,系统kernel准备数据(从网络设备拷贝到内核缓冲区)。
-
开始第二阶段,将数据从内核拷贝到了用户空间。
-
进程解除阻塞。
非阻塞I/O模型
非阻塞I/O模型通常表示当用户进程(java进程)底层调用操作系统recv()/recvfrom()函数接收数据。内核空间数据没有准备好时会返回一个错误表示不接收处理,应用进程需要不断的询问直到内核空间数据准备好后接收处理,将内核空间数据拷贝到用户空间中返回成功
image流程
-
用户进程调用read,发起system call,如果操作系统kernel数据准备还没有准备好,操作系统kernel并不会block用户进程,而是立刻返回一个error。
-
用户进程获取read操作结果进行判断,如果结果是一个error时,它就知道内核空间数据还没有准备好,可以等待一段时间后继续发出read操作
-
当操作系统kernel数据准备,并且又再次收到了用户进程的system call;。
-
将内核空间数据拷贝到了用户空间内存,然后返回。
I/O多路复模型
I/O多路复用实际上就是用select, poll, epoll监听多个io对象在内核缓存数据的准备过程,当io对象有变化(有数据)的时候就通知对应IO的用户进程。
image流程
-
用户进程调用select,发起system call,同时整个进程会被block,而同时,操作系统kernel上注册了一个监听事件.监听系统kernel数据是否准备好。
-
当系统kernel准备数据,select就会返回;
-
这个时候用户进程再调用read操作,将数据从内核空间数据中拷贝到用户空间并返回
信号驱动IO
image-
用户进程调用read,发起system call,系统kernel准备数据,操作系统kernel并不会block用户进程。
-
系统kernel准备数据,就会产生一个信号通道用户进程
-
用户进程接收信号,将数据从内核空间数据中拷贝到用户空间。
异步 I/O
image(1)用户进程发起read操作之后,立刻就可以开始去做其它的事。
(2)而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。
(3)然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。
2 同步、异步、阻塞、非阻塞
同步与异步:描述的是用户线程与内核的交互方式,同步指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行;而异步是指用户线程发起IO请求后仍然继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数。
阻塞与非阻塞:描述是用户线程调用内核IO操作的方式,阻塞是指IO操作需要彻底完成后才返回到用户空间;而非阻塞是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。
网友评论