Linux探秘之I/O效率

Linux系统调用深度解析:从strace实战到文件I/O性能优化 系统调用是用户程序与操作系统内核交互的核心接口,它构成了应用程序访问硬件资源的基础桥梁。理解系统调用的工作原理,对于掌握操作系统运行机制、进行程序调试和性能优化至关重要。通过strace等工具跟踪分析系统调用,可以直观地观察数据在用户态与内核态之间的流动路径,包括权限验证、缓冲区管理和上下文切换等底层细节。这种技术手段在排查文件访问失败、分析I/O性能瓶颈、优化资源使用效率等工程实践中具有重要价值。本文以文件访问类系统调用为切入点,结合strace工具链的实战应用,深入剖析open、read、write等关 阅读详情

一、文章来由

  最近看了《UNIX环境高级编程》,对以前比较模糊的一些知识结构又做了进一步的加强,特别是前两章讲到不带缓冲的文件I/O和带缓冲的标准I/O,对read、write、fread、fwrite、printf等等这些函数又有了新的认识。一个很大的感受是我们很多时候编程开发都只注重上层逻辑,虽然一个项目接一下项目,看上去做了不少事,但是夜深人静时仔细一想,究竟我们是否真正掌握了这些知识点,对于每一个知识点实现的机制我们是否能完整地说出来。这些东西最能体现一个人的基础知识是否扎实,我发现互联网公司的面试中最喜欢问这些基础知识,由一个很基本的函数都会层层递进引申出很多的问题。很多时候我们内心可能会很排斥,甚至不屑于这些基础知识,想着等用到的时候,我再来查,我就专注上层逻辑就好了,这样有助于提升我的开发效率。这样的想法貌似也没什么错,但是往往这就是瓶颈的来源,程序员最可怕的就是遇到瓶颈了。因为瓶颈这个东西是很难意识到的,一味追求实践而放弃理论学习,很容易就遇到瓶颈。(个人见解,不喜勿喷)

  本文算是自己看完《UNIX环境高级编程》文件I/O和标准I/O两章的读书笔记,文件I/O一章说不带缓冲,但后面又出现可带缓冲,搞得我有点晕,特意记下自己对此的理解。如果有什么不对的,欢迎指出,如果你觉得本文对你有帮助,就动动手指推荐下,或者是粉我下,你的关注是我写作的最大动力。^_^

 

二、缓冲机制

  众所周知,CPU和内存的数据交换要远大于磁盘操作,通过缓存机制,可以减少磁盘读写的次数,提高并发处理程序的效率,因此,缓存是一种提高任务存储和处理效率的有效方法。我们很多时候可以看到,缓存不单单在操作系统方面被采用,更是在Web技术、服务器端、分布式系统等领域发挥着及其重要的作用。

  从宏观上看,Linux操作系统分为用户态和内核态,在处理I/O操作的时候,两者都提供了缓存。用户态的称为标准I/O缓存,也称为用户空间缓存,而内核态的称为缓冲区高速缓存,也叫页面高速缓存。既然都提供了缓存,那为什么这本书上却分不带I/O的缓存和带I/O的缓存,原因其实是“不带I/O缓存”指的是用户空间中不为这些I/O操作设有缓冲,而内核是带缓冲的,这样来看,就不会糊涂了。

 

三、系统I/O和标准I/O

  系统I/O,又称文件I/O,或是内核态I/O,引用文件的方式是通过文件描述符,一个文件对应一个文件描述符。一个文件描述符用一个非负整数表示,0、1、2系统默认表示标准输入、标准输出、标准错误,某些UNIX系统规定了描述符的上限值OPEN_MAX,这些常量都定义在头文件<unistd.h>中。当读或写一个文件时,使用open或create系统调用返回的文件描述符标识该文件,并将其作为参数传递给read或write系统调用。

#include <unistd.h>
ssize_t read(int filedes, void *buf, size_t nbytes);
ssize_t write(int filedes, const void *buf, size_t nbytes);

   标准I/O,又叫用户态I/O,引用文件的方式则是通过文件流(stream),一般用fopen和freopen函数打开一个流,返回一个指向FILE对象的指针,其他函数如果要引用这个流,则将FILE指针作为参数传递。一个进程预定义了三个流,并且这三个流自动被进程使用,它们是标准输入流、标准输出流和标准出错流,这三个流和系统I/O所规定的三个文件描述符所引用的文件相同。当读或写一个文件时,不像系统I/O,仅定义了read和write两个系统调用函数,标准I/O定义了多个函数,程序员可以根据自己的需求灵活使用。这些函数可以分为每次一个字符的I/O,每次一行的I/O和直接I/O(或者二进制I/O、一次一个对象I/O、面向记录的I/O、面向结构的I/O)。

1)每次一个字符的I/O

复制代码
#include<sdio.h>

/* 输入函数 */
int getc(FILE *fp) -> 宏
int fgetc(FILE *fp) -> 函数
int getchar(void) 等价于getc(stdin)

/* 输出函数 */
int putc(int c, FILE *fp)
int fputc(int c, FILE *fp)
int putchar(int c) 等效于putc(c, stdout)
复制代码

 

2)每次一行I/O

复制代码
#include <stdio.h>

/* 输入函数 */
char *fgets(char *restrict buf, int n, FILE *restrict fp)
char *gets(char *buf)

/* 输出函数 */
int fputs(cont char *restrict str, FILE *restrict fp)
int puts(const char *str)
复制代码

 

3)直接I/O

#include <stdio.h>

size_t fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp)

size_t fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp)

 

  到此,我们大概了解了系统I/O和标准I/O引用文件的方法,以及一些常用的I/O函数。下面通过一个图来详细看下当用户调用一个I/O函数时,用户态和内核态的一个执行流程是什么样的,进一步了解缓存在I/O操作中的作用,以及用户态I/O和内核态I/O在执行效率上的区别。

 

四、I/O操作的流程

  如上图所示,用户进程空间和内核进程空间读写磁盘的操作都要经过缓冲区缓存,缓存的作用前面也提到过,是为了减少磁盘读写的次数,提高I/O的效率。当读写一个文件时,首先看系统I/O的操作流程。

1、系统I/O: 属于内核系统调用,没有涉及用户态的参与。以图中标号为例:

(3) 调用write函数向文件中写数据,buf中存放的就是要写入的数据,如write(fd, 'abc', 3)。调用前需要先设置BUFFSIZE。不同的BUFFSIZE会影响I/O效率,下面再来说这个问题。

(5) 延迟写:当缓存区高速缓存满或者内核要重写缓冲区的时候,才将数据写入输出队列,等数据到队列首部的时候,才真正触发磁盘的写操作。

(6) 预读:当检测到正进行顺序读取时,内核就试图读入比应用程序所要求更多的数据,并假想应用程序很快就会读到这些数据。这样,当缓冲区没有数据时,能够快速填充下次要读取的数据。

(4) 调用read从缓冲区高速缓存读取所需数据到逻辑单元中进行处理。

以上,就是系统I/O所涉及到的四步操作。

2、标准I/O:属于ISO C实现的标准库函数,调用的是底层的系统调用。

(1) 将逻辑单元中的数据写入文件,根据需求,有三种函数类型可以调用,以fputc、fputs、fwrite为例,这些函数不用人为去控制缓冲区的大小,而是系统自动申请的,当用户定义了相应的I/O函数之后,根据不同的缓存类型(是全缓冲、行缓冲还是无缓冲),系统自动调用malloc等函数申请缓冲区,即标准I/O缓存。

(3)(5) 当用户缓冲区满了之后,如系统I/O操作一般,此时调用write从标准I/O缓存中复制数据到内核缓冲区,再写入磁盘。

(4)(6) 同系统I/O操作,从内核缓冲区调用read读入到用户缓冲区。

(2) 同样有三种函数类型可以调用,以fgetc、fgets、fread为例,读入逻辑单元进行后续的处理。

可见,标准I/O实现的机制就是基于系统I/O,这样看来,标准I/O在效率上肯定不如系统I/O,但事实是标准I/O与系统I/O相比并不慢很多,而且还有很多其他的优点,下面一一述说(本篇文章最重要的就是下一小节)。

 

五、I/O效率

  系统I/O效率受限于read、write系统调用的次数,而系统调用次数则又受限于内核缓冲区的大小,即BUFFSIZE,通过设置不同的BUFFSIZE,系统CPU时间是不同的,其最小值出现在BUFFSIZE=4096处,原因是该测试所采用的是Linux ext2文件系统,其块长为4096字节,也即缓冲区所能申请到的最大缓冲区大小,我们把4096字节看做是本次最佳I/O长度。如果继续扩大缓冲区大小,对此时间几乎没有影响。所以,对于系统I/O操作,一个最大的问题就是:需要人为控制缓存的大小及最佳I/O长度的选择,另外就是系统调用与普通函数调用相比通常需要花费更多的时间,因为系统调用具体内核要执行这样的操作:1)内核捕获调用,2)检查系统调用参数的有效性,3)在用户空间和内核空间之间传输数据。

  因此,引入标准I/O的目的就是为了通过标准I/O缓存来避免BUFFSIZE选择不当而带来的频繁的系统调用。根据用户不同的需求,选择不同的I/O函数,然后根据不同的缓存类型,自动调用malloc等缓存分配函数分配合适的缓存,等分配的缓存满之后,再调用系统I/O从标准I/O缓存向内核缓存拷贝数据,这样就进一步减少了系统调用的次数。

  但是不同的标准I/O函数,不同的缓存类型也会带来不同的效率。如上图,当选择系统最佳I/O长度,即BUFFSIZE的大小和文件系统的块长一致,可以得到最佳的时间。当选用标准I/O函数时,每次一个字符函数fgetc、fputc和每次一行函数fgets、fputs函数相比要花费较多的CPU时间,而每次单个字节调用系统I/O则花费更多的时间,如果是一个100M的文件,则要执行大概2亿次函数调用,也就引起2亿次系统调用(从用户缓冲区到内核缓冲区,再到磁盘),而fgetc版本也执行了2亿次函数调用,但只引起了大约25222次系统调用,所以,时间就大大减少了。

  综合以上,标准I/O函数虽然基于系统I/O实现,但很大程度上减少了系统调用的次数,而且不用人为关心缓冲区大小的选择,整体上提高了I/O的效率。另外,标准I/O提供了多种缓存类型,方便程序员根据不同的应用需求选择不同的缓存要求,提高了编程的灵活性,当选择无缓存时,就相当于直接调用系统I/O。

Ubuntu18.04安装VCS、Verdi、dve全套教程亲测(有成功截图) 通过下面十个步骤,我们就可以在ubuntu上使用vcs、verdi、dve等软件! 一、安装ubuntu系统,这个不在这里讲了,重点叫安装VCS、Verdi等软件 下面的过程一部分同其他网上流程一致,对其中部分进行修改。如果按照其他网上教程是不会成功的,本人亲测,下面是我试验网上不同文章的流程遇到的错误摸索后总结的流程,按照步骤保证可以成功运行,包括破解流程,使用时间不限制在2020年,可以自定义2030等等。 二、安装文件结构 –synopsys_installer #此程序用于安装下面三个程序。所以你想 阅读详情

相关推荐

UNITE真菌数据库使用避坑指南:从下载到注释的完整流程

本文提供了UNITE真菌数据库从下载到注释的完整实战指南。针对真菌ITS序列分析,重点解析了数据库版本选择、格式转换的常见陷阱,并详细介绍了如何训练优化的sklearn分类器以提升物种注释的精度和可靠性,帮助用户构建高效稳定的本地工作流。

weixin_29240343的博客 199

深入探讨 I/O 多路复用:提升系统 I/O 效率的关键技术

I/O(输入/输出)操作是计算机系统中不可或缺的一部分,而 I/O 多路复用技术则是提高系统 I/O 效率的重要手段。本文将浅谈 I/O 的基本概念,重点探讨 I/O 多路复用技术的原理、优势以及在现代系统中的应用。

欢迎你来到我的博客 554

高斯投影坐标正反算算法(python)

高斯坐标正反算算法,在正算的时候,y的第一个数字表示是用几度带换算的,第二个数字和第三个数字表示带号。该程序是根据武汉大学出版社出版的第三版的《大地测量学基础》编写, 用于课程实验学习。

weixin_64338372的博客 4586

操作系统—输入输出系统

输入输出系统 1 I/O系统的功能、模型和接口 I/O系统管理对象主要是I/O设备和相应的设备控制器,主要任务是完成用户提出的I/O请求,提高I/O速率,提高设备利用率 1.1 I/O系统的基本功能 第一第二功能为了方便用户使用I/O设备 第三第四功能是用于提高CPU和I/O设备的利用率 第五第六功能是为用户在共享设备时提供方便,保证系统有条不紊进行,系统发生错误时候,能及时发现错误,甚至能自动修复错误。 ① 隐藏物理设备的细节 对于不同的设备,需要有不同的命令和参数。如果要求程序员或用户编

weixin_45709829的博客 4451

别再瞎猜了!Fluent瞬态计算时间步长到底设多少?一个公式+实战案例搞定

本文详细解析了Fluent瞬态计算中时间步长的设置方法,通过黄金公式和实战案例,帮助工程师准确确定时间步长,避免计算发散和资源浪费。文章还介绍了动态调整策略和典型工程场景的参数优化,提升仿真效率和精度。

weixin_42638178的博客 425

MaxKey+JeecgBoot单点登录实战:手把手教你从零配置到账户同步

本文提供MaxKey与JeecgBoot单点登录(SSO)的实战整合指南。详细讲解从MaxKey应用注册、JeecgBoot前后端改造,到最终实现账户同步的完整步骤,重点解决Shiro配置、令牌交换等关键问题,帮助企业构建统一、可扩展的身份管理体系。

weixin_29258297的博客 540

标准I/O库之标准I/O效率、二进制I/O和定位流

标准I/O的效率 使用getc和putc函数将标准输入复制到标准输出。 #include "../../include/apue.h" int main(void) { int c; while((c=getc(stdin)) != EOF) if(putc(c,stdout) == EOF) err_sys("output error");...

Dunk.Wan 369

VS Code配合Prettier自动格式化代码

每次修改 .prettierrc.cjs 配置文件后,如果发现保存代码时格式化没有生效,可能是 VS Code 插件缓存未及时刷新。建议此时重启 VS Code,确保新的配置被正确加载并生效。

分享技术,分享经验 7422

linux系统性能调优第一步——性能分析(vmstat)

性能调优的第一步是性能分析,下面从性能分析着手进行一些介绍,尤其对linux性能分析工具vmstat的用法和实践进行详细介绍。 —————————————————————————————————————————— 1.性能分析的目的 1)找出系统性能瓶颈(包括硬件瓶颈和软件瓶颈); 2)提供性能优化的方案(升级硬件?改进系统系统结构?); 3)达到合理的硬件和软件配置; 4)使系统资源使

diquren的专栏 628

波分复用-Zblock仿真

Z-BLOCK是一种用于光通信模块的光学元件,其核心结构是在玻璃块一侧贴有4个特定波长滤光片,另一侧镀有反射膜和增透膜。光信号在内部呈现Z字形反射路径,实现波长分离。文章以可见光LED为例,在ZEMAX中演示了非序列模式下的建模过程,重点介绍了滤光片和膜层的参数设置方法。需要注意的是,入射角度需根据玻璃折射率、通道高度和厚度精确计算,否则可能导致光泄漏。该建模方法同样适用于光通信中的光纤激光器系统。

Zhaxibamao的博客 1743

openbmc 支持mctp over pcie(三)(支持作为endpoint)

本文介绍了在Intel BHS平台上支持MCTP over PCIe端点开发的调试方法。开发基于CodeConstruct和AspeedTech-BMC的开源项目,将内核驱动改为模块形式以便调试。内容包括DTS配置、驱动模块设置步骤,以及如何将BMC配置为端点设备。调试过程涉及内核模块加载、MCTPD启动、参数设置等关键命令,并展示了路由表、地址和邻居信息的测试结果。该方案仅为实验性工作,需注意停止其他使用/dev/aspeed-mctp0接口的应用。

wit_yuan的专栏 1553

探秘高性能I/O库:liburing

`liburing` 是一个开源的C库,它为应用程序提供了简洁的接口,用于与Linux内核的io_uring机制交互。io_uring是Linux内核提供的一种新型异步I/O框架,旨在大幅提升系统处理I/O请求的效率和性能。liburing简化了这个复杂过程,让开发者可以更方便地利用这一先进技术。 ## 项目技术分析 `liburing` 的设计目标是与特定内核版本解耦,这意味着即使在旧版本的...

gitblog_00070的博客 838

基于STM32L4XX、HAL库的 ATGM336H GPS定位模块驱动程序设计

/ 转换为km/h。// 定位状态: 0=无效, 1=GPS定位, 2=差分定位。// GPS解析数据。支持GPS/BeiDou/GLONASS/Galileo/QZSS多系统联合定位。// 回调函数(需要在HAL_UART_RxCpltCallback中调用)// 地面速度(km/h)// 海拔高度(m)

zhusl6688的博客 794

linux - I/O Storage Stack

引用 闪存物理结构 https://blog.youkuaiyun.com/juS3Ve/article/details/93679509 你所不知道到的NVMe https://blog.youkuaiyun.com/juS3Ve/article/details/89325237蛋蛋读NVMe之一:为什么刘备需要NVMe https://blog.youkuaiyun.com/juS3Ve/article/details/93...

vertor11的博客 1119

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

探秘nio_uring:Java界的I/O新星

探秘nio_uring:Java界的I/O新星 在高性能网络编程的领域里,每一项技术革新都如星辰闪烁,今天我们来探索一颗璀璨的新星——nio_uring,一个专为Java打造的基于Linux io_uring特性的I/O库。 项目介绍 nio_uring是Java开发者的一份厚礼,它巧妙地利用了Linux内核的io_uring机制,旨在提供一个简单灵活、高效至极且零复制的数据传输解决方案。这个开源...

gitblog_00097的博客 1089

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

【探秘Linux内核:那些惊艳算法的奇妙之旅】

你是否好奇 Linux 内核高效运行的秘密?快来阅读这篇博客,一同探索内核中经典且巧妙的算法。从睡眠锁、信号量确保多线程对共享资源的有序访问,到跳表在网络缓存里实现快速查找;还有布隆过滤器在网络地址过滤时的神来之笔,快速判断元素是否存在。文章不仅详细阐述算法原理,还配有源码示例辅助理解。无论是想深入钻研内核机制的技术大神,还是渴望提升算法认知的编程爱好者,这篇博客都能满足你。别再犹豫,立即开启这场探索 Linux 内核算法的精彩之旅,相信你定会收获满满!

youngerwang的博客 1070

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Linux标准IO与文件IO的区别与联系

前言: 本文主要内容是从网上摘录的,将查到的零散信息消化后归纳在一起,方便以后查阅。 参考以下文章,谢谢作者的分享: Linux探秘之I/O效率 https://cloud.tencent.com/developer/article/1018033 linux系统编程之基础必备(四):C 标准库IO缓冲区和内核缓冲区的区别 https://cloud.tencent.com/developer/article/10...

the_wan的博客 1735

Linux - 探秘 Linux 的 /proc/sys/vm 常见核心配置

是 Linux 系统中的一个虚拟目录,属于/proc文件系统的一部分。/proc文件系统提供了一种与内核交互的方式,其中的许多文件允许我们查看和调整内核的行为。目录专门用于内核的虚拟内存(Virtual Memory)管理相关的参数设置。目录提供了丰富的内存管理参数,允许用户根据具体需求优化系统性能。理解并合理配置这些参数,可以显著提高 Linux 系统的效率和稳定性。

小工匠 6303

从 Linux 内核角度探秘 JDK NIO 文件读写本质

带你彻底打通 Linux 文件 IO 相关的知识系统脉络

bin的技术小屋 639

Linux硬件信息探秘:从/dev/mem到RWLinux的图形化实践

本文深入探讨了Linux系统如何通过/dev/mem等底层接口访问硬件信息,并介绍了图形化工具RWLinux的实践。文章从物理内存映射、内核安全限制讲起,详细阐述了绕过限制、访问PCI配置空间等核心技术,最终展示了如何利用ncurses库构建一个轻量、免安装的图形化硬件调试工具,为嵌入式开发和系统调试提供了便利。

ppp33的博客 295

探秘Linux IO虚拟化:virtio的奇幻之旅(1)

探秘Linux IO虚拟化:virtio的奇幻之旅(1)

phmatthaus的专栏 245

探秘Linux IO虚拟化:virtio的奇幻之旅

在虚拟化的大家族中,Linux IO 虚拟化占据着重要的地位。它专注于解决虚拟机与物理硬件之间输入 / 输出(I/O)通信的问题,力求打破 I/O 性能瓶颈,让虚拟机在数据传输的高速公路上畅行无阻。想象一下,虚拟机就像一个个繁忙的工厂,不断地需要原材料(输入数据)和输出产品(输出数据),而 Linux IO 虚拟化就是优化工厂运输线路和装卸流程的关键技术,确保原材料和产品能够快速、高效地进出工厂。而 virtio,作为 Linux IO 虚拟化领域的璀璨明星,发挥着举足轻重的作用。

深度Linux 1873

从Java程序员视角探秘Linux系统性能调优

linux系统性能调优解决方案

m0_73978383的博客 1088

探秘Netty1:从IO模型谈到Reactor、Proactor线程模型

目录 随着计算机硬件性能不断提高,服务器 CPU 的核数越来越越多,为了充分利用多核 CPU 的处理能力,提升系统的处理效率和并发性能,多线程并发编程越来越显得重要。无论是 C++ 还是 Java 编写的网络框架,大多数都是基于 Reactor 模式进行设计和开发,Reactor 模式基于事件驱动,特别适合处理海量的 I/O 事件,今天我们就简单聊聊 Reactor 线程模型,主要内容分为以下几...

Java技术江湖 2023
上一篇: Linux探秘之用户态与内核态
下一篇: UNIX环境高级编程笔记之高级I/O
ZWFGOGO
博客等级 码龄14年 11粉丝 6原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值