在前文《read文件一个字节理论会产生多大的磁盘IO?》写完之后,原本想着偷个懒,只通过读操作来让大家理解下Linux IO栈的各个模块就行了。但很多同学示意再让我写一篇对于写操作的。既然不少人都有这个需要,那我就写一下吧。

Linux内核真的是太简单了,源代码的行数曾经从1.0版本时的几万行,到当初曾经是千万行的一个硕大无朋了。间接钻进去的话,很容易在各种目迷五色的各种调用中迷失了本人,再也钻不进去了。我分享给大家一个我在推敲内核的办法。个别我本人先想一个本人很想搞清楚的问题。不论在代码里咋跳来跳去,时刻都要记得本人的问题,无关的局部尽量少去发散,只有把本人的问题搞清楚了就行了。

当初我想搞明确的问题是,在最罕用的形式下,不开O_DIRECT、不开O_SYNC(写文件的办法有很多,有sync模式、direct模式、mmap内存映射模式),write是怎么写的。c的代码示例如下:

#include

int main()

{

char c = 'a';

int out;

out = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC);

write(out,&c,1);

...

}

进一步细化我的问题,咱们对关上的问题写入一个字节后

write函数在内核里是怎么执行的?

数据在什么机会真正能写入到磁盘上?

咱们在探讨的过程中不可避免地要波及到内核代码,我应用的内核版本是3.10.1。如果有须要,你能够到这里来下载。https://mirrors.edge.kernel.org/pub/linux/kernel/v3.x/。

write函数实现分析

我花了不短的时候跟踪write写到ext4文件系统时的各种调用和返回,大抵理进去了一个交互图。当然为了突出重点,我摈弃了不少细节,比方DIRECT IO、ext4日志记录啥的都没有体现进去,只抽取进去了一些我认为要害的调用。

在下面的流程图里,所有的写操作最终到哪儿了呢?在最初面的__block_commit_write中,只是make dirty。而后大部分状况下你的函数调用就返回了(稍后再说balance_dirty_pages_ratelimited)。数据当初还在内存中的PageCache里,并没有真正写到硬盘。

为什么要这样实现,不间接写硬盘呢?起因就在于硬盘尤其是机械硬盘,性能是在是太慢了。一块服务器级别的万转盘,最坏随机拜访均匀提早都是毫秒级别的,换算成IOPS只有100多不到200。构想一下,如果你的后端接口里每个用户来拜访都须要一次随机磁盘IO,不论你多牛的服务器,每秒200的qps都将间接打爆你的硬盘,置信作为为百万/千万/过亿用户提供接口的你,这个是你相对不能忍的。

Linux这么搞也是有副作用的,如果接下来服务器产生掉电,内存里货色全丢。所以Linux还有另外一个“补丁”-提早写,帮咱们缓解这个问题。留神下,我说的是缓解,并没有彻底解决。

再说下balance_dirty_pages_ratelimited,尽管绝大部分状况下,都是间接写到Page Cache里就返回了。但在一种状况下,用户过程必须得期待写入实现才能够返回,那就是对balance_dirty_pages_ratelimited的判断如果超出限度了。该函数判断以后脏页是否曾经超过脏页下限dirty_bytes、dirty_ratio,超过了就必须得期待。这两个参数只有一个会失效,另外1个是0。拿dirty_ratio来说,如果设置的是30,就阐明如果脏页比例超过内存的30%,则write函数调用就必须期待写入实现能力返回。能够在你的机器下的/proc/sys/vm/目录来查看这两个配置。

# cat /proc/sys/vm/dirty_bytes

0

# cat /proc/sys/vm/dirty_ratio

30

内核提早写

内核是什么时候真正把数据写到硬盘中呢?为了疾速摸清楚全貌,我想到的方法是用systemtap工具,找到内核写IO过程中的一个要害函数,而后在其中把函数调用堆栈打进去。查了半天材料当前,我决定用do_writepages这个函数。

#!/usr/bin/stap

probe kernel.function("do_writepages")

{

printf("--------------------------------------------------------\n");

print_backtrace();

printf("--------------------------------------------------------\n");

}

systemtab跟踪当前,打印信息如下:

0xffffffff8118efe0 : do_writepages+0x0/0x40 [kernel]

0xffffffff8122d7d0 : __writeback_single_inode+0x40/0x220 [kernel]

0xffffffff8122e414 : writeback_sb_inodes+0x1c4/0x490 [kernel]

0xffffffff8122e77f : __writeback_inodes_wb+0x9f/0xd0 [kernel]

0xffffffff8122efb3 : wb_writeback+0x263/0x2f0 [kernel]

0xffffffff8122f35c : bdi_writeback_workfn+0x1cc/0x460 [kernel]

0xffffffff810a881a : process_one_work+0x17a/0x440 [kernel]

0xffffffff810a94e6 : worker_thread+0x126/0x3c0 [kernel]

0xffffffff810b098f : kthread+0xcf/0xe0 [kernel]

0xffffffff816b4f18 : ret_from_fork+0x58/0x90 [kernel]

从下面的输入咱们能够看出,真正的写文件过程操作是由worker内核线程收回来的(和咱们本人的应用程序过程没有半毛钱关系,此时咱们的应用程序的write函数调用早就返回了)。这个worker线程写回是周期性执行的,它的周期取决于内核参数dirty_writeback_centisecs的设置,依据参数名也大略能看进去,它的单位是百分之一秒。

# cat /proc/sys/vm/dirty_writeback_centisecs

500

我查看到我的配置是500,就是说每5秒会周期性地来执行一遍。回顾咱们的问题,咱们最关怀的问题的啥时候写入的,围绕这个思路不过多发散。于是沿着这个调用栈一直地跟踪,跳转,终于找到了上面的代码。如下代码里咱们看到,如果是for_background模式,且over_bground_thresh判断胜利,就会开始回写了。

static long wb_writeback(struct bdi_writeback *wb,

struct wb_writeback_work *work)

{

work->older_than_this = &oldest_jif;

...

if (work->for_background && !over_bground_thresh(wb->bdi))

break;

...

if (work->for_kupdate) {

oldest_jif = jiffies -

msecs_to_jiffies(dirty_expire_interval * 10);

} else ...

}

static long wb_check_background_flush(struct bdi_writeback *wb)

{

if (over_bground_thresh(wb->bdi)) {

...

return wb_writeback(wb, &work);

}

}

那么over_bground_thresh函数判断的是啥呢?其实就是判断以后的脏页是不是超过内核参数里dirty_background_ratio或dirty_background_bytes的配置,没超过的话就不写了(代码位于fs/fs-writeback.c:1440,限于篇幅我就不贴了)。这两个参数只有一个会真正失效,其中dirty_background_ratio配置的是比例、dirty_background_bytes配置的是字节。

在我的机器上的这两个参数配置如下,示意脏页比例超过10%就开始回写。

# cat /proc/sys/vm/dirty_background_bytes

0

# cat /proc/sys/vm/dirty_background_ratio

10

那如果脏页始终都不超过这个比例怎么办呢,就不写了吗? 不是的。在下面的wb_writeback函数中咱们看到了,如果是for_kupdate模式,会记录一个过期标记到work->older_than_this,再往后面的代码中把合乎这个条件的页面也写回了。dirty_expire_interval这个变量是从哪儿来的呢? 在kernel/sysctl.c里,咱们发现了蛛丝马迹。哦,原来它是来自/proc/sys/vm/dirty_expire_centisecs这个配置。

1158 {

1159 .procname = "dirty_expire_centisecs",

1160 .data = &dirty_expire_interval,

1161 .maxlen = sizeof(dirty_expire_interval),

1162 .mode = 0644,

1163 .proc_handler = proc_dointvec_minmax,

1164 .extra1 = &zero,

1165 },

在我的机器上,它的值是3000。单位是百分之一秒,所以就是脏页过了30秒就会被内核线程认为须要写回到磁盘了。

# cat /proc/sys/vm/dirty_expire_centisecs

3000

论断

咱们demo代码中的写入,其实绝大部分状况都是写入到PageCache中就返回了,这时并没有真正写入磁盘。咱们的数据会在如下三个机会下被真正发动写磁盘IO申请:

第一种状况,如果write零碎调用时,如果发现PageCache中脏页占比太多,超过了dirty_ratio或dirty_bytes,write就必须期待了。

第二种状况,write写到PageCache就曾经返回了。worker内核线程异步运行的时候,再次判断脏页占比,如果超过了dirty_background_ratio或dirty_background_bytes,也发动写回申请。

第三种状况,这时同样write调用曾经返回了。worker内核线程异步运行的时候,尽管零碎内脏页始终没有超过dirty_background_ratio或dirty_background_bytes,然而脏页在内存中呆的工夫超过dirty_expire_centisecs了,也会发动会写。

如果对以上配置不称心,你能够本人通过批改/etc/sysctl.conf来调整,批改完了别忘了执行sysctl -p。

最初咱们要意识到,这套write pagecache+回写的机制第一指标是性能,不是保障不失落咱们写入的数据的。如果这时候掉电,脏页工夫未超过dirty_expire_centisecs的就真的丢了。如果你做的是和钱相干十分重要的业务,必须保障落盘实现能力返回,那么你就可能须要思考应用fsync。

开发内功修炼之硬盘篇专辑:

1.磁盘开篇:扒开机械硬盘坚挺的外衣!

2.磁盘分区也是隐含了技术技巧的

3.咱们怎么解决机械硬盘既慢又容易坏的问题?

4.拆解固态硬盘构造

5.新建一个空文件占用多少磁盘空间?

6.只有1个字节的文件理论占用多少磁盘空间

7.文件过多时ls命令为什么会卡住?

8.了解格式化原理

9.read文件一个字节理论会产生多大的磁盘IO?

10.write文件一个字节后何时发动写磁盘IO?

11.机械硬盘随机IO慢的超乎你的设想

12.搭载固态硬盘的服务器到底比搭机械硬盘快多少?

我的公众号是「开发内功修炼」,在这里我不是单纯介绍技术实践,也不只介绍实践经验。而是把实践与实际联合起来,用实际加深对实践的了解、用实践进步你的技术实际能力。欢送你来关注我的公众号,也请分享给你的好友~~~

linux write文件,关于linux:write文件一个字节后何时发起写磁盘IO相关推荐

  1. Linux查看哪些进程占用较多的cpu、内存和磁盘IO的方法

    linux 查看哪个进程占用CPU和内存 1.ps aux | grep mysql 用这个方法找到进程号 如下:找到mysql的进程号是1651 [root@vm254 ~]# ps aux |gr ...

  2. linux 服务器 ssd,关于linux:搭载固态硬盘的服务器究竟比机械硬盘快多少

    置信大家都晓得固态硬盘(SSD)的劣势在于速度比传统的机械硬盘(HDD)要快,所以当初线上服务器里越来越多看到固态硬盘的呈现.不过作为一个对性能数字宽宏大量的开发,我想更准确地弄明确搭载SSD的服务器 ...

  3. dd命令测试linux磁盘io情况,【LINUX】正确的使用dd进行磁盘读写速度测试

    测试方式:使用dd指令,对磁盘进行连续写入,不使用内存缓冲区,每次写入8k的数据,总共写入20万次,产生1.6G大小的文件. 测试指令:dd if=/dev/zero of=/data01/test. ...

  4. 学习 Linux,101: 使用 vi 编辑文件

    总是可用的编辑器 了解如何使用 vi 编辑器,这款编辑器在大部分 UNIX® 和 Linux® 系统中都存在.本文提供的材料可以帮助您准备 Linux 系统管理员认证 LPI 101 考试,也可以作为 ...

  5. 原linux的字符文件作用,linux特殊字符及其作用大全

    1.通配符 ? 匹配单个字符 * 代表所有字符 [abcd] 匹配[]里任意一个字符.4选1 [a-d] [!abcd]  匹配不含[]里任意一个字符的字符.[^abcd] 2.路径相关 ~     ...

  6. Linux 学习之创建、删除文件和文件夹命令

    今天学习了几个命令,是创建.删除文件和文件夹的,在linux里,文件夹是目录,下面说下我学习的命令. 创建文件夹[mkdir] 一.mkdir命令使用权限 所有用户都可以在终端使用 mkdir 命令在 ...

  7. linux怎么查找文件名带有abc的文件,如何查找一个文件夹里的最新的文件

    如何查找一个文件夹里的最新的文件以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 如何查找一个文件夹里的最新的文件 安装一 ...

  8. linux 下生成docx,linux下创建、删除文件和文件夹命令.docx

    linux下创建.删除文件和文件夹命令.docx 还剩 6页未读, 继续阅读 下载文档到电脑,马上远离加班熬夜! 亲,喜欢就下载吧,价低环保! 内容要点: 学习 Linux 二(创建.删除文件和文件夹 ...

  9. 学习Linux二(创建、删除文件和文件夹命令)

    学习Linux二(创建.删除文件和文件夹命令) 今天学习了几个命令,是创建.删除文件和文件夹的,在linux里,文件夹是目录,下面说下我学习的命令. 创建文件夹[mkdir] 一.mkdir命令使用权 ...

最新文章

  1. 快速傅里叶变换(FFT)算法【详解】
  2. mysql中的索引对查询的影响
  3. 不会python怎么了?靠敏捷BI和数据可视化,照样去阿里腾讯
  4. vscode 格式化json
  5. MySQL 6.子查询
  6. Atitit mac os 版本 新特性 attilax大总结
  7. 字符串的unicode java_Java把字符串转Unicode
  8. win7虚拟机_VMware8虚拟机安装教程
  9. 【SPEA】T100BT 全能型电池测试仪——精度高、智能化
  10. Android 圆形 ImageView
  11. 工作流系统之四十 抄送功能的实现
  12. c语言设计程序实现顺序冒泡_C语言学习 顺序程序设计
  13. 旭日x3派个人配置总结(ubuntu server + xrdp)
  14. 什么是数据库的二级映像
  15. 图像的阈值分割(Optimum Thresholding)
  16. SONY ICX618AL/AQ 电路升级改造----第一章:初步方案确定
  17. 如何降低计算机版本,Win10系统如何降级版本的?回退到Windows 10上一个版本的方法...
  18. Wordpress 网站设计入门3 网页设计和管理
  19. 在html页面中写验证码图片不显示,freemarker页面实现验证码图片不刷新问题求助_html/css_WEB-ITnose...
  20. 【Linux】终端 [root@localhost ~]# 变成 -bash-4.2# 问题及解决

热门文章

  1. C语言约瑟夫报数出圈算法,详解约瑟夫环问题及其相关的C语言算法实现
  2. FM,FMM,deepFFM模型总结,深度排序模型
  3. 基于Django框架实现前后端分离(三)
  4. Chem 3D软件可以改变背景吗
  5. strncmp函数的简单用法
  6. 腾达n4怎么设置虚拟服务器,腾达n4路由器怎么设置
  7. kubernetes cordon原理
  8. uni-app中,文字超出隐藏并显示省略号(实现展开、收起全文)
  9. 如何获取网站的ico图标
  10. 自动化测试学习daytwo(接口自动化概论)