找回密码
 立即注册
Qt开源社区 门户 查看内容

Linux 网络I/O模型

2019-7-6 19:57| 发布者: admin| 查看: 665| 评论: 0

摘要: 在开始讲网络IO模式之前,我们先来熟悉一下几个概念:用户空间和内核空间进程切换文件描述符buffer IO用户空间和内核空间在Linux中不管是内核空间还是用户空间都是使用虚拟地址,在32位平台而言,它的寻址范围是4GB ...
在开始讲网络IO模式之前,我们先来熟悉一下几个概念:

  • 用户空间和内核空间

  • 进程切换

  • 文件描述符

  • buffer IO


用户空间和内核空间


在Linux中不管是内核空间还是用户空间都是使用虚拟地址,在32位平台而言,它的寻址范围是4GB,如下图所示:



所以从较低3GB(0x00000000到0xBFFFFFFF)称之为用户空间,较高1GB(0xC0000000到0xFFFFFFFF)称之为内核空间。在我的《Linux内存管理》文章有更加详细的说明,可自行前往参考。

进程切换


在Linux中存在多线程的情况下,肯定需要进程的切换,也就是说需要挂起正在运行的进程,然后运行正在就绪状态的进程。这种行为我们称之为进程切换。进程切换有哪些变化呢?


  1. 首先需要保存寄存器现场,保存处理机上下文

  2. 更新PCB信息

  3. 把进程的PCB加入到某些队列中(例如就绪队列)

  4. 选择需要运行的进程,并且执行,然后更新其PCB

  5. 更新内存管理的结构

  6. 恢复处理机上下文

虽然从这六个步骤中,无法感觉,但是真的挺繁琐的,非常消耗CPU资源。

文件描述符


文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。

文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。

buffer IO


buffer IO也就是标准IO,是默认的IO模式。在用户态write或者read等IO操作时,内核中数据是存储在文件系统的页缓存中。它有个缺点:许多多次拷贝的消耗,带来CPU和内存的消耗。在本人之前的《Linux直接IO原理》中有详细比较两种IO的优缺点。

IO模型


先直接给出五种网络模型:

  1. 阻塞I/O

  2. 非阻塞I/O

  3. I/O多路复用

  4. 信号驱动I/O

  5. 异步I/O


在Linux中,socket的I/O默认都是阻塞的,流程图,如下:



当应用程序调用recvfrom系统调用,内核进入第一个阶段:等待数据。所以在用户态这边整个进程都会阻塞。当内核准备数据之后,还需要将数据拷贝到用户态内存,然后才会return,之后用户进程才会接触阻塞状态。

非阻塞I/O

设置I/O属性可以修改socket为非阻塞模式,当进行读操作时候,流程图如下图:



当用户进程read操作时候,如果内核还没数据,就会返回无数据状态。在用户进程的角度上,如果是无数据,继续recvfrom等待。直到内核数据准备好,拷贝到用户空间内存中。所以非阻塞I/O需要用户进程一直轮询IO。

I/O多路复用(I/O multiplexing)

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



从图中我们可以看出,当调用select,整个进程都会阻塞,select可以同时监听多个socket,一旦其中任意一个socket准备就绪状态,select就会立即返回,用户进程就可以read操作了。

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

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

在IO multiplexing 模型中,实际中,对于所有的socket,一般都设置成为非阻塞的。但是,如上图所示,整个用户进程其实是一直被阻塞的。只不过进程是被select这个函数阻塞的,而不是被socket IO给阻塞。

异步I/O

Linux中异步I/O其实用的很少。下图流程:



用户进程发起read操作之后,这个时候就可以去干其他事情了。而从kernel的角度,当它受到一个异步 read之后,首先它会立刻返回,所以不会对用户进程产生任何阻塞。然后,内核会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,内核会给用户进程发送一个信号,告诉它read操作完成了。

总结


看下图各I/O模型比较图:



从图中我们可以发现,非阻塞和异步的区别还是很明显的,在非阻塞中进程大部分不会阻塞的,但是需要主动check I/O。而异步I/O就不需要,内核将数据拷贝完成后,发送信号通知进程就行了。

觉得不错,记得关注,转发,点赞!


----------------------------------------------------------------------------------------------------------------------
我们尊重原创,也注重分享,文章来源于微信公众号:技术原理君,建议关注公众号查看原文。如若侵权请联系qter@qter.org。
----------------------------------------------------------------------------------------------------------------------

鲜花

握手

雷人

路过

鸡蛋

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了