前一阵子公司一部门有人叫帮忙调查,说他们write系统调用基本上是个位数微秒就返回,或者说几十us,但偶尔出现几次write系统调用达到几百毫秒和情况。大家都知道,通过vfs进行write,都是写写到page cache中,然后内核线程再定期同步到磁盘。写到内存应该是很快的才对。刚开始,我做了许多设想,1)磁盘IO太重,内存中的脏数据达到一定比率后write必须同步写到磁盘;2)那些耗时长的write是使用direct io,绕过了page cache;3、刚刚好write一个page时,read也在读同一page,那个page被lock了,write要等它。后来每一种假设又都被自己推翻了。以下是通过vfs进行write系统调用的图:

前几天,使用systemtap进行一步步的定位,最后把可疑的点锁定在__block_write_begin这个函数中(fs/buffer.c)。

先说明下这个函数是干什么的。generic_perform_write函数是把数据写入磁盘的主要处理函数。它先调用address_space_operations. write_begin(其中会调用我们上文提到的__block_write_begin函数),接下来把数据拷贝到上一步分配出来的page中,最后调用address_space_operations.write_end。上面所说的write_begin和write_end会具体根据不同的file system调用不同的函数,拿ext4来说,它的函数如下(有dellay allocation的话):

static const struct address_space_operations ext4_da_aops = {

         .readpage                 = ext4_readpage,

         .readpages               = ext4_readpages,

         .writepage                = ext4_writepage,

         .writepages              = ext4_writepages,

         .write_begin            = ext4_da_write_begin,

         .write_end                = ext4_da_write_end,

         .bmap                        = ext4_bmap,

         .invalidatepage                = ext4_da_invalidatepage,

         .releasepage            = ext4_releasepage,

         .direct_IO                 = ext4_direct_IO,

         .migratepage           = buffer_migrate_page,

         .is_partially_uptodate  = block_is_partially_uptodate,

         .error_remove_page      = generic_error_remove_page,

};

可以看到,ext4的write_begin是ext4_da_write_begin。Ext4_da_write_begin会先分配一个页框,然后调用__block_write_begin分配缓冲区(为什么要分配缓冲区这里就不说明了)。那么,__block_write_begin分配缓冲区而已,为什么有时候要耗时那么长呢?先看看代码:

int __block_write_begin(struct page *page, loff_t pos, unsigned len,

get_block_t *get_block)

{

  ......

  if (!buffer_uptodate(bh) && !buffer_delay(bh) &&

    !buffer_unwritten(bh) &&

     (block_start < from || block_end > to)) {

  ll_rw_block(READ, 1, &bh); //这里指定read操作,提交BIO

  *wait_bh++=bh;

  }

......

/*

* If we issued read requests - let them complete.

*/

  while(wait_bh > wait) { //这里等待io操作完成

  wait_on_buffer(*--wait_bh);

  if (!buffer_uptodate(*wait_bh))

  err = -EIO;

  }

}

  

write的基本单位是块(由磁盘文件系统定义,默认为4K)。这样的话,page cache中一个page(刚好也为4K)刚好就是一个块。比如说write的地址是512,长度是20,page cache就分配了一个能写 0~4095的页page frame,这个page frame刚好对应一个磁盘块,假如把要write的数据拷贝到这个page frame的 512~531,这样的话0~511和532~4095是空的。但是下次write back的时候会把整块数据都写入磁盘,所以需要把这一整块的数据都先从磁盘中读出来,写入page cache中,以防止0~511和532~4095被误写。这个读操作应该就是耗时长的原因了。

write 系统调用耗时长的原因的更多相关文章

  1. redis 间断性耗时长问题解决

    我发现开发项目用的redis 隔一两分钟就出现 耗时问题,长达五秒.一开始以为是 redis 服务器不稳定,但运维测试发现redis稳定的,在高并发下最大耗时也就只有100毫秒左右,怎么也不可能达到5 ...

  2. [WCF] - 访问任意方法耗时长问题之解决

    问题 访问 WCF 任意方法耗时都很长(15s+) 原因 当执行语句 log4net.Config.XmlConfigurator.Configure(); 时需要连接到 log4net 对应的数据库 ...

  3. Microsoft Dynamics CRM 4.0导入组织(Import Organization)时间过长的原因总结

    952934    How to move the Microsoft Dynamics CRM 4.0 deployment http://support.microsoft.com/default ...

  4. SSH远程连接连接其他主机,等待时间过长的原因。

    ssh远程连接登录到其他主机,输入登录用户名,等待时间很长时间,然后才出现输入密码的提示.导致这样时间过长,太慢了的原因有两个.(1)当使用ssh远程登录到某个IP时,这个IP的主机系统会读取/etc ...

  5. wrHDL编译中软核代码初始化及编译耗时长的问题

    问题的提出整个WR的ISE工程比较大,编译时间很长,导致开发效率低.通过分析发现,ISE在综合的时候大量的时间都花在了初始化DPRAM上.调研发现Xilinx提供了BMM文件和DATA2MEM工具,可 ...

  6. file_get_contents微信头像等待时间过长的原因

    UPDATE 2016/05/13 stackoverflow上的解决方法:http://stackoverflow.com/questions/3629504/php-file-get-conten ...

  7. pycharm索引index时间很长的原因

    pycharm进行索引index的目的时代码自动补全,当引入新的插件时,就会增加索引时间,插件越多,索引时间越长 没有好的解决办法,除非增加硬件:或者不使用代码自动补全功能

  8. 查看耗时长,CPU 100% 的SQL

    [session_id], [request_id], [start_time] AS '开始时间', [status] AS '状态', [command] AS '命令', dest.[text] ...

  9. saltstack执行state.sls耗时长的坑

    一直用的 jenkins + saltstack 自动化构建发布项目,一共也就不超过20台服务器,奈何运行时间越来越慢,并且负载越来越高(这里大部分都是使用state模块),但是不用state模块效率 ...

随机推荐

  1. Https之秘钥交换过程分析

    一.概念回顾 A <------M------> B场景:A.B两个人之间通讯,A传输信息M给B,假定是在不安全的通路上传输. 1.明文传输 被中间人C拦截下来,可以随意篡改A发送给B的消 ...

  2. web安全学习方向~两图胜千言~~

  3. Salesforce学习第一天

    好久没有在博客园发布学习博客了,开学事情多,奇葩心思多嘛,谅解.现在在一家公司实习Salesforce开发,每天都在看英文文档,然后学着操作,只可惜没人能培训下,学习起来进度比较慢.英文的文档看的思绪 ...

  4. 使用Node.js快速搭建简单的静态文件服务器

    做前端有时会采用一些复杂框架,在文件系统中直接打开页面(用file:///方式打开),往往会报跨域的错,类似于“XMLHttpRequest cannot load ...(文件名). Cross o ...

  5. 【MSDN_C#】C#版本介绍

    C# 1,Visual Studio .NET 2002 首次发布 C# 1.1,Visual Studio .NET 2003 #line 杂注和 xml 文档注释 C# 2,Visual Stud ...

  6. Zabbix 触发器函数方法整理

    函数介绍 abschange 参数:忽略 支持类型:float,int,str,text,log 作用:返回最近获得的值与之前获得值差的绝对值,对于字符串类型:0表示相等,1表示不同 avg 参数:秒 ...

  7. 照着官网来安装openstack pike之keystone安装

    openstack基础环境安装完成后,现在开启安装keystone服务(在控制节点上执行下面所有操作) 1.为keystone创建数据库 mysql -u root -p MariaDB [(none ...

  8. git推送到github报错:error: The requested URL returned error: 403 Forbidden while accessing https://github.com

    最近使用git命令从github克隆仓库到版本,然后进行提交到github时报错如下: [root@node1 git_test]# git push origin mastererror: The ...

  9. STL学习笔记(不定期更新)

    algorithm *1.sort() 用法:sort(数组名,名+长度(,cmp)); int cmp(T a,T b)//T是要排序的a,b的类型,也可以是结构体中任意成员变量 { return ...

  10. Linux文件系统学习(一)之相关概念⭐⭐⭐

    “一切皆是文件”是 Unix/Linux 的基本哲学之一.不仅普通的文件,目录.字符设备.块设备.套接字等在 Unix/Linux 中都是以文件被对待:它们虽然类型不同,但是对其提供的却是同一套操作界 ...