公众号关注 「开源Linux」

回复「学习」,有我为您特别筛选的学习资料~

来源于:360云计算

1

前言

Linux IO是文件存储的基础。本文参考了网上博主的一些文章,主要总结了LinuxIO的基础知识。

2

linux IO栈

Linux文件IO采用分层的设计。分层有两个好处:

  1. 架构清晰;

  2. 功能解耦;

Linux文件IO的时候,从通用性和性能的角度考虑,采用了一个折中的方案来满足我们日常写磁盘。

例如:

void foo() {
char *buf = malloc(MAX_SIZE);
strncpy(buf, src, MAX_SIZE);
fwrite(buf, MAX_SIZE, 1, fp);
fclose(fp);
}

上面代码的说明如下:

malloc的buf对应图中的application buffer。调用fwrite之后,操作系统将数据从application buffer拷贝到libc buffer,即c库标准IO buffer。fwrite 返回后,数据还保存在libc buffer,如果这个时候进程退出,这些数据将会丢失,没有写到磁盘上。

当调用fclose的时候,fclose只会刷新libc buffer到page cache,如果确保数据写到磁盘,kernel buffer也必须flush。例如使用sync,fsync。除了fclose方法外,还有一个主动刷新接口fflush函数,但是fflush函数只是将数据从libc buffer拷贝到page cache,并没有刷新到磁盘上,从page cache刷新到磁盘上,可以通过调用fsync完成。

sync会告诉OS,你要将这些数据刷新到磁盘上,但并不能真正保证确实已经写到磁盘上了。属于尽力而为的操作。( According to the standard specification (e.g., POSIX.1-2001), sync() schedules the writes, but may return before the actual writing is done. )sync会将page cache数据送到 disk cache层,至于什么时候写入物理磁盘介质上,则由磁盘控制器自己决定。

从上面可以看到,一个常用的fwrite执行过程,需要经历多次数据拷贝才能最终到达磁盘。

如果我们不想通过fwrite+fflush这种方式,而是想直接写到page cache。这就是我们linux常用的系统调用read/write函数。调用write函数时,实际上是直接通过系统调用将数据从application buffer拷贝到page cache中。但是我们知道,系统调用read/write会触发用户态到内核态的转换这将会一定性能消耗。

如果我们想绕过page cache,直接将数据送到磁盘设备上怎么办?可以通过open的时候携带O_DIRECT属性。这时write该文件,就是直接写到设备上。

如果我们想直接写到磁盘扇区有没有办法?这就是RAW设备写,绕开了文件系统,直接写扇区,例如:fdisk,dd之类的操作就是。

3

IO调用链

fwrite是系统提供的最上层接口,也是最常用的接口。它在用户进程空间开辟了一个buffer,将多次小数据量相邻写操作先缓存起来,然后合并,最终调用write系统调用一次性写入(或者将大块数据分解多次write调用)。

write函数通过调用系统调用接口,将数据从应用层拷贝到内核,因此write会触发用户态到内核态的切换。当数据到达page cache后,内核并不会立即将数据往下传递,而是返回用户空间。数据什么时候写入磁盘,是由内核IO调度决定,所以write是个异步调用。这一点和read不同,read调用先检查page cache里面是否有数据,如果有,就取回来并返回给用户,如果没有,就同步等待下去并等待有数据再返回给用户。所以read是个同步过程。如果想把write的异步过程改成同步过程,可以将open文件的时候,携带O_SYNC。

数据到了page cache之后,内核有pdflush线程不停的检测脏页,判断是否要写回到磁盘中。然后把需要写回的页提交到IO队列(即:IO调度队列),由IO调用队列的调度策略决定何时写回。

4

IO调度层

加入IO调度队列的任务并不一定会立即执行,调度层会从全局出发,尽量让整体磁盘IO性能最大化。大致的工作方式是让磁头类似电梯那样工作,先往一个方向走,走到尽头再回来,这样磁盘效率会比较高;磁盘是单向旋转的,不会反复逆时针转动,因为磁头寻道时间比较耗时。

内核中有多种IO调度算法,例如:noop,deadline和cfg,在你机器上,通过dmesg | grep -i scheduler 命令可以查看你的linux支持的算法。当磁盘是SSD时,采用的是随机读写,并没有磁道、磁头,应用于传统机械磁盘的调度算法反而不适用。调度算法中的noop算法,适合配置SSD硬盘。

任务从IO队列出来后,就到了驱动层,驱动层通过DMA,将数据写入磁盘cache。

至于磁盘cache何时写入磁盘介质,这是由磁盘控制器自己决定。如果想确认要写到磁盘介质上,就调用fsync函数。

5

一致性和安全性

5.1 安全性

从上可以看到,数据没有到达磁盘介质之前,可能处于不同的物理内存cache中,那么这个时候如果出现进程退出,内核挂掉,物理机器掉电的情况,数据会丢失吗?

  • 当进程退出后:在application buffer或者libc buffer的数据会丢失。如果数据到了page cache,此时进程退出,即使数据还没有到达硬盘,数据也不会丢失。

  • 当内核挂掉:只要数据还没有到disk cache,都将会丢失。

  • 物理机掉电:此时所有数据都会丢失。

5.2 一致性

Q:同一个进程A中,多次open同一个文件,然后write数据会怎么样?

fd1 = open("file", O_RDWR|O_TRUNC);
fd2 = open("file", O_RDWR|O_TRUNC); while (1) {
write(fd1, "hello \n", 6);
write(fd2, "world \n", 6);
}

A:先写的数据是会被覆盖的。原因在于同一进程中不同的文件描述符(fd),各自对应一个独立的打开文件表,在打开文件表中有属于自己的文件位移量,开始都是0,然后各自从0开始写,每写一个字节向后移动一个字节,写的位置是重叠的,因此会覆盖。

如何解决被覆盖的问题呢?

必须为每个open指定O_APPEND。文件长度是共享的,当文件被写入数据后,文件长度就会被更新,指定O_APPEND之后,使用不同fd写数据时,都会使用文件长度更新自己的文件位移量,保证每次都是在文件的最末尾写数据,这样就不会出现覆盖。

这里补充一点:同一个进程中多次open同一个文件时,文件描述符是不同的,在同一进程中某个文件描述符被占用,在close之前,是不会被再次分配。

Q:两个进程A和进程B,open同一个文件,然后write数据会怎么样?

A:先写的数据是会被覆盖的。覆盖的原因也是各自有独立的文件位移量。同样指定O_APPEND,使用文件长度更新文件位移量,保证各自操作时,都在文件尾部操作,不会出现相互覆盖的情况。

这里补充一点:不同进程打开同一个文件时,各自使用的fd可能是相等的,之所以相同,是因为不同进程有自己单独的文件描述符池,默认是0~1023,各自分配各自的,是有可能分配到相等的fd

Q:A进程写,B进程读取会写脏吗?

A:会的。读取进程对文件内容变化毫无感知,只是按部就班的读取,直到文件结束EOF。

5.3 读文件过程

Linux read文件的流程大致如下:

  1. lib中的read函数首先进入系统调用sys_read;

  2. 接着sys_read再进入VFS中的vfs_read、generic_file_read等函数;

  3. 接着VFS中的generic_file_read会判断是否缓存命中,如果命中则返回;

  4. 如果没有命中,内核在page cache中分配一个新页框,发出缺页中断;

  5. 内核向通用块层发起IO请求,块设备屏蔽disk、U盘等差异;

  6. 通用块层将bio代表的IO请求发送到IO请求队列中;

  7. IO调度层通过电梯算法来调度队列中的请求;

  8. 驱动程序向磁盘控制器发出读取命令控制,DMA方式直接填充到page cache中的新页框;

  9. 控制器发出中断通知;

  10. 内核将用户需要的数据填充到用户内存中;

  11. 然后你的进程被唤醒;

6

性能问题

磁盘的寻道时间时相当的慢,平均寻道大概在10ms,也就是每秒只能100-200次寻道。

磁盘转速也是影响性能的关键,如果是15000rpm,大概每秒500转。一般情况下,盘片转太快,磁头跟不上,所以需要多转几圈才能完全读出磁道内容。

机械硬盘顺序写 0~30MB左右,顺序读取速率一般 0~50MB左右,性能好的可以达到100多MB;SSD读取达到0~400MB左右。

相关参考文章

  • http://blog.chinaunix.net/uid-27105712-id-3270102.html?page=2

  • https://zhuanlan.zhihu.com/p/138371910

  • https://meik2333.com/posts/linux-many-proc-write-file/

  • https://blog.csdn.net/qq_43648751/article/details/104151401


关注「开源Linux」加星标,提升IT技能

浅谈 Linux IO的更多相关文章

  1. 浅谈linux IO csy 360技术 2021-01-18

    浅谈linux IO csy 360技术 2021-01-18

  2. Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理

    Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理 转自:https://www.jianshu.com/p/2b71ea919d49 本系列文章首发于我的个人博 ...

  3. 浅谈Linux中的信号处理机制(二)

    首先谢谢 @小尧弟 这位朋友对我昨天夜里写的一篇<浅谈Linux中的信号处理机制(一)>的指正,之前的题目我用的“浅析”一词,给人一种要剖析内核的感觉.本人自知功力不够,尚且不能对着Lin ...

  4. 浅谈 Linux 内核无线子系统

    浅谈 Linux 内核无线子系统 本文目录 1. 全局概览 2. 模块间接口 3. 数据路径与管理路径 4. 数据包是如何被发送? 5. 谈谈管理路径 6. 数据包又是如何被接收? 7. 总结一下 L ...

  5. []转帖] 浅谈Linux下的五种I/O模型

    浅谈Linux下的五种I/O模型 https://www.cnblogs.com/chy2055/p/5220793.html  一.关于I/O模型的引出 我们都知道,为了OS的安全性等的考虑,进程是 ...

  6. 浅谈linux中shell变量$#,$@,$0,$1,$2,$?的含义解释

    浅谈linux中shell变量$#,$@,$0,$1,$2,$?的含义解释 下面小编就为大家带来一篇浅谈linux中shell变量$#,$@,$0,$1,$2的含义解释.小编觉得挺不错的,现在就分享给 ...

  7. 【VS开发】【DSP开发】浅谈Linux PCI设备驱动(二)

    我们在 浅谈Linux PCI设备驱动(一)中(以下简称 浅谈(一) )介绍了PCI的配置寄存器组,而Linux PCI初始化就是使用了这些寄存器来进行的.后面我们会举个例子来说明Linux PCI设 ...

  8. 浅谈Linux下/etc/passwd文件

    浅谈Linux 下/etc/passwd文件 看过了很多渗透测试的文章,发现在很多文章中都会有/etc/passwd这个文件,那么,这个文件中到底有些什么内容呢?下面我们来详细的介绍一下. 在Linu ...

  9. (转)浅谈 Linux 内核无线子系统

    前言 Linux 内核是如何实现无线网络接口呢?数据包是通过怎样的方式被发送和接收呢? 刚开始工作接触 Linux 无线网络时,我曾迷失在浩瀚的基础代码中,寻找具有介绍性的材料来回答如上面提到的那些高 ...

随机推荐

  1. Mybatis的XML文件调用静态方法

    如果需要在Mapper文件中调用静态方法,需要 <choose> // 需要静态方法返回true还是false <when test="@staticClass@stati ...

  2. AQS分析笔记

    1 介绍 AQS: AbstractQueuedSynchronizer,即队列同步器.是构建锁或者其他同步组件的基础框架.它维护了一个volatile int state(代表共享资源)和一个FIF ...

  3. STM32试题及答案

    一.选择题 1.Cortex-M处理器采用的架构是(  D  ) (A)v4T               (B)v5TE              (C)v6                (D)v ...

  4. Failed to write HTTP message,Could not write JSON错误

    今天遇到使用@ResponseBody注解返回json数据时报错 Failed to write HTTP message: org.springframework.http.converter.Ht ...

  5. [性能测试] locust学习-基础篇

    在本文中,我将介绍一个名为Locust的性能测试工具.我将从Locust的功能特性出发,结合实例对Locust的使用方法进行介绍. 概述 Locust主要有以下的功能特性: 在Locust测试框架中, ...

  6. 要多简单就有多简单的H5拍照加水印

    来一个简单粗暴的gif演示图 先来html 内容 <video id="video" width="320" height="240" ...

  7. Slog71_选取、上传和显示本地图片GET !(微信小程序之云开发-全栈时代3)

    ArthurSlog SLog-71 Year·1 Guangzhou·China Sep 12th 2018 ArthurSlog Page GitHub NPM Package Page 掘金主页 ...

  8. Android Studio连接SQLite数据库与SQLite Studio实时同步的实现

    最近学习用到了android开发连接数据库这一块,发现连接成功后,都要先访问安卓项目的数据库路径data/data/项目/databases,然后把对应的db文件拷出来,再在SQLite的可视化工具中 ...

  9. js select 删除某一项下拉列表的值

    <script type="text/javascript"> //muqingwei modify 通过群组类别选择需要的的群组 var mySelect = doc ...

  10. 小程序拿checkbox的checked属性

     方法一.checkbox <checkbox class="round red" bindtap="checkboxChange" checked=&q ...