Linux 网络I/O模型

举报
王建峰 发表于 2021/11/19 02:15:29 2021/11/19
【摘要】 转载于:技术原理君 原文链接:https://mp.weixin.qq.com/s/Zej9ZDHsxIS0Ns7XxH3WQg     在开始讲网络IO模式之前,我们先来熟悉一下几个概念: 用户空间和内核空间 进程切换 文件描述符 buffer IO   用户空间和内核...

转载于:技术原理君

原文链接:https://mp.weixin.qq.com/s/Zej9ZDHsxIS0Ns7XxH3WQg

 

 

在开始讲网络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就不需要,内核将数据拷贝完成后,发送信号通知进程就行了。

 

文章来源: blog.csdn.net,作者:hinzer,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/feit2417/article/details/95598663

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。