专栏名称: 京东成都研究院
京东商城成都研究院信息平台
目录
相关文章推荐
51好读  ›  专栏  ›  京东成都研究院

CDRD TAIL|从Java NIO 谈谈计算的IO

京东成都研究院  · 公众号  · 成都  · 2017-10-09 17:45

正文

本文你将看到如下内容:

  1. 最好的学习就是教别人。

  2. NIO 的用途。

  3. 现代操作系统的 5 IO 模型。

  4. JavaNIO 的实现及简单的服务器例子。

  5. NodeJs IO 模型。

  6. 计算机 IO 的硬件基础。

  • 最好的学习就是教别人:

首先感谢 CDRD 给了我这一次分享的机会,为什么要将感谢放在最开头。因为这次我选择 java nio 这个主题是因为我自己不理解 Java nio 的内部实现原理,但是又想学。 最好的学习方式就是教别人 ,所以建议大家以后想要学什么东西,可以尝试搞清楚以后,与大家分享。

  • 带着功利心开始我们的学习:谈谈 NIO 的用途


当今时代是一个知识信息爆炸的时代,知识的量几乎是无限的,如果我们不带着功利心去学习知识,那么提升认知的效率将大幅度降低。那么学习 Java NIO 能有什么用呢?

答案是:帮助我们提高 IO 密集型应用的单机并发量。

何为 IO 密集型应用 ? 何为 CPU 密集型应用,这个问题留给大家一起思考。在我们所用到的,或者知道的应用中,有哪些是 IO 密集型的,又有哪些是 CPU 密集型的 ?

为什么 Java NIO 能够提升 IO 密集型应用的单击并发量呢?这要从操作系统的 IO 模型说起。

  • 现代操作系统的IO模型:

对于一次 IO 访问(以 read 举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个 read 操作发生时,它会经历两个阶段:
1.
等待数据准备 (Waiting for the data to be ready)
2.
将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)

正式因为这两个阶段, linux 系统产生了下面五种网络模式的方案。
-
阻塞 I/O blocking IO
-
非阻塞 I/O nonblocking IO
- I/O
多路复用( IO multiplexing
-
信号驱动 I/O signal driven IO
-
异步 I/O asynchronous IO

1 、阻塞 I/O blocking IO

linux 中,默认情况下所有的 socket 都是 blocking ,一个典型的读操作流程大概是这样:

当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据(对于网络IO来说,很多时候数据在一开始还没有到达。比如,还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来)。这个过程需要等待,也就是说数据被拷贝到操作系统内核的缓冲区中是需要一个过程的。而在用户进程这边,整个进程会被阻塞(当然,是进程自己选择的阻塞)。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。

所以,blocking IO的特点就是在IO执行的两个阶段都被block了。

2 、非阻塞 I/O nonblocking IO

linux 下,可以通过设置 socket 使其变为 non-blocking 。当对一个 non-blocking socket 执行读操作时,流程是这个样子:

当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的systemcall,那么它马上就将数据拷贝到了用户内存,然后返回。

所以,nonblocking IO的特点是用户进程需要不断的主动询问kernel数据好了没有。

3 I/O 多路复用( IO multiplexing

IO multiplexing 就是我们说的 select poll epoll ,有些地方也称这种 IO 方式为 event driven IO select/epoll 的好处就在于单个 process 就可以同时处理多个网络连接的 IO 。它的基本原理就是 select poll epoll 这个 function 会不断的轮询所负责的所有 socket ,当某个 socket 有数据到达了,就通知用户进程。

当用户进程调用了 select ,那么整个进程会被 block ,而同时, kernel 监视 所有 select 负责的 socket ,当任何一个 socket 中的数据准备好了, select 就会返回。这个时候用户进程再调用 read 操作,将数据从 kernel 拷贝到用户进程。

所以, I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态, select() 函数就可以返回。

这个图和 blocking IO 的图其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个 systemcall (select recvfrom) ,而 blocking IO 只调用了一个 systemcall (recvfrom) 。但是,用 select 的优势在于它可以同时处理多个 connection

所以,如果处理的连接数不是很高的话,使用 select/epoll web server 不一定比使用 multi-threading+ blocking IO web server 性能更好,可能延迟还更大。 select/epoll 的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)

IO multiplexing Model 中,实际中,对于每一个 socket ,一般都设置成为 non-blocking ,但是,如上图所示,整个用户的 process 其实是一直被 block 的。只不过 process 是被 select 这个函数 block ,而不是被 socket IO







请到「今天看啥」查看全文