[转载] 写mmap内存和文件产生几百ms延迟原因

原文: http://weibo.com/p/1001603830912709174661

写mmap内存和文件产生几百ms延迟原因

2015年4月12日 21:10 阅读 4274

最近看到一个bug介绍,作者花了4个月追踪定位,发现jvm统计会造成垃圾回收过程停顿好几百ms。jvm统计信息会写到一片内存区域中,该区域mmap到/tmp下的文件。

这个Bug的详细情况见下面的链接:

The Four Month Bug: JVM statistics cause garbage collection pauses
链接: http://www.tuicool.com/articles/rime6bV,原文链接:http://www.tuicool.com/articles/goto?id=rime6bV

我们知道,linux内核通过page cache加速对块设备的读写访问,应用程序写文件时,写到page cache,即内核中就返回,由后台的flush进程将数据写回到块设备中。而对块设备上的文件访问有两种方式,一种是标准的文件访问方式,open/read/write/close,另一种是通过mmap将文件的一部分映射到内存区域中,程序直接读取对应的内存区域。

那这样的话,修改mmap对应的内存,不会产生停顿几百ms的情况。那实际情况却是产生停顿,为什么呢?

我把这个问题发到部门内部邮件组,有位同事找到下面的信息:

http://www.evanjones.ca/linux-write-caching.html
可能要FQ,我摘抄下:……

. While the percentage of dirty pages is less than dirty_background_ratio (default: 10% on my system), then dirty pages stay in memory until they are older than dirty_expire_centisecs (default: 30 seconds). The pdflush kernel process wakes up every dirty_writeback_centisecs to flush these expired pages out.
. If a writing process dirties enough pages that the percentage rises above dirty_background_ratio, then it proactively wakes pdflush to start writing data out in the background.
. If the percentage of dirty pages rises above dirty_ratio (default: 20% on my system), then the writing process itself will synchronously write pages out to disk. This puts the process in "uninterruptable sleep" (indicated by a D in top). The CPU will be shown in the "iowait" state. This is actually idle time: if there were processes that needed CPU, they would be scheduled to run.
. The percentages are of the total reclaimable memory (free + active + inactive from /proc/meminfo). On a 32-bit system, the "high memory" region is excluded if vm_highmem_is_dirtyable is 0 (default).
……

从上面的第3条,可以看出,当脏页比例超过dirty_ratio时,进程再生成脏页时,由异步改成同步操作,会等待一定比例的脏页写回到块设备才返回。

搜索内核代码,发现进程写入数据后,都会调用函数balance_dirty_pages,这个函数在文件mm/page-writeback.c中,详细的代码调用路径就不再例举了。

下面看内核版本2.6.32.61对应的balance_dirty_pages,关键代码如下:
 
unsigned long pause = 1; /* 初始的阻塞时间是1ms */
for(;;) {
          writeback_inodes_wbc(&wbc);  /* 向块设备发起写回数据请求,不会阻塞 */
         __set_current_state(TASK_INTERRUPTIBLE);

io_schedule_timeout(pause); /* 让出cpu,等待pause之后才继续 */

/*
           * Increase the delay for each loop, up to our previous
           * default of taking a 100ms nap.
           * 每循环一次,等特时间加倍,最长达到100ms */
           */       
          pause <<= 1;
          if (pause > HZ / 10)
                  pause = HZ / 10;
}

从上面的代码可以看出,写入数据进程可能会多次等待,直到脏数据比例满足要求才退出,最长的一次等待时间会达到100ms。

看来,当脏数据过多,而块设备写入能力跟不上的时候,内核会强制挂起写入数据的进程,减慢数据写入的速度,但这样程序往块设备中写入数据就不是实时的了,而且时延还不可控,高达几百ms是很正常的,而且别的进程会受写数据大户的影响,也不是公平的。

除此之外,我觉得还有另外的问题。本来由后台flusher进程集中写入,这样块设备吞吐量能够达到最大。而这样做,很多请求都发起写入请求,会打乱块设备的计划,对硬盘来说,磁头会产生比较多的seek,这样块设备吞吐能力会变差,这只会让块设备写入数据的速度更加跟不上数据写入的速度。另外,多个进程同时去操作写入队列,也会产生很多锁争用的情况。

接着看内核3.10.73的代码,这个对应centos7,balance_dirty_pages的跟时延有关的代码如下:

for(;;) {

__set_current_state(TASK_KILLABLE);
     io_schedule_timeout(pause);

}

内核3的代码有变化,pause时间在10ms到200ms之间,动态计算出来的,这个算法有点复杂,没有看太懂,有机会再细看。另外,进程不会发起请回数据请求,只会挂起一段时间,以减慢写入数据的速度。

看来,内核3在脏页比例高的情况下,进程不会发起写回请求,这样就不会影响块设备的吞吐量。但是节流措施依然存在,这样进程写入数据时,会同样被挂起,而产生比较大的时延。

简单梳理完了代码,对内核在写文件方面进行节流的来龙去脉还是不是很清楚,接着查了一下相关的文档,如下,供有兴趣的同学进一步追查。

Dynamic writeback throttling,链接:http://lwn.net/Articles/405076/
No-I/O dirty throttling,链接:http://lwn.net/Articles/456904/

从上面的文档可以看出,intel的吴峰光提交了这方面的patch。内核开发者对其中的算法也表示太复杂,看来,不仅是我一个人的感觉。呵呵。

2012 Linux Storage, Filesystem, and Memory Management Summit - Day 1,链接:http://lwn.net/Articles/490114/

从上面的文档中,看到stable pages也会引起写入停顿,这是另外一种原因。正准备细查,后来发现网上已经有现成的介绍,如下:

写mmap内存变慢的原因,链接:http://www.360doc.com/content/12/0309/10/2459_192929550.shtml
mmap internals,链接:http://blog.chinaunix.net/uid-20662820-id-3873318.html

接下来需要关注的一个问题是,跟linux容器的隔离有关,在一个容器中的某个进程大量写入文件,别的容器的进程写入文件数据时会不会因此而被挂起。初步感觉,容器之间会相互影响,这个需要进一步的确认。

时间: 2025-01-22 13:34:11

[转载] 写mmap内存和文件产生几百ms延迟原因的相关文章

python标准库基础之mmap:内存映射文件

#作用:建立内存映射文件而不是直接读取内容文本信息内容:如下(名称是text.txt) Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec egestas, enim et consectetuer ullamcorper, lectus ligula rutrum leo, a elementum elit tortor eu quam. Duis tincidunt nisi ut ante. Nulla facil

linux mmap 内存映射【转】

转自:http://blog.csdn.net/xyyangkun/article/details/7830313 [-] mmap vs readwritelseek mmap vs malloc mmap共享内存进程通信 总结 http://www.perfgeeks.com/?p=723 mmap() vs read()/write()/lseek() 通过strace统计系统调用的时候,经常可以看到mmap()与mmap2().系统调用mmap()可以将某文件映射至内存(进程空间),如此

内存映射文件原理探索(转载)

转载:http://blog.chinaunix.net/uid-20761674-id-3072683.html 一直都对内存映射文件这个概念很模糊,不知道它和虚拟内存有什么区别,而且映射这个词也很让人迷茫,今天终于搞清楚了...下面,我先解释一下我对映射这个词的理解,再区分一下几个容易混淆的概念,之后,什么是内存映射就很明朗了. 原理 首先,"映射"这个词,就和数学课上说的"一一映射"是一个意思,就是建立一种一一对应关系,在这里主要是只 硬盘上文件 的位置与进程

第二十天:mmap内存映射

可以说,一天的时间都在了解内存映射mmap这个函数,冯诺依曼结构中表示运算器不能直接对硬盘上的文件进行操作.mmap函数的功能就是将文件映射到某一段内存中,然后操作内存就相当与操作文件.这样的话对文件操作更加方便.mamp函数的定义如下:void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offsize);一共有六个参数,参数算是比较多了.第一个表示映射内存的起始地址,如果为NULL,那么操作系统会自动的找到空

NET 4 中 内存映射文件

原文链接 : http://blogs.msdn.com/salvapatuel/archive/2009/06/08/working-with-memory-mapped-files-in-net-4.aspx 预备知识 : 本文需要你对 OS 内存管理有一定了解. 我想探索下即将到来的 .NET 4 中一些与众不同的新特性,而不是已被大众所熟知的动态类型.协变与逆变等特性.出于对性能增强的喜爱,接下来俺将发表几篇新特性的博文. 内存映射文件对于托管世界的开发人员来说,似乎就像是火星人一样陌生

共享内存之——mmap内存映射

共享内存允许两个或多个进程共享一给定的存储区,因为数据不需要来回复制,所以是最快的一种进程间通信机制.共享内存可以通过mmap()映射普通文件 (特殊情况下还可以采用匿名映射)机制实现,也可以通过systemV共享内存机制实现.应用接口和原理很简单,内部机制复杂.为了实现更安全通信,往往还与信号灯等同步机制共同使用. 这一篇详解mmap内存文件映射原理及其案例,system V共享内存 以及他们的区别将在后面的随笔中讨论. 非原创,内容源于互联网 mmap内存文件映射 一.传统文件访问 unix

内存映射文件 修改以及保存文件

/**************************************************************** // Function : 内存映射文件进行文件同步操作的例子,转载请注明出处 // Author : Lthis // Create : 2015-2-1 11:07:50 // Checked : Lthis 2015-2-1 ****************************************************************/ #i

内存映射文件处理大文件

先说结论:使用内存映射文件来处理大文件可以提高效率. 为什么呢? 我们先来看看如果不使用内存映射文件的处理流程是怎样的,首先我们得先读出磁盘文件的内容到内存中,然后修改,最后回写到磁盘上.第一步读磁盘文件是要经过一次系统调用的,它首先将文件内容从磁盘拷贝到内核空间的一个缓冲区,然后再将这些数据拷贝到用户空间,实际上是两次数据拷贝.第三步回写也一样也要经过两次数据拷贝. 所以我们基本上会有四次数据的拷贝了,因为大文件数据量很大,几十GB甚至更大,所以拷贝的开销是非常大的. 而内存映射文件是操作系统

Linux的mmap内存映射机制解析

在讲述文件映射的概念时,不可避免的要牵涉到虚存(SVR 4的VM).实际上,文件映射是虚存的中心概念, 文件映射一方面给用户提供了一组措施,好似用户将文件映射到自己地址空间的某个部分,使用简单的内存访问指令读写文件:另一方面,它也可以用于内核的基本组织模式,在这种模式种,内核将整个地址空间视为诸如文件之类的一组不同对象的映射.中的传统文件访问方式是,首先用open系统调用打开文件,然后使用read, write以及lseek等调用进行顺序或者随即的I/O.这种方式是非常低效的,每一次I/O操作都