您的位置:首页 > 其它

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

2015-04-17 09:06 267 查看
原文: 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容器的隔离有关,在一个容器中的某个进程大量写入文件,别的容器的进程写入文件数据时会不会因此而被挂起。初步感觉,容器之间会相互影响,这个需要进一步的确认。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐