pdflush后台例程

15.4    pdflush后台例程

由于页高速缓存的缓存作用,写操作实际上会被延迟。当页高速缓存中的数据比后台存储的数据更新时,那么该数据就被称做脏数据。在内存中累积起来的脏页最终必须被写回磁盘。在以下两种情况发生时,脏页被写回磁盘:

·当空闲内存低于一个特定的阈值时,内核必须将脏页写回磁盘,以便释放内存。

·当脏页在内存中驻留时间超过一个特定的阈值时,内核必须将超时的脏页写回磁盘,以确保脏页不会无限期地驻留在内存中。

上面两种工作的目的完全不同。实际上,在老内 核中,这是由两个独立的内核线程(请看后面章节)分别完成的。但是在2.6内核中,由一群内核线程—pdflush后台回写例程—统一执行两种工作。说 pdflush是“dirty page flush”的缩写是不正确的,不用去管这个让人混淆的名称,我们来看看这两个目标是如何具体实现的。

首先,pdflush线程在系统中的空闲内存低于一 个特定的阈值时,将脏页刷新回磁盘。该后台回写例程的目的在于在可用物理内存过低时,释放脏页以重新获得内存。特定的内存阈值可以通过 dirty_background_ratio sysctl系统调用设置。当空闲内存比阈值:dirty_ background_ratio还低时,内核便会调用函数wakeup_bdflush()唤醒一个pdflush线程,随后pdflush线程进一步 调用函数background_writeout()开始将脏页写回磁盘。函数background_ writeout()需要一个长整型参数,该参数指定试图写回的页面数目。函数background_writeout()会连续地写出数据,直到满足以 下两个条件:

·已经有指定的最小数目的页被写出到磁盘。

·空闲内存数已经回升,超过了阈值dirty_background_ratio。

上述条件确保了pdflush操作可以减轻系统中内存不足的压力。回写操作不会在达到这两个条件前停止,除非pdflush写回了所有的脏页,没有剩下的脏页可再被写回了。

了满足第二个目标,pdflush后台例程会被周期 性唤醒(和空闲内存是否过低无关),将那些在内存中驻留时间过长的脏页写出,确保内存中不会有长期存在的脏页。如果系统发生崩溃,由于内存处于混乱之中, 所以那些在内存中还没来得及写回磁盘的脏页就会丢失,所以周期性同步页高速缓存和磁盘非常重要。在系统启动时,内核初始化一个定时器,让它周期地唤醒 pdflush线程,随后使其运行函数wb_kupdate()。该函数将把所有驻留时间超过百分之dirty_expire_centisecs秒的脏 页写回。然后定时器将再次被初始化为百分之dirty_expire_ centisecs秒后唤醒pdflush线程。总而言之,pdflush线程周期地被唤醒并且把超过特定期限的脏页写回磁盘。

系统管理员可以在/proc/sys/vm中设置回写相关的参数,也可以通过sysctl系统调用设置它们。表15-1列出了与pdflush相关的所有可设置变量。

15.4.1    膝上型电脑模式

膝上型电脑模式是一种特殊的页回写策略,该策略主要 意图是将硬盘转动的机械行为最小化,允许硬盘尽可能长时间的停滞,以此延长电池供电时间。该模式可通过/proc/sys/vm/laptop_mode 文件进行配置,通常,上述配置文件内容为零,也就是说膝上型电脑模式关闭,如果需要启用膝上型电脑模式,则向配置文件中写入1。

膝上型电脑模式的页回写行为与传统方式相比只有一处变化。除了当缓存中的页面太旧时要执行回写脏页以外,pdflush还会找准磁盘运转的时机,把所有其他的物理磁盘I/O、刷新脏缓冲等统统写回到磁盘,以便保证不会专门为了写磁盘而去主动激活磁盘运行。

上述回写行为变化要求dirty_expire_centisecs和dirty_writeback_centisecs两阈值必须设置的更大,比如10分钟。因为磁盘运转并不很频繁,所以用这样长的回写延迟才能保证膝上型电脑模式可以等到磁盘运转机会写入数据。

多数Linux发布板会在电脑接上电池或拔掉电池时,自动开启或禁止膝上型电脑模式以及其他需要的pdflush可调节开关。因此机器可在使用电池电源时自动进入膝上型电脑模式;而在插上交流电源时恢复到常规的页回写模式。

15.4.2              bdflush和kupdated

在2.6版本前,pdflush线程的工作是分别由 bdflush和kupdated两个线程共同完成。当可用内存过低时,bdflush内核线程在后台执行脏页回写操作。与pdflush一样,它也有一 组阈值参数,当系统中空闲内存消耗到特定阈值以下时,bdflush线程就被wakeup_bdflush()函数唤醒。

bdflush和pdflush之间有两个主要区 别。第一个区别在下一节还会谈到,系统中只有一个bdflush后台线程,而pdflush线程的数目却可以动态变化;第二个区别是bdflush线程基 于缓冲,它将脏缓冲写回磁盘,相反,pdflush基于页面,它将整个脏页写回磁盘。当然,页面可能包含缓冲,但是实际I/O操作对象是整页,而不是块。 因为页在内存中是更普遍和普通的概念,所以管理页相比管理块要简单。

因为只有在内存过低和缓冲数量过大 时,bdflush例程才刷新缓冲,所以kupdated例程被引入,以便周期地写回脏页。它和pdflush线程的wb_kupdate()函数提供同 样的服务。bdflush和kupdate内核线程现在完全被pdflush线程取代了。

15.4.3              避免拥塞的方法:使用多线程

使用bdflush线程最主要的一个缺点就 是:bdflush仅仅包含了一个线程,因此很有可能在页回写任务很重时,造成拥塞。这是因为单一的线程有可能堵塞在某个设备的已拥塞请求队列上(正在等 待将请求提交给磁盘的I/O请求队列),而其他设备的请求队列却没法得到处理。如果系统有多个磁盘和较强的处理能力,内核应该能使得每个磁盘都处于忙状 态。不幸的是,即使还有许多数据需要回写,单个的bdflush线程也可能会堵塞在某个队列的处理上,不能使所有磁盘都处于饱和的工作状态。原因在于磁盘 的吞吐量是有限的—不幸的是它实在太小。正是因为磁盘的吞吐量很有限,所以如果只有惟一线程执行页回写操作,那么这个线程很容易苦苦等待对一个磁盘上的操 作。为了避免出现这种情况,内核需要多个回写线程并发执行,这样单个设备队列的拥塞就不会成为系统瓶颈了。

2.6内核通过使用多个pdflush线程来解决上述问题。每个线程可以相互独立地将脏页刷新回磁盘,而且不同的pdflush线程处理不同的设备队列。

通过一个简单的算法,线程的数目可以根据系统的运行 时间进行调整。如果所有已存在的pdflush线程都已持续工作1秒以上,内核就会创建一个新的pdflush线程。线程数量最多不能超过 MAX_PDFLUSH_THREADS—默认值是8。相反,如果一个pdflush线程睡眠超过1秒,内核就会终止该线程。线程的数量最少不得小于 MIN_PDFLUSH_THREADS—默认值是2。pdflush线程数量取决于页回写的数量和拥塞情况,动态调整。如果所有存在的pdflush线 程都忙着写回数据,那么一个新线程就会被创建,确保不会出现一个设备队列处于拥塞状态,而其他设备队列却在等待—而不是接收—回写数据的情况。如果堵塞消 除,pdflush线程的数量便会自动减少,以便节约内存。

这种方式看起来很理想,但是如果每一个 pdflush线程都挂起在同一个堵塞的队列上会怎么样呢?在这种情况下,多个pdflush线程的性能并不会比单个线程提高多少,反而会造成严重的内存 浪费。为了克服这种负面影响,pdflush线程利用了拥塞避免策略,它们会积极地试图写回那些不属于拥塞队列的页面。这样一来,pdflush线程通过 分派回写工作,阻止多个线程在同一个忙设备上纠缠。所以pdflush线程很“忙”—此时一个新线程会被创建—它们是真正地繁忙。

由于页回写性能的提高,以及pdflush线程的引入,2.6内核相比以前的内核,能够使更多磁盘饱和工作,即使处于繁重负载的情况下,pdflush线程也可以让多个磁盘保持高吞吐量。

留下评论

邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据