2010-4-6 有关回写

来源:互联网 发布:手机大头贴软件 编辑:程序博客网 时间:2024/05/21 07:10

一、与回写有关参数

/proc/sys/vm/中所有的文件如下所示,其中有一部分是和页面回收相关的,我先分析页面回写机制,然后分析这些变量对回写的影响。

-rw-r--r-- 1 root root 0 Mar 31 23:54 block_dump

-rw-r--r-- 1 root root 0 Mar 31 23:54 dirty_background_ratio

-rw-r--r-- 1 root root 0 Mar 31 23:54 dirty_expire_centisecs

-rw-r--r-- 1 root root 0 Mar 31 23:54 dirty_ratio

-rw-r--r-- 1 root root 0 Mar 31 23:54 dirty_writeback_centisecs

-rw-r--r-- 1 root root 0 Mar 31 23:54 drop_caches

-rw-r--r-- 1 root root 0 Mar 31 23:54 hugetlb_shm_group

-rw-r--r-- 1 root root 0 Mar 31 23:54 laptop_mode

-rw-r--r-- 1 root root 0 Mar 31 23:54 legacy_va_layout

-rw-r--r-- 1 root root 0 Mar 31 23:54 lowmem_reserve_ratio

-rw-r--r-- 1 root root 0 Mar 31 23:54 max_map_count

-rw-r--r-- 1 root root 0 Mar 31 23:54 min_free_kbytes

-rw-r--r-- 1 root root 0 Mar 31 23:54 min_unmapped_ratio

-rw-r--r-- 1 root root 0 Mar 31 23:54 nr_hugepages

-r--r--r-- 1 root root 0 Mar 31 23:54 nr_pdflush_threads

-rw-r--r-- 1 root root 0 Mar 31 23:54 overcommit_memory

-rw-r--r-- 1 root root 0 Mar 31 23:54 overcommit_ratio

-rw-r--r-- 1 root root 0 Mar 31 23:54 page-cluster

-rw-r--r-- 1 root root 0 Mar 31 23:54 panic_on_oom

-rw-r--r-- 1 root root 0 Mar 31 23:54 percpu_pagelist_fraction

-rw-r--r-- 1 root root 0 Mar 31 23:54 swappiness

-rw-r--r-- 1 root root 0 Mar 31 23:54 swap_token_timeout

-rw-r--r-- 1 root root 0 Mar 31 23:54 vfs_cache_pressure

-rw-r--r-- 1 root root 0 Mar 31 23:54 zone_reclaim_mode

    在mm/pag_writeback.c文件中,找到了如下内容,注释表明这就是那些导出到/proc中的参数。

/* The following parameters are exported via /proc/sys/vm */

//下列参数将导出到/proc/sys/vm目录中

/*

 * Start background writeback (via pdflush) at this percentage

 */

//脏背景阈值。在这个百分比的情况下启动background 回写

//指的是回调函数为background_writeout()pdflush进程

int dirty_background_ratio = 10;

/*

 * The generator of dirty data starts writeback at this percentage

 */

//这句不知道怎么翻译

int vm_dirty_ratio = 40;

/*

 * The interval between `kupdate'-style writebacks, in jiffies

 */

//jiffies为单位的,kupdate类型的回写的间隔时间

int dirty_writeback_interval = 5 * HZ;

/*

 * The longest number of jiffies for which data is allowed to remain dirty

 */

//数据允许为脏状态的最长时间(以jiffies为单位)

int dirty_expire_interval = 30 * HZ;

/*

 * Flag that makes the machine dump writes/reads and block dirtyings.

 */

//不知道

int block_dump;

/*

 * Flag that puts the machine in "laptop mode". Doubles as a timeout in jiffies:

 * a full sync is triggered after this time elapses without any disk activity.

 */

//膝上电脑模式

int laptop_mode;

EXPORT_SYMBOL(laptop_mode);

/* End of sysctl-exported parameters */

对这些源代码总结如下表所示:

dirty_background_ratio

背景阈值,脏页比例达到这个比例时,wakeup_pdflush()被调用

vm_dirty_ratio

阈值

dirty_writeback_interval

kupdate类型的回写的间隔时间

dirty_expire_interval

数据允许为脏状态的最长时间

block_dump

laptop_mode

膝上电脑模式(为节电,而减少回写)

    

这些变量依次与/proc/sys/vm/目录中的dirty_background_ratiodirty_ratiodirty_writeback_centisecsdirty_expire_centisecsblock_dumplaptop_mode。此外文件nr_pdflush_threadsnr_pdflush_threadspdflush线程数)相关。

有些变量和文件的名字不一致,不过不要紧,先看看文件kernel/sysctl.c中的内容:

{

.ctl_name = VM_DIRTY_BACKGROUND,

.procname = "dirty_background_ratio",

.data = &dirty_background_ratio,

.maxlen = sizeof(dirty_background_ratio),

.mode = 0644,

.proc_handler = &proc_dointvec_minmax,

.strategy = &sysctl_intvec,

.extra1 = &zero,

.extra2 = &one_hundred,

},

{

.ctl_name = VM_DIRTY_RATIO,

.procname = "dirty_ratio",

.data = &vm_dirty_ratio,

.maxlen = sizeof(vm_dirty_ratio),

.mode = 0644,

.proc_handler = &proc_dointvec_minmax,

.strategy = &sysctl_intvec,

.extra1 = &zero,

.extra2 = &one_hundred,

},

{

.ctl_name = VM_DIRTY_WB_CS,

.procname = "dirty_writeback_centisecs",

.data = &dirty_writeback_interval,

.maxlen = sizeof(dirty_writeback_interval),

.mode = 0644,

.proc_handler = &dirty_writeback_centisecs_handler,

},

{

.ctl_name = VM_DIRTY_EXPIRE_CS,

.procname = "dirty_expire_centisecs",

.data = &dirty_expire_interval,

.maxlen = sizeof(dirty_expire_interval),

.mode = 0644,

.proc_handler = &proc_dointvec_userhz_jiffies,

},

{

.ctl_name = VM_NR_PDFLUSH_THREADS,

.procname = "nr_pdflush_threads",

.data = &nr_pdflush_threads,

.maxlen = sizeof nr_pdflush_threads,

.mode = 0444 /* read-only*/,

.proc_handler = &proc_dointvec,

},

……

{

.ctl_name = VM_LAPTOP_MODE,

.procname = "laptop_mode",

.data = &laptop_mode,

.maxlen = sizeof(laptop_mode),

.mode = 0644,

.proc_handler = &proc_dointvec_jiffies,

.strategy = &sysctl_jiffies,

},

{

.ctl_name = VM_BLOCK_DUMP,

.procname = "block_dump",

.data = &block_dump,

.maxlen = sizeof(block_dump),

.mode = 0644,

.proc_handler = &proc_dointvec,

.strategy = &sysctl_intvec,

.extra1 = &zero,

},

结构定义在文件include/linux/sysctl.h

struct ctl_table 

{

int ctl_name; /* Binary ID */

const char *procname; /* Text ID for /proc/sys, or zero */

void *data;

int maxlen;

mode_t mode;

ctl_table *child;

proc_handler *proc_handler; /* Callback for text formatting */

ctl_handler *strategy; /* Callback function for all r/w */

struct proc_dir_entry *de; /* /proc control block */

void *extra1;

void *extra2;

};

将其总结如下:

procname

data

mode

proc_handler

dirty_background_ratio

dirty_background_ratio

0644

proc_dointvec_minmax

dirty_ratio

vm_dirty_ratio

0644

sproc_dointvec_minmax

dirty_writeback_centisecs

dirty_writeback_interval

0644

dirty_writeback_centisecs_handler

dirty_expire_centisecs

dirty_expire_interval

0644

proc_dointvec_userhz_jiffies

nr_pdflush_threads

nr_pdflush_threads

0444

proc_dointvec

laptop_mode

laptop_mode

0644

proc_dointvec_jiffies

block_dump

block_dump

0644

proc_dointvec

二、定时回写 wb_kupdate()

在系统启动的过程中,start_kernel()函数会调用page_writeback_init()函数,在这个函数中首先要计算出下次启动计时器的时间。page_writeback_init()的代码如下:

/*

 * If the machine has a large highmem:lowmem ratio then scale back the default

 * dirty memory thresholds: allowing too much dirty highmem pins an excessive

 * number of buffer_heads.

 */

//如果机器中的 highmem:lowmem的比例较高,则缩减默认的脏页阈值

void __init page_writeback_init(void)

{

//nr_free_buffer_pages()得到ZONE_DMAZONE_NORMAL中的空闲内存数

//源代码中的注释为:Amount of free RAM allocatable within ZONE_DMA and ZONE_NORMAL

long buffer_pages = nr_free_buffer_pages();

long correction;

//nr_free_pagecache_pages()得到所有的空闲页面数

//源码中的注释为:Amount of free RAM allocatable within all zones

total_pages = nr_free_pagecache_pages();

//计算“调节因子”

correction = (100 * 4 * buffer_pages) / total_pages;

//如果correction < 100,则buffer_pages小于total_pages1/4

if (correction < 100) {

//重置脏页的背景阈值和阈值,使其变小

dirty_background_ratio *= correction;

dirty_background_ratio /= 100;

vm_dirty_ratio *= correction;

vm_dirty_ratio /= 100;

if (dirty_background_ratio <= 0)

dirty_background_ratio = 1;

if (vm_dirty_ratio <= 0)

vm_dirty_ratio = 1;

}

//设置定时器的启动时间是dirty_writeback_interval之后

mod_timer(&wb_timer, jiffies + dirty_writeback_interval);

set_ratelimit();

register_cpu_notifier(&ratelimit_nb);

}

    

通过代码,我们可以发现page_writeback_init()根据ZONE_DMAZONE_LOW中空闲页和系统中所有空闲页的比例可能会重置dirty_background_ratiovm_dirty_ratio,对于时间间隔dirty_writeback_interval没做修改。

对于wb_timer,有static DEFINE_TIMER(wb_timer, wb_timer_fn, 0, 0),即超时回调函数为wb_timer_fnwb_timer_fn()函数定义在mm/page-writeback,c中,整个函数就只有一个if语句:

if (pdflush_operation(wb_kupdate, 0) < 0)

mod_timer(&wb_timer, jiffies + HZ); /* delay 1 second */

    调用pdflush_operation()来激活空闲的pdflush线程,如果返回值为小于0,即如果失败,则1s后再次调用wb_timer_fn()

于是,我们终于看到pdflush_operation()函数了,它完成的工作就是"wake up a pdflush thread, and get it to do some work for you""find a worker thread, and passed your payload to it",即唤醒一个pdflush线程,并设置其回调函数fn,让它执行预想的工作。

整个函数大体的流程是:先从pdflush_list链表中获得一个pdflush_work描述符,然后给描述符中的fn赋值,将其赋为wb_kupdate()最后调用wake_up_process()函数唤醒一个pdflush线程。

    

pdflush线程对应于函数pdflush(),而pdflush()将调用__pdflush()__pdflush()函数首先将自己的线程置入睡眠状态,等待唤醒。唤醒后,它调用回调函数fn完成具体的工作,之后根据系统策略创建新的pdflush线程、结束线程或继续睡眠。

在这个函数中,会用到nr_pdflush_threads变量,不过它只不过是用来和MAX_PDFLUSH_THREADS8)、MAX_PDFLUSH_THREADS2)来比较,以辅助确定是否创建新的pdflush线程等,从而实现机制:"If there have been no idle pdflush instances for 1 second, create a new one.","If the least-recently-went-to-sleep pdflush thread has been asleep for more than one second, terminate a thread."

接下来,转入正题wb_kupdate(),它的函数调用关系如下:

wb_kupdate()

|-- sync_supers()

|-- writeback_inodes()

|  |-- sync_sb_inodes()

|  |  |-- __writeback_single_inode()

|  |  |  |-- __sync_single_inode()

|  |  |  |  |-- do_writepages()

|-- mod_timer()

wb_kupdate()函数先调用sync_supers()将脏的超级块回刷到磁盘,再计算出要回刷的页面数,之后循环调用writeback_inodes()进行回刷工作,每次回刷时最大页面数为MAX_WRITEBACK_PAGES(即1024),当要回刷的页都完成时结束循环。

在这个函数有如下三条赋值语句:

oldest_jif = jiffies - dirty_expire_interval;

start_jif = jiffies;

next_jif = start_jif + dirty_writeback_interval;

oldest_jifstart_jifnext_jif分别是需要回刷的脏页的最晚时间(默认为30s之前)、当前时间、下次回刷的时间(默认为5s之后)。oldest_jif会赋给writeback_control结构wbcolder_than_this字段,即回刷上次回刷之前变脏的脏页。

对于next_jif,在mod_timer之前会有如下if语句:

if (time_before(next_jif, jiffies + HZ))

next_jif = jiffies + HZ;

if (dirty_writeback_interval)

mod_timer(&wb_timer, next_jif);

    

由此可见,如果原定的next_jif1s之后早,则下次启动定时器的时间为1s后,之后如果dirty_writeback_interval不为0,则重启定时器。补充一点的是:在代码中没有找到对dirty_writeback_interval赋值的语句。

writeback_inodes()用来进行回刷工作,它遍历超级块链表,对于sb->s_dirtysb->s_io链表不为空的超级块,调用函数sync_sb_inodes()处理该超级块的所有节点。

sync_sb_inodes()将该超级块sb->s_dirty队列中的索引节点都放入sb->s_io链表中,然后遍历sb->s_io链表,循环处理链表中各个索引节点。

如果wbc->older_than_this不为0,则不处理在此之后变脏的索引节点;如果当前线程是pdflush,则在处理索引节点前测试并设置BDI_pdflush,因为可能会有另一个线程在处理这个索引节点;调用__writeback_single_inode()回写与索引节点有关的脏页,之后入股当前线程是pdflush,则清除BDI_pdflush位。每处理完一个索引节点后,都会调用cond_resched()函数。

__writebace_single_inode()前面几行语句用来处理inode已经被上锁的情况,最后一句是最关键的: return __sync_single_inode()。

__sync_single_inode()函数调用do_writepages()将一个inode对应的mapping中的页回写到磁盘,之后根据情况,将inode节点移入sb->s_dirty、inode_in_use或inode_unused链表。

通过这些分析,我们了解了定时回写页高速缓存的机制。wb_kupdate()涉及到的/pooc/sys/vm中的变量dirty_background_ratiovm_dirty_ratiodirty_expire_intervaldirty_writeback_intervalpdflush线程自己会用到nr_pdflush_threads

三、回写 background_writeout()

 3.1wakeup_pdflush

  3.1.1 回写流程

     wakeup_pdflush()函数在nr_page参数为0的情况下,大致计算出系统中的脏页总数(nr_pages = global_page_state(NR_FILE_DIRTY) +global_page_state(NR_UNSTABLE_NFS),再调用pdflush_operation()唤醒一个pdflush线程,设置其回调函数为background_writeout(),而nr_pages则赋给pdflush_work结构的arg0字段。

background_writeout()的结构比较简单,就是多次调用writeback_inodes()函数,试图每次写1024个脏页,直到min_pagespage已经被写完,且系统中脏页小于阈值。

/*

 * writeback at least _min_pages, and keep writing until the amount of dirty

 * memory is less than the background threshold, or until we're all clean.

 */

static void background_writeout(unsigned long _min_pages)

{

long min_pages = _min_pages;

struct writeback_control wbc = {

.bdi = NULL,

.sync_mode = WB_SYNC_NONE,

.older_than_this = NULL,

.nr_to_write = 0,

.nonblocking = 1,

.range_cyclic = 1,

};

for ( ; ; ) {

//背景阈值?

long background_thresh;

//脏页阈值?

long dirty_thresh;

//获取背景阈值和脏页阈值

get_dirty_limits(&background_thresh, &dirty_thresh, NULL);

//要求要写的页写完了,且系统中脏页小于背景阈值,则停止循环

if (global_page_state(NR_FILE_DIRTY) +

global_page_state(NR_UNSTABLE_NFS) < background_thresh

&& min_pages <= 0)

break;

wbc.encountered_congestion = 0;

wbc.nr_to_write = MAX_WRITEBACK_PAGES; //1024

wbc.pages_skipped = 0;

//尝试写1024个脏页

writeback_inodes(&wbc);

min_pages -= MAX_WRITEBACK_PAGES - wbc.nr_to_write;

//如果页没有写完或跳过了某页,则请求队列可能拥塞

if (wbc.nr_to_write > 0 || wbc.pages_skipped > 0) {

/* Wrote less than expected */

//睡眠

blk_congestion_wait(WRITE, HZ/10);

//这个if的用意是什么?

if (!wbc.encountered_congestion)

break;

}

}

}

在上面的代码中,通过get_dirty_limits()函数对现有的脏页背景阈值和脏阈值进行调整,其代码如下:

//计算出未映射的页面的比例

//NR_FILE_MAPPED是映射的,NR_ANON_PAGES是匿名映射

unmapped_ratio = 100 - ((global_page_state(NR_FILE_MAPPED) +

global_page_state(NR_ANON_PAGES)) * 100) /

total_pages;

//脏阈值

dirty_ratio = vm_dirty_ratio;

//脏阈值不大于未映射页比例的1/2

if (dirty_ratio > unmapped_ratio / 2)

dirty_ratio = unmapped_ratio / 2;

//脏阈值不能小于5%

if (dirty_ratio < 5)

dirty_ratio = 5;

//脏背景阈值

background_ratio = dirty_background_ratio;

//脏背景阈值不大于脏阈值

if (background_ratio >= dirty_ratio)

background_ratio = dirty_ratio / 2;

//脏背景阈值页数 脏阈值页数

background = (background_ratio * available_memory) / 100;

dirty = (dirty_ratio * available_memory) / 100;

tsk = current;

//如果当前进程是释放内存的进程 或 是实时进程 则增加脏背景阈值 和 脏阈值

if (tsk->flags & PF_LESS_THROTTLE || rt_task(tsk)) {

background += background / 4;

dirty += dirty / 4;

}

由此可得,background_writeout()函数借助dirty_background_ratiovm_dirty_ratio变量来确定回写后脏页应该小于的比例background_ratio

  3.1.2 回写时机

定时回写的回写时机不需多说。

wakeup_pdflush()会被do_sync()try_to_free_pages()free_more_memory()调用,do_sync()与现实的系统调用有关,就不多说了。

try_to_free_pages()会被free_more_memory()__alloc_pages()调用。

free_more_memory()会被alloc_page_buffers()__getblk_slow()调用。

现在要做的就是通过这些函数,了解wakeup_pdflush()被调用的“高层语义”。受限于内存管理和回收页框方面的知识,现在还不能进行详细的分析:

1alloc_page_buffers()

这个函数用来为一个页分配缓冲区首部,以形成缓冲区页,其中会调用alloc_buffer_head来为每个缓冲区首部分配内存,如果buffer_head分配失败,则会调用free_more_memory()释放出一些内存,然后再重新尝试。

2__getblk_slow()

    __getblk_slow()在调用grow_buffers()失败时调用free_more_memory()函数。

3__alloc_pages()

函数用来分配内存页,当空闲内存实在太少,调用try_to_free_pages进行内存回收.

    

free_more_memory()try_to_free_pages()都会在内存不够时被调用,他们都会唤醒一个pdflush线程,将页高速缓存中的页写回磁盘,使得包含页高速缓存、缓冲区首部等数据结构的页框变成可释放的。

 3.2balance_dirty_pages_ratelimited

通过调用pdflush_operation()函数激活background_writeout()函数进行回写的地方,除了在wakeup_pdflush()中有一处之外,在balance_dirty_pages()中又有一处。通过这个函数追溯到了balance_dirty_pages_ratelimited()

  3.2.1回写流程

balance_dirty_pages_ratelimited()函数只是balance_dirty_pages_ratelimited_nr()的简单封装。

balance_dirty_pages_ratelimited_nr(mapping, 1);

balance_dirty_pages_ratelimited_nr()在当前的cpu产生速率脏页的速率超过ratelimit的情况下调用balance_dirty_pages()。相关代码如下:

//ratelimit_pages 默认值是32

//After a CPU has dirtied this many pages, balance_dirty_pages_ratelimited

//will look to see if it needs to force writeback or throttling.

ratelimit = ratelimit_pages;

//当系统中的脏页超过阈值时,dirty_exceeded被置位

//如果dirty_exceeded被置位,那么ratelimit将大大减小

//防止进程以32为限定,而使得脏页产生速率过度的超出

if (dirty_exceeded)

ratelimit = 8;

/*

 * Check the rate limiting. Also, we do not want to throttle real-time

 * tasks in balance_dirty_pages(). Period.

 */

preempt_disable();

p =  &__get_cpu_var(ratelimits);

*p += nr_pages_dirtied;

//如果超出速率限制则调用balance_dirty_pages()

if (unlikely(*p >= ratelimit)) {

*p = 0;

preempt_enable();

balance_dirty_pages(mapping);

return;

}

preempt_enable();

balance_dirty_pages()函数会先从正在写的页面所对应的inode所属的文件系统的脏页中回写ratelimit_page + ratelimit_page / 2页(默认48页),之后如果检查系统中的脏页大于阈值,则唤醒一个回调函数为background_writeout()pdflush线程。相关代码如下:

//每次尝试写nr_to_write

//如果写后脏页量小于阈值 或 这次写了nr_to_write48)页 则退出循环

if (nr_reclaimable) {

writeback_inodes(&wbc);

get_dirty_limits(&background_thresh,

  &dirty_thresh, mapping);

nr_reclaimable = global_page_state(NR_FILE_DIRTY) +

global_page_state(NR_UNSTABLE_NFS);

if (nr_reclaimable +

global_page_state(NR_WRITEBACK)

<= dirty_thresh)

break;

pages_written += write_chunk - wbc.nr_to_write;

if (pages_written >= write_chunk)

break; /* We've done our duty */

}

……

//如果是laptop_mode,则趁此回写系统的脏页

//如果不是laptop_mode模式,则在脏页超过脏背景阈值时,启动pdflush线程进行回刷

if ((laptop_mode && pages_written) ||

     (!laptop_mode && (nr_reclaimable > background_thresh)))

pdflush_operation(background_writeout, 0);

这里出现了laptop_mode变量,它对回刷的影响很明显,laptop_mode && pages_written条件中的pages_written在调用writeback_inodes()函数后脏页还较多的情况就会被赋值,于是系统抓住这个机会开始回写。

  3.2.2回写时机

对于函数balance_dirty_pages_ratelimited_nr(),源代码的注释为''Processes which are dirtying memory should call in here once for each page which was newly dirtied. "使页变脏的所有写缓存的操作都要调用balance_dirty_pages_ratelimited_nr()。然后,如上所说就会检查要不要刷与索引节点相关的文件系统的脏页和要不要回刷系统的脏页。

balance_dirty_pages_ratelimited_nr()函数会被三个函数调用,这些函数是:reiserfs_file_write()balance_dirty_page_ratelimited()sys_msync(),其中sys_msync()与系统调用有关,reiserfs_file_write()reiserfs文件系统相关,对此没有做分析。

balance_dirty_page_ratelimited()函数的调用有5处,3处和ntfs有关,1处事在pipe_to_file()中调用,还有一处就是generic_file_buffered_write()

generic_file_buffered_write()是通用的文件系统对磁盘进行些操作时要调用的函数,它写完缓存后,就会调用balance_dirty_page_ratelimited()

四、参数对回刷的影响

从上面的分析来讲,dirty_background_ratiovm_dirty_ratio可以影响回刷时脏页的比例,但系统对它也会做一些调整;dirty_writeback_centisecs会影响定时回刷的时间间隔,但是系统对回刷的时间间隔也会有所调整,此外如果dirty_writeback_centisecs0,则会停止定时回刷;dirty_expire_centisecs函数是页允许保存脏状态的最长时间,系统在使用时没有对其做修改。

除了直观的影响之外,还可以看下相应的回调函数。ctl_table结构中,proc_handler读写数据处理的回调函数,该函数处理数据的输入和输出,它在通过/proc文件系统读写数据时被使用。对应于每个参数有:

procname

proc_handler

dirty_background_ratio

proc_dointvec_minmax

dirty_ratio

sproc_dointvec_minmax

dirty_writeback_centisecs

dirty_writeback_centisecs_handler

dirty_expire_centisecs

proc_dointvec_userhz_jiffies

nr_pdflush_threads

proc_dointvec

laptop_mode

proc_dointvec_jiffies

block_dump

proc_dointvec

    

对于nr_pdflush_threads,它是只读的,就不多说了。

函数proc_dointvec_minmax()proc_dointvec_userhz_jiffies()proc_dointvec()proc_dointvec_jiffies()都是通用的函数,在写入后没有特别的处理。

需要注意的是dirty_writeback_centisecsdirty_expire_centisecs中时间间隔的单位是1/USER_HZ秒(USER_HZ100,而HZ=CONFIG_HZ,在配置文件.configCONFIG_HZ=250),函数proc_dointvec_userhz_jiffies()会对其进行转化。

proc_dointvec_minmax()会对写入的数据进行调整,使之在ctl_table结构的.extra1.extra2之间,对于dirty_background_ratiodirty_ratio这两个值分别是zeroone_hundred

dirty_writeback_centisecs_handler()函数在写入数据后,会根据新的dirty_writeback_interval重新启动定时器,如果dirty_writeback_interval0的话,则关闭定时器,即禁止定时回刷机制。

对于block_dumplaptop_mode对回写的影响,暂时还没有看见,于是用"cs f s XXX"进行查找。

对于block_dump,用到这个变量的地方有两个:submit_bio__mark_inode_dirty()。个人理解是:如果设置了block_dump,则会输出一些信息。

submit_bio()中相关代码如下:

if (unlikely(block_dump)) {

char b[BDEVNAME_SIZE];

printk(KERN_DEBUG "%s(%d): %s block %Lu on %s/n",

current->comm, current->pid,

(rw & WRITE) ? "WRITE" : "READ",

(unsigned long long)bio->bi_sector,

bdevname(bio->bi_bdev,b));

}

__mark_inode_dirty()中相关代码如下:

if (unlikely(block_dump)) {

struct dentry *dentry = NULL;

const char *name = "?";

if (!list_empty(&inode->i_dentry)) {

dentry = list_entry(inode->i_dentry.next,

    struct dentry, d_alias);

if (dentry && dentry->d_name.name)

name = (const char *) dentry->d_name.name;

}

if (inode->i_ino || strcmp(inode->i_sb->s_id, "bdev"))

printk(KERN_DEBUG

       "%s(%d): dirtied inode %lu (%s) on %s/n",

       current->comm, current->pid, inode->i_ino,

       name, inode->i_sb->s_id);

}

对于laptop_mode,用到这个变量的地方有七处:end_that_request_last()do_sync()xfs_fs_sync_super()balance_dirty_pages()laptop_io_completion()try_to_free_pages()balance_pgdat()

原创粉丝点击