0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

深入理解Linux传统的System Call I/O

GReq_mcu168 来源:CSDN博客 作者:范桂飓 2021-11-19 09:52 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

传统的 System Call I/O

Linux 系统中,传统的访问方式是通过 write() 和 read() 两个系统调用实现的,通过 read() 函数读取文件到到缓存区中,然后通过 write() 方法把缓存中的数据输出到网络端口

read(file_fd, tmp_buf, len);write(socket_fd, tmp_buf, len);

下图分别对应传统 I/O 操作的数据读写流程,整个过程涉及 2 次 CPU 拷贝、2 次 DMA 拷贝,总共 4 次拷贝,以及 4 次上下文切换。

CPU 拷贝:

由 CPU 直接处理数据的传送,数据拷贝时会一直占用 CPU 的资源。

DMA 拷贝:

由 CPU 向DMA磁盘控制器下达指令,让 DMA 控制器来处理数据的传送,数据传送完毕再把信息反馈给 CPU,从而减轻了 CPU 资源的占有率。

上下文切换:

当用户程序向内核发起系统调用时,CPU 将用户进程从用户态切换到内核态;

当系统调用返回时,CPU 将用户进程从内核态切换回用户态。

读操作

当应用程序执行 read 系统调用读取一块数据的时候,如果这块数据已经存在于用户进程的页内存中,就直接从内存中读取数据。

如果数据不存在,则先将数据从磁盘加载数据到内核空间的读缓存(Read Buffer)中,再从读缓存拷贝到用户进程的页内存中。

read(file_fd, tmp_buf, len);

基于传统的 I/O 读取方式,read 系统调用会触发 2 次上下文切换,1 次 DMA 拷贝和 1 次 CPU 拷贝。

发起数据读取的流程如下:

用户进程通过 read() 函数向 Kernel 发起 System Call,上下文从 user space 切换为 kernel space。

CPU 利用 DMA 控制器将数据从主存或硬盘拷贝到 kernel space 的读缓冲区(Read Buffer)。

CPU 将读缓冲区(Read Buffer)中的数据拷贝到 user space 的用户缓冲区(User Buffer)。

上下文从 kernel space 切换回用户态(User Space),read 调用执行返回。

写操作

当应用程序准备好数据,执行 write 系统调用发送网络数据时,先将数据从用户空间的页缓存拷贝到内核空间的网络缓冲区(Socket Buffer)中,然后再将写缓存中的数据拷贝到网卡设备完成数据发送。

write(socket_fd, tmp_buf, len);

基于传统的 I/O 写入方式,write() 系统调用会触发 2 次上下文切换,1 次 CPU 拷贝和 1 次 DMA 拷贝。

用户程序发送网络数据的流程如下:

用户进程通过 write() 函数向 kernel 发起 System Call,上下文从 user space 切换为 kernel space。

CPU 将用户缓冲区(User Buffer)中的数据拷贝到 kernel space 的网络缓冲区(Socket Buffer)。

CPU 利用 DMA 控制器将数据从网络缓冲区(Socket Buffer)拷贝到 NIC 进行数据传输。

上下文从 kernel space 切换回 user space,write 系统调用执行返回。

网络 I/O

磁盘 I/O

高性能优化的 I/O

零拷贝技术。

多路复用技术。

页缓存(PageCache)技术。

其中,页缓存(PageCache)是操作系统对文件的缓存,用来减少对磁盘的 I/O 操作,以页为单位的,内容就是磁盘上的物理块,页缓存能帮助程序对文件进行顺序读写的速度几乎接近于内存的读写速度,主要原因就是由于 OS 使用 PageCache 机制对读写访问操作进行了性能优化。

页缓存读取策略:当进程发起一个读操作 (比如,进程发起一个 read() 系统调用),它首先会检查需要的数据是否在页缓存中:

如果在,则放弃访问磁盘,而直接从页缓存中读取。

如果不在,则内核调度块 I/O 操作从磁盘去读取数据,并读入紧随其后的少数几个页面(不少于一个页面,通常是三个页面),然后将数据放入页缓存中。

页缓存写策略:当进程发起 write 系统调用写数据到文件中,先写到页缓存,然后方法返回。此时数据还没有真正的保存到文件中去,Linux 仅仅将页缓存中的这一页数据标记为 “脏”,并且被加入到脏页链表中。

然后,由 flusher 回写线程周期性将脏页链表中的页写到磁盘,让磁盘中的数据和内存中保持一致,最后清理“脏”标识。在以下三种情况下,脏页会被写回磁盘:

空闲内存低于一个特定阈值。

脏页在内存中驻留超过一个特定的阈值时。

当用户进程调用 sync() 和 fsync() 系统调用时。

存储设备的 I/O 栈

从系统调用的接口再往下,Linux 下的 IO 栈致大致有三个层次:

文件系统层,以 write 为例,内核拷贝了 write 参数指定的用户态数据到文件系统 Cache 中,并适时向下层同步。

块层,管理块设备的 IO 队列,对 IO 请求进行合并、排序(还记得操作系统课程学习过的 IO 调度算法吗?

)。

设备层,通过 DMA 与内存直接交互,完成数据和具体设备之间的交互。

结合这个图,想想 Linux 系统编程里用到的 Buffered IO、mmap、Direct IO,这些机制怎么和 Linux I/O 栈联系起来呢?上面的图有点复杂,我画一幅简图,把这些机制所在的位置添加进去:

f0bcdbac-43ac-11ec-b939-dac502259ad0.png

Linux IO系统

这下一目了然了吧?传统的 Buffered IO 使用 read 读取文件的过程什么样的?假设要去读一个冷文件(Cache 中不存在),open 打开文件内核后建立了一系列的数据结构,接下来调用 read,到达文件系统这一层,发现 Page Cache 中不存在该位置的磁盘映射,然后创建相应的 Page Cache 并和相关的扇区关联。然后请求继续到达块设备层,在 IO 队列里排队,接受一系列的调度后到达设备驱动层,此时一般使用 DMA 方式读取相应的磁盘扇区到 Cache 中,然后 read 拷贝数据到用户提供的用户态 buffer 中去(read 的参数指出的)。

整个过程有几次拷贝?从磁盘到 Page Cache 算第一次的话,从 Page Cache 到用户态 buffer 就是第二次了。而 mmap 做了什么?mmap 直接把 Page Cache 映射到了用户态的地址空间里了,所以 mmap 的方式读文件是没有第二次拷贝过程的。

那 Direct IO 做了什么?这个机制更狠,直接让用户态和块 IO 层对接,直接放弃 Page Cache,从磁盘直接和用户态拷贝数据。好处是什么?写操作直接映射进程的buffer到磁盘扇区,以 DMA 的方式传输数据,减少了原本需要到 Page Cache 层的一次拷贝,提升了写的效率。对于读而言,第一次肯定也是快于传统的方式的,但是之后的读就不如传统方式了(当然也可以在用户态自己做 Cache,有些商用数据库就是这么做的)。

除了传统的 Buffered IO 可以比较自由的用偏移+长度的方式读写文件之外,mmap 和 Direct IO 均有数据按页对齐的要求,Direct IO 还限制读写必须是底层存储设备块大小的整数倍(甚至 Linux 2.4 还要求是文件系统逻辑块的整数倍)。所以接口越来越底层,换来表面上的效率提升的背后,需要在应用程序这一层做更多的事情。所以想用好这些高级特性,除了深刻理解其背后的机制之外,也要在系统设计上下一番功夫。

I/O Buffering

f121afa0-43ac-11ec-b939-dac502259ad0.png

如图,当程序调用各类文件操作函数后,用户数据(User Data)到达磁盘(Disk)的流程如图所示。

图中描述了 Linux 下文件操作函数的层级关系和内存缓存层的存在位置。中间的黑色实线是用户态和内核态的分界线。

从上往下分析这张图:

1. 首先是 C 语言 stdio 库定义的相关文件操作函数,这些都是用户态实现的跨平台封装函数。stdio 中实现的文件操作函数有自己的 stdio buffer,这是在用户态实现的缓存。此处使用缓存的原因很简单 — 系统调用总是昂贵的。如果用户代码以较小的 size 不断的读或写文件的话,stdio 库将多次的读或者写操作通过 buffer 进行聚合是可以提高程序运行效率的。stdio 库同时也支持 fflush 函数来主动的刷新 buffer,主动的调用底层的系统调用立即更新 buffer 里的数据。特别地,setbuf 函数可以对 stdio 库的用户态 buffer 进行设置,甚至取消 buffer 的使用。

2. 系统调用的 read/write 和真实的磁盘读写之间也存在一层 buffer,这里用术语 Kernel buffer cache 来指代这一层缓存。在 Linux 下,文件的缓存习惯性的称之为 Page Cache,而更低一级的设备的缓存称之为 Buffer Cache。这两个概念很容易混淆,这里简单的介绍下概念上的区别:Page Cache 用于缓存文件的内容,和文件系统比较相关。文件的内容需要映射到实际的物理磁盘,这种映射关系由文件系统来完成;Buffer Cache 用于缓存存储设备块(比如磁盘扇区)的数据,而不关心是否有文件系统的存在(文件系统的元数据缓存在 Buffer Cache 中)。

作者:范桂飓原文:https://is-cloud.blog.csdn.net/article/details/105897963

责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • cpu
    cpu
    +关注

    关注

    68

    文章

    11216

    浏览量

    222913
  • Linux
    +关注

    关注

    88

    文章

    11628

    浏览量

    217948
  • 网络
    +关注

    关注

    14

    文章

    8130

    浏览量

    93080

原文标题:深入理解 Linux的 I/O 系统

文章出处:【微信号:mcu168,微信公众号:硬件攻城狮】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    飞凌嵌入式ElfBoard-文件I/O深入学习之存储映射I/O

    存储映射I/O(memory-mapped I/O)是一种基于内存区域的高级I/O操作,它能将一
    发表于 12-06 16:39

    飞凌嵌入式ElfBoard-文件I/O深入学习之异步I/O

    I/O多路复用中,进程通过系统调用select或poll来主动查询文件描述符上是否可以执行I/O操作。而在异步I/
    发表于 12-05 11:23

    飞凌嵌入式ElfBoard-文件I/O深入学习之I/O多路复用

    I/O多路复用(IO multiplexing)通过一种机制,可以监视多个文件描述符,一旦某个文件描述符(也就是某个文件)可以执行I/O操作时,能够通知应用程序进行相应的读写操作。
    发表于 12-05 11:18

    飞凌嵌入式ElfBoard-文件I/O深入学习之阻塞I/O与非阻塞I/O

    1.4.1.1 概念 阻塞I/O顾名思义就是对文件的I/O操作是阻塞式的,即假如对某些类型文件(管道文件、网络设备文件和字符设备文件)进行读操作时,如果数据未准备好、文件当前无数据可读
    发表于 12-01 13:07

    浅谈光学I/O模块的热挑战

    服务器和机架式网络基础设施系统内的光学 I/O 模块通常接受主动冷却系统的直接冷却,特别是来自机架式设备前面板的强制风冷。机架式设备的散热设计需要平衡 I/O 模块的热管理与处理器或
    的头像 发表于 11-03 09:32 436次阅读
    浅谈光学<b class='flag-5'>I</b>/<b class='flag-5'>O</b>模块的热挑战

    深入剖析I2C协议

    I2C是由Philips开发的简单的双向两线总线,在深入浅出理解SPI协议中,我们区分了单工,半双工,全双工协议数据流向的区别,根据特征,I2C协议属于半双工协议(即同一时刻,数据单向
    的头像 发表于 08-21 15:10 3271次阅读
    <b class='flag-5'>深入</b>剖析<b class='flag-5'>I</b>2C协议

    TCAL9539 I2C总线I/O扩展器技术解析与应用指南

    Texas Instrument TCAL9539/TCAL9539-Q1 I^2^C总线/SMBus I/O扩展器为双线双向I^2^C总线(或SMBus)协议提供通用并行输入/输出
    的头像 发表于 08-08 11:49 928次阅读
    TCAL9539 <b class='flag-5'>I</b>2C总线<b class='flag-5'>I</b>/<b class='flag-5'>O</b>扩展器技术解析与应用指南

    Linux系统环境监测终极指南

    Linux系统环境主要监测CPU、内存、磁盘I/O和网络流量。
    的头像 发表于 06-25 14:41 543次阅读
    <b class='flag-5'>Linux</b>系统环境监测终极指南

    MAX7325 I²C端口扩展器,提供8路推挽式I/O和8个漏极开路I/O技术手册

    MAX7325 2线串行接口外设具有16路I/O端口。其中8路为推挽输出,另外8路为I/O端口,带有可选择的内部上拉和瞬态检测功能。8路I/
    的头像 发表于 05-22 15:27 679次阅读
    MAX7325 <b class='flag-5'>I</b>²C端口扩展器,提供8路推挽式<b class='flag-5'>I</b>/<b class='flag-5'>O</b>和8个漏极开路<b class='flag-5'>I</b>/<b class='flag-5'>O</b>技术手册

    Linux系统管理的核心概念

    的管理、权限控制信息的查看,以及chmod和chown命令的使用。这些知识对于任何希望深入理解Linux系统管理的用户来说都是至关重要的。
    的头像 发表于 05-15 17:05 491次阅读

    I/O接口与I/O端口的区别

    在计算机系统中,I/O接口与I/O端口是实现CPU与外部设备数据交换的关键组件,它们在功能、结构、作用及运作机制上均存在显著差异,却又相互协同工作,共同构建起CPU与外部设备之间的桥梁
    的头像 发表于 02-02 16:00 2805次阅读

    单片机I/O接口的传输方式

    着数据传输的任务,还影响着整个系统的性能和可靠性。本文将深入探讨单片机I/O接口的传输方式,包括无条件传送、查询传送、中断传送和DMA传送等,以期为单片机应用开发者提供有价值的参考。
    的头像 发表于 02-02 15:56 1637次阅读

    深入探讨Linux系统中的动态链接库机制

    异常或崩溃。为深入理解动态链接机制及其工作原理,我重温了《程序员的自我修养》,并通过实践演示与反汇编分析,了解了动态链接的过程。 本文将深入探讨Linux系统中的动态链接库机制,这其中包括但不限于全局符号介入(Global Sy
    的头像 发表于 12-18 10:06 932次阅读
    <b class='flag-5'>深入</b>探讨<b class='flag-5'>Linux</b>系统中的动态链接库机制

    Linux系统监控报I/O问题怎么办

    10次。 2、用法展示   # iostat -x 1 10Linux 2.6.18-92.el5xen 02/03/2009avg-cpu: %user %nice %system %iowait %steal %idle 1.10 0.00 4.82 39.54 0.
    的头像 发表于 12-18 09:07 967次阅读

    深入理解C语言:循环语句的应用与优化技巧

    能让你的代码更加简洁明了,还能显著提升程序执行效率。本文将详细介绍C语言中的三种常见循环结构——while循环、for循环和do...while循环,带你深入理解
    的头像 发表于 12-07 01:11 1063次阅读
    <b class='flag-5'>深入理解</b>C语言:循环语句的应用与优化技巧