当前位置: 首页 > news >正文

Linux中页面写回初始化page_writeback_init函数实现

页面写回初始化函数page_writeback_init

void __init page_writeback_init(void)
{long buffer_pages = nr_free_buffer_pages();long correction;total_pages = nr_free_pagecache_pages();correction = (100 * 4 * buffer_pages) / total_pages;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;}mod_timer(&wb_timer, jiffies + (dirty_writeback_centisecs * HZ) / 100);set_ratelimit();register_cpu_notifier(&ratelimit_nb);
}

函数整体功能

这个函数用于初始化Linux内核的页面写回子系统,根据系统内存情况调整脏页写回参数,并启动定时写回机制

代码分段详解

第一段:函数定义

void __init page_writeback_init(void)
  • void: 无返回值
  • __init: 初始化函数宏,表示这个函数在内核初始化后会被释放
  • page_writeback_init: 页面写回初始化函数

第二段:变量声明和初始页面计数

        long buffer_pages = nr_free_buffer_pages();long correction;total_pages = nr_free_pagecache_pages();

获取缓冲区页面数量:

long buffer_pages = nr_free_buffer_pages();
  • nr_free_buffer_pages(): 内核函数,返回可用的缓冲区页面数量
  • 缓冲区页面用于文件系统缓存等操作

声明校正因子变量:

long correction;
  • 用于存储内存调整的校正因子

获取总页面缓存数量:

total_pages = nr_free_pagecache_pages();
  • nr_free_pagecache_pages(): 返回页面缓存中的总空闲页面数
  • total_pages: 全局变量,存储系统总页面数

第三段:计算校正因子

        correction = (100 * 4 * buffer_pages) / total_pages;
  • correction = (100 * 4 * buffer_pages) / total_pages: 计算内存校正因子
  • 公式含义: 校正因子 = (100 × 4 × 缓冲区页面) / 总页面
  • 为什么要×4: 期望比例是1/4
  • 为什么要×100: 为了使用整数运算避免浮点数,保持精度
  • 作用: 根据缓冲区内存占总内存的比例来调整写回参数

第四段:应用校正因子

        if (correction < 100) {dirty_background_ratio *= correction;dirty_background_ratio /= 100;vm_dirty_ratio *= correction;vm_dirty_ratio /= 100;

条件检查:

if (correction < 100)
  • 只在校正因子小于100时进行调整
  • 如果校正因子≥100,说明缓冲区内存足够,使用默认参数

调整后台脏页比率:

dirty_background_ratio *= correction;
dirty_background_ratio /= 100;
  • dirty_background_ratio: 全局变量,后台脏页比率阈值
  • 当脏页比例超过此值时,内核开始在后台写回脏页
  • 调整公式:新比率 = 原比率 × 校正因子 / 100

调整虚拟内存脏页比率:

vm_dirty_ratio *= correction;
vm_dirty_ratio /= 100;
  • vm_dirty_ratio: 全局变量,系统脏页比率阈值
  • 当脏页比例超过此值时,应用程序可能会被阻塞以等待写回完成
  • 调整公式同上

第五段:边界检查

                if (dirty_background_ratio <= 0)dirty_background_ratio = 1;if (vm_dirty_ratio <= 0)vm_dirty_ratio = 1;}

检查后台脏页比率:

if (dirty_background_ratio <= 0)dirty_background_ratio = 1;
  • 如果调整后的比率小于等于0,设置为最小值1
  • 防止除零错误和无效参数

检查系统脏页比率:

if (vm_dirty_ratio <= 0)vm_dirty_ratio = 1;
  • 同样的边界检查,确保比率至少为1%

第六段:启动写回定时器

        mod_timer(&wb_timer, jiffies + (dirty_writeback_centisecs * HZ) / 100);
  • mod_timer(&wb_timer, ...): 修改定时器的到期时间
  • &wb_timer: 写回定时器指针
  • jiffies: 内核时间单位,系统启动后的时钟滴答数
  • dirty_writeback_centisecs: 全局变量,写回间隔(百分之一秒)
  • HZ: 系统时钟频率(每秒滴答数)
  • 计算公式: 到期时间 = 当前时间 + (写回间隔 × HZ) / 100
  • 作用: 启动周期性脏页写回定时器

第七段:设置速率限制

        set_ratelimit();
  • set_ratelimit(): 设置写回速率限制函数
  • 初始化写回操作的速率控制参数
  • 防止写回操作消耗过多系统资源

第八段:注册CPU通知器

        register_cpu_notifier(&ratelimit_nb);
}
  • register_cpu_notifier(&ratelimit_nb): 注册CPU热插拔通知器
  • &ratelimit_nb: 速率限制通知块指针
  • 作用: 当CPU被热添加或移除时,调整写回速率限制参数

内存调整策略可视化

系统内存状态
计算缓冲区比例
缓冲区比例小?
降低脏页阈值
使用默认阈值
更频繁的写回
正常的写回频率
避免内存压力
优化IO性能
调整目标
平衡内存和IO
防止系统卡顿

校正因子公式分解

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

1. 基本比例计算

(buffer_pages) / total_pages

含义: 缓冲区页面占总页面缓存的比例

2. 乘以4的原因

4 * buffer_pages

为什么要乘以4?

在Linux内核的早期版本中,这个设计基于以下观察:

内存使用模式:

总内存 ≈ 缓冲区内存 + 文件缓存 + 应用程序内存

当缓冲区内存占总内存的 25% (1/4) 时,系统能达到较好的平衡。所以:

  • 基准线: 25% 的缓冲区比例被认为是"理想"状态
  • 校正逻辑: 如果实际比例小于25%,就需要调整脏页阈值

数学推导:

期望比例 = 25% = 1/4
校正因子 = (实际比例) / (期望比例) = (buffer_pages/total_pages) / (1/4)= 4 * (buffer_pages/total_pages)

3. 乘以100避免浮点数

100 * 4 * buffer_pages

先放大100倍,然后进行除法运算,提高精度的同时不会出现浮点运算

校正因子语义解释

校正因子的取值范围

缓冲区比例
校正因子
25%缓冲区
校正因子=100
12.5%缓冲区
校正因子=50
6.25%缓冲区
校正因子=25
50%缓冲区
校正因子=200

具体计算示例:

缓冲区比例计算过程校正因子
25%(100×4×25)/100 = 100100
12.5%(100×4×12.5)/100 = 5050
6.25%(100×4×6.25)/100 = 2525
50%(100×4×50)/100 = 200200

条件判断的逻辑

if (correction < 100)

为什么只在校正因子<100时调整?

设计哲学:

  • 校正因子 < 100: 缓冲区内存不足,需要降低脏页阈值
  • 校正因子 ≥ 100: 缓冲区内存充足,使用默认参数

pdflush线程池提交一个写回操作任务pdflush_operation

int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0)
{unsigned long flags;int ret = 0;if (fn == NULL)BUG();          /* Hard to diagnose if it's deferred */spin_lock_irqsave(&pdflush_lock, flags);if (list_empty(&pdflush_list)) {spin_unlock_irqrestore(&pdflush_lock, flags);ret = -1;} else {struct pdflush_work *pdf;pdf = list_entry(pdflush_list.next, struct pdflush_work, list);list_del_init(&pdf->list);if (list_empty(&pdflush_list))last_empty_jifs = jiffies;pdf->fn = fn;pdf->arg0 = arg0;wake_up_process(pdf->who);spin_unlock_irqrestore(&pdflush_lock, flags);}return ret;
}

函数整体功能

这个函数用于向pdflush(页面脏数据刷新)线程池提交一个写回操作任务,是Linux内核旧版本中页面写回机制的核心调度函数

代码分段详解

第一段:函数定义和变量声明

int pdflush_operation(void (*fn)(unsigned long), unsigned long arg0)
{unsigned long flags;int ret = 0;
  • int: 返回整型状态码,0成功,-1失败
  • void (*fn)(unsigned long): 函数指针参数,指向要执行的回调函数
  • unsigned long arg0: 回调函数的参数
  • flags: 用于保存中断状态的变量
  • ret = 0: 返回值,初始化为0(成功)

第二段:函数指针有效性检查

        if (fn == NULL)BUG();          /* Hard to diagnose if it's deferred */
  • if (fn == NULL): 检查传入的函数指针是否为空
  • BUG(): 如果为空,触发内核致命错误
  • 作用: 确保传入有效的回调函数,避免后续执行空指针

第三段:获取锁并保存中断状态

        spin_lock_irqsave(&pdflush_lock, flags);
  • spin_lock_irqsave(&pdflush_lock, flags):
    • 获取pdflush锁,保护共享数据结构
    • 同时保存当前中断状态到flags,并禁用中断
    • 防止并发访问pdflush_list和中断处理程序的竞争条件

第四段:检查pdflush线程池是否为空

        if (list_empty(&pdflush_list)) {spin_unlock_irqrestore(&pdflush_lock, flags);ret = -1;}

检查空列表:

if (list_empty(&pdflush_list))
  • 检查pdflush线程列表是否为空
  • pdflush_list: 全局变量,存储所有可用的pdflush线程

释放锁并返回错误:

spin_unlock_irqrestore(&pdflush_lock, flags);
ret = -1;
  • 恢复中断状态并释放锁
  • 设置返回值为-1(失败)
  • 场景: 没有可用的pdflush线程来处理任务

第五段:有可用线程的情况

        else {struct pdflush_work *pdf;
  • else: pdflush线程池非空,可以处理任务
  • struct pdflush_work *pdf: 声明pdflush工作结构指针

第六段:获取第一个可用线程

                pdf = list_entry(pdflush_list.next, struct pdflush_work, list);
  • list_entry(pdflush_list.next, struct pdflush_work, list):
    • pdflush_list.next: 获取链表第一个节点的指针
    • struct pdflush_work: 目标结构体类型
    • list: 在结构体中的链表成员名称
    • 作用: 通过链表节点指针获取包含它的完整结构体指针

第七段:从链表中移除线程

                list_del_init(&pdf->list);
  • list_del_init(&pdf->list):
    • 将线程从可用列表中移除
    • 并重新初始化链表节点(设置为空链表状态)
    • 作用: 标记这个线程为"工作中"状态

第八段:更新最后空列表时间

                if (list_empty(&pdflush_list))last_empty_jifs = jiffies;
  • if (list_empty(&pdflush_list)): 检查移除后列表是否为空
  • last_empty_jifs = jiffies: 如果为空,记录当前时间戳
  • jiffies: 内核时间单位,系统启动后的时钟滴答数
  • 作用: 记录线程池耗尽的时间,用于监控和统计

第九段:设置任务参数

                pdf->fn = fn;pdf->arg0 = arg0;
  • pdf->fn = fn: 将回调函数指针保存到工作结构
  • pdf->arg0 = arg0: 将参数保存到工作结构
  • 作用: 准备任务执行所需的所有信息

第十段:唤醒pdflush线程

                wake_up_process(pdf->who);
  • wake_up_process(pdf->who):
    • pdf->who: 指向pdflush线程的task_struct指针
    • 唤醒对应的内核线程,让它开始执行任务
    • 作用: 触发实际的写回操作执行

第十一段:释放锁并返回

                spin_unlock_irqrestore(&pdflush_lock, flags);}return ret;
}
  • spin_unlock_irqrestore(&pdflush_lock, flags): 恢复中断状态并释放锁
  • return ret: 返回操作结果(0成功,-1失败)

数据结构关系图

pdflush_list全局链表
pdf1: pdflush_work
pdf2: pdflush_work
pdf3: pdflush_work
task_struct who
函数指针 fn
参数 arg0
链表节点 list
操作过程
从链表获取pdf
设置fn和arg0
唤醒对应线程

定义写回定时器wb_timer

static struct timer_list wb_timer =TIMER_INITIALIZER(wb_timer_fn, 0, 0);
static void wb_timer_fn(unsigned long unused)
{if (pdflush_operation(wb_kupdate, 0) < 0)mod_timer(&wb_timer, jiffies + HZ); /* delay 1 second */
}

代码整体功能

这个代码定义了一个写回定时器,用于定期触发脏页写回操作,并在操作失败时实现退避重试机制

代码分段详解

第一段:定时器静态初始化

static struct timer_list wb_timer =TIMER_INITIALIZER(wb_timer_fn, 0, 0);

变量声明:

static struct timer_list wb_timer
  • static: 静态变量,限制作用域在当前文件
  • struct timer_list: 内核定时器结构体类型
  • wb_timer: 写回定时器变量名

定时器初始化宏:

TIMER_INITIALIZER(wb_timer_fn, 0, 0)
  • TIMER_INITIALIZER: 内核定时器静态初始化宏
  • wb_timer_fn: 定时器到期时调用的回调函数
  • 第一个 0: 定时器到期时间,0表示未激活
  • 第二个 0: 传递给回调函数的参数,这里为0

第二段:定时器回调函数定义

static void wb_timer_fn(unsigned long unused)
{
  • static void: 静态函数,无返回值
  • wb_timer_fn: 函数名,写回定时器回调函数
  • unsigned long unused: 参数,名为unused表示未使用
    • 虽然传递了参数,但函数体内没有使用它
    • 保持函数签名与定时器回调要求一致

第三段:尝试执行写回操作

        if (pdflush_operation(wb_kupdate, 0) < 0)

函数调用:

pdflush_operation(wb_kupdate, 0)
  • pdflush_operation: 前面分析的pdflush任务提交函数
  • wb_kupdate: 实际的写回操作函数指针
  • 0: 传递给wb_kupdate函数的参数

返回值检查:

if (... < 0)
  • 检查pdflush_operation的返回值
  • < 0 表示操作失败(通常是没有可用的pdflush线程)

wb_kupdate函数的作用:

  • 这是实际的脏页写回函数
  • 遍历内存中的脏页并写回到存储设备
  • 是页面写回系统的核心逻辑

第四段:失败时的退避重试

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

mod_timer函数:

mod_timer(&wb_timer, jiffies + HZ)
  • mod_timer: 内核函数,修改定时器的到期时间
  • &wb_timer: 要修改的定时器指针
  • jiffies + HZ: 新的到期时间

时间计算:

  • jiffies: 系统当前的时钟滴答数
  • HZ: 系统时钟频率,表示每秒的时钟滴答数
  • jiffies + HZ: 当前时间 + 1秒

作用: 如果写回操作失败,在1秒后重试

实际执行场景

场景1:正常执行

时间点T: 定时器到期
→ 调用wb_timer_fn
→ pdflush_operation成功提交任务
→ 函数返回,定时器停止
→ 需要其他代码在适当时候重新启动定时器

场景2:资源紧张

时间点T: 定时器到期
→ 调用wb_timer_fn  
→ pdflush_operation失败(无可用线程)
→ 设置1秒后重试
→ 时间点T+1秒: 定时器再次到期
→ 重试写回操作...

与其他组件的交互

pdflush_operation的关系

// 这是一个生产者-消费者模式
wb_timer_fn (生产者) → pdflush_operation → pdflush线程 (消费者)

与系统初始化的关系

page_writeback_init 中:

mod_timer(&wb_timer, jiffies + (dirty_writeback_centisecs * HZ) / 100);

这里设置了定时器的第一次触发时间

定期将过期的脏页写回到存储设备wb_kupdate

static void wb_kupdate(unsigned long arg)
{unsigned long oldest_jif;unsigned long start_jif;unsigned long next_jif;long nr_to_write;struct writeback_state wbs;struct writeback_control wbc = {.bdi            = NULL,.sync_mode      = WB_SYNC_NONE,.older_than_this = &oldest_jif,.nr_to_write    = 0,.nonblocking    = 1,.for_kupdate    = 1,};sync_supers();get_writeback_state(&wbs);oldest_jif = jiffies - (dirty_expire_centisecs * HZ) / 100;start_jif = jiffies;next_jif = start_jif + (dirty_writeback_centisecs * HZ) / 100;nr_to_write = wbs.nr_dirty + wbs.nr_unstable +(inodes_stat.nr_inodes - inodes_stat.nr_unused);while (nr_to_write > 0) {wbc.encountered_congestion = 0;wbc.nr_to_write = MAX_WRITEBACK_PAGES;writeback_inodes(&wbc);if (wbc.nr_to_write > 0) {if (wbc.encountered_congestion)blk_congestion_wait(WRITE, HZ/10);elsebreak;  /* All the old data is written */}nr_to_write -= MAX_WRITEBACK_PAGES - wbc.nr_to_write;}if (time_before(next_jif, jiffies + HZ))next_jif = jiffies + HZ;if (dirty_writeback_centisecs)mod_timer(&wb_timer, next_jif);
}

函数整体功能

这个函数是Linux内核脏页写回的核心实现,负责定期将过期的脏页写回到存储设备,维护系统内存的清洁度

代码分段详解

第一段:函数定义和变量声明

static void wb_kupdate(unsigned long arg)
{unsigned long oldest_jif;unsigned long start_jif;unsigned long next_jif;long nr_to_write;struct writeback_state wbs;
  • static void: 静态函数,无返回值
  • wb_kupdate: 函数名,写回更新操作
  • unsigned long arg: 参数(未使用)
  • oldest_jif: 最老脏页的时间戳阈值
  • start_jif: 本次写回开始时间
  • next_jif: 下一次写回计划时间
  • nr_to_write: 需要写回的页面数量估计
  • wbs: 写回状态结构,用于获取系统脏页统计

第二段:写回控制结构初始化

        struct writeback_control wbc = {.bdi            = NULL,.sync_mode      = WB_SYNC_NONE,.older_than_this = &oldest_jif,.nr_to_write    = 0,.nonblocking    = 1,.for_kupdate    = 1,};

写回控制参数:

  • .bdi = NULL: 后备设备信息,NULL表示所有设备
  • .sync_mode = WB_SYNC_NONE: 异步模式,不等待写回完成
  • .older_than_this = &oldest_jif: 指向时间阈值,只写回比这个时间老的脏页
  • .nr_to_write = 0: 初始写回页面数为0,后面会设置
  • .nonblocking = 1: 非阻塞模式
  • .for_kupdate = 1: 标记为定期更新操作

第三段:同步超级块

        sync_supers();
  • sync_supers(): 内核函数,将文件系统超级块同步到磁盘
  • 超级块包含文件系统元数据,需要定期持久化
  • 确保文件系统结构的一致性

第四段:获取系统脏页状态

        get_writeback_state(&wbs);
  • get_writeback_state(&wbs): 获取当前系统的写回状态
  • 填充wbs结构,包含:
    • nr_dirty: 脏页数量
    • nr_unstable: 不稳定页数量
    • 其他脏页统计信息

第五段:计算时间阈值

        oldest_jif = jiffies - (dirty_expire_centisecs * HZ) / 100;
  • jiffies: 当前系统时间戳
  • dirty_expire_centisecs: 全局变量,脏页过期时间(百分之一秒)
  • HZ: 系统时钟频率
  • 计算公式: 最老时间 = 当前时间 - 过期时间
  • 作用: 只写回存在时间超过dirty_expire_centisecs的脏页

第六段:记录开始时间和计算下次执行时间

        start_jif = jiffies;next_jif = start_jif + (dirty_writeback_centisecs * HZ) / 100;
  • start_jif = jiffies: 记录本次写回开始时间
  • next_jif = ...: 计算下一次写回执行时间
  • dirty_writeback_centisecs: 全局变量,写回间隔时间
  • 作用: 建立定期写回的时间计划

第七段:估算需要写回的页面数量

        nr_to_write = wbs.nr_dirty + wbs.nr_unstable +(inodes_stat.nr_inodes - inodes_stat.nr_unused);
  • wbs.nr_dirty: 当前脏页数量
  • wbs.nr_unstable: 不稳定页数量(正在写回中的页)
  • inodes_stat.nr_inodes - inodes_stat.nr_unused: 活跃inode数量估计
  • 作用: 粗略估算可能需要写回的最大页面数量
  • 这是一个保守估计,确保不会遗漏脏页

第八段:写回循环

        while (nr_to_write > 0) {
  • while (nr_to_write > 0): 循环直到所有需要写回的页面都处理完
  • 或者遇到无法继续写回的情况

第九段:设置本次写回参数

                wbc.encountered_congestion = 0;wbc.nr_to_write = MAX_WRITEBACK_PAGES;
  • wbc.encountered_congestion = 0: 重置拥塞标志
  • wbc.nr_to_write = MAX_WRITEBACK_PAGES: 设置本次最大写回页面数
  • MAX_WRITEBACK_PAGES: 常量,通常为1024,表示每次最多写回1024页

第十段:执行实际写回操作

                writeback_inodes(&wbc);
  • writeback_inodes(&wbc): 核心写回函数
  • 遍历所有inode,将其中的脏页写回到存储设备
  • 根据wbc参数控制写回行为

第十一段:检查写回完成情况

                if (wbc.nr_to_write > 0) {
  • if (wbc.nr_to_write > 0): 检查是否还有剩余的写回额度
  • 如果nr_to_write > 0,说明没有用完本次配额,可能因为:
    1. 所有旧数据都已写回
    2. 遇到IO拥塞无法继续

第十二段:处理IO拥塞

                        if (wbc.encountered_congestion)blk_congestion_wait(WRITE, HZ/10);elsebreak;  /* All the old data is written */}

拥塞情况:

if (wbc.encountered_congestion)blk_congestion_wait(WRITE, HZ/10);
  • wbc.encountered_congestion: 写回过程中检测到IO拥塞
  • blk_congestion_wait(WRITE, HZ/10): 等待写操作拥塞缓解
  • HZ/10: 等待0.1秒

无拥塞情况:

elsebreak;  /* All the old data is written */
  • 所有旧数据都已写回
  • 跳出循环,结束本次写回操作

第十三段:更新剩余写回数量

                nr_to_write -= MAX_WRITEBACK_PAGES - wbc.nr_to_write;}
  • MAX_WRITEBACK_PAGES - wbc.nr_to_write: 本次实际写回的页面数
  • nr_to_write -= ...: 从总估计值中减去已写回的数量
  • 循环继续处理剩余的脏页

第十四段:确保最小间隔

        if (time_before(next_jif, jiffies + HZ))next_jif = jiffies + HZ;
  • time_before(next_jif, jiffies + HZ): 检查计划的下次时间是否在1秒内
  • next_jif = jiffies + HZ: 如果在1秒内,设置为至少1秒后
  • 作用: 防止写回操作过于频繁,保证最小1秒间隔

第十五段:重新启动定时器

        if (dirty_writeback_centisecs)mod_timer(&wb_timer, next_jif);
}
  • if (dirty_writeback_centisecs): 检查写回间隔是否非零
  • mod_timer(&wb_timer, next_jif): 重新设置写回定时器
  • 作用: 建立下一次定期写回

控制脏页写回的速度set_ratelimit

static struct notifier_block ratelimit_nb = {.notifier_call  = ratelimit_handler,.next           = NULL,
};
static int
ratelimit_handler(struct notifier_block *self, unsigned long u, void *v)
{set_ratelimit();return 0;
}
static void set_ratelimit(void)
{ratelimit_pages = total_pages / (num_online_cpus() * 32);if (ratelimit_pages < 16)ratelimit_pages = 16;if (ratelimit_pages * PAGE_CACHE_SIZE > 4096 * 1024)ratelimit_pages = (4096 * 1024) / PAGE_CACHE_SIZE;
}

代码整体功能

这个代码实现了基于CPU数量的动态速率限制机制,用于控制脏页写回的速度,防止在多CPU系统中产生IO风暴

代码分段详解

第一段:通知器块定义

static struct notifier_block ratelimit_nb = {.notifier_call  = ratelimit_handler,.next           = NULL,
};

结构体定义:

  • static struct notifier_block ratelimit_nb: 静态通知器块变量
  • struct notifier_block: 内核通知链机制的结构体

成员初始化:

  • .notifier_call = ratelimit_handler: 通知回调函数指针
  • .next = NULL: 下一个通知器指针,NULL表示链表结束

作用: 注册到CPU热插拔通知链,当CPU状态变化时自动调用ratelimit_handler

第二段:通知器回调函数

ratelimit_handler(struct notifier_block *self, unsigned long u, void *v)
{set_ratelimit();return 0;
}

函数签名:

  • ratelimit_handler: 回调函数名
  • struct notifier_block *self: 指向自身的通知器块指针
  • unsigned long u: 事件类型(CPU上线、下线等)
  • void *v: 事件相关数据

函数体:

  • set_ratelimit(): 调用设置速率限制函数
  • return 0: 返回0表示处理成功

特点:

  • 忽略具体的事件类型和参数
  • 任何CPU状态变化都触发速率限制重计算
  • 保持简单的响应逻辑

第三段:速率限制设置函数

static void set_ratelimit(void)
{ratelimit_pages = total_pages / (num_online_cpus() * 32);

计算公式:

  • ratelimit_pages = total_pages / (num_online_cpus() * 32)
  • total_pages: 全局变量,系统总页面数
  • num_online_cpus(): 返回当前在线的CPU数量
  • 32: 经验系数,每个CPU分配32分之一的总页面作为速率限制

设计原理:

  • CPU越多,允许的并发写回页面越少
  • 防止多CPU同时产生大量写回IO
  • 平衡并行性和IO带宽

第四段:下限保护

        if (ratelimit_pages < 16)ratelimit_pages = 16;
  • if (ratelimit_pages < 16): 检查计算值是否小于16页
  • ratelimit_pages = 16: 如果太小,设置为最小值16页
  • 作用: 确保即使在大规模多CPU系统中也有基本的写回能力

第五段:上限保护

        if (ratelimit_pages * PAGE_CACHE_SIZE > 4096 * 1024)ratelimit_pages = (4096 * 1024) / PAGE_CACHE_SIZE;
}

条件检查:

  • ratelimit_pages * PAGE_CACHE_SIZE > 4096 * 1024
  • PAGE_CACHE_SIZE: 页面缓存大小,通常4096字节(4KB)
  • 4096 * 1024: 4MB(4096KB)

上限设置:

  • ratelimit_pages = (4096 * 1024) / PAGE_CACHE_SIZE
  • 如果超过4MB,限制为4MB对应的页面数
  • 对于4KB页面:4096 * 1024 / 4096 = 1024

作用: 防止在少CPU大内存系统中单次写回过多数据

设计原理分析

1. 基于CPU数量的自适应

// 核心思想:CPU越多,限制越严格
ratelimit_pages = total_pages / (num_online_cpus() * 32)

为什么是32?

  • 经验值,平衡并行性和IO效率
  • 每个CPU允许写回总内存的1/32份额
  • 防止多CPU同时发起写回产生IO竞争

2. 边界保护的意义

下限保护(16页):

  • 确保基本功能:即使在大规模系统中也能进行写回
  • 维持吞吐量:64KB的最小写回单元

上限保护(4MB):

  • 控制单次写回规模:避免大块IO阻塞系统
  • 公平性:防止少CPU系统占用过多IO带宽
  • 响应性:保证系统及时响应其他IO请求

3. 热插拔感知

通过通知器机制:

  • CPU上线:自动收紧限制(分母变大)
  • CPU下线:自动放宽限制(分母变小)
  • 实时适应系统拓扑变化
http://www.dtcms.com/a/515158.html

相关文章:

  • 神经网络中的随机高斯初始化技术
  • 怎样做网站分流赚钱东莞网站制作哪家公司好
  • HOOPS 3D可视化引擎:覆盖实时渲染与仿真分析的高性能解决方案!
  • 云原生架构下微服务接入 SkyWalking 最佳实践
  • 单片机中的机器周期、指令周期、总线周期的联系和区别
  • spring微服务宏观概念
  • 在阿里云通过docker部署srs流媒体服务器(支持webrtc、http-flv)
  • 【WAF】 Nginx如何集成安全检测服务
  • nginx安装和使用
  • 茂名市建设银行网站今天哈尔滨最新通告
  • 快飞建站月夜直播免费版
  • Windows 11 25H2 更新补丁导致鼠标键盘失灵,紧急更新补丁已推出
  • 移动端 (RN) - 键盘弹出, 不遮挡输入框 (和 底部按钮)的处理方案
  • 【C++闯关笔记】深究继承
  • Python爬虫抓取豆瓣TOP250数据
  • AWS Elemental MediaConvert:视频转码不再难
  • 华为OD最新机试真题-乘坐保密电梯-OD统一考试(C卷)
  • SpringBoot 如何实现零拷贝:深度解析零拷贝技术
  • 问卷调查网站怎么做做百度推广
  • Jenkins 持续集成与部署指南
  • 新书速览|DeepSeek高效数据分析:从数据清洗到行业案例
  • 搜索百科(5):Easysearch — 自主可控的国产分布式搜索引擎
  • 自己建商城型网站做设计的软件
  • # 模型量化(二):基于BERT的量化代码实战
  • 网站没有备案会怎样资源网官网
  • 【C++:继承】面向对象编程精要:C++继承机制深度解析与最佳实践
  • Python访问者模式实战指南:从基础到高级应用
  • 《数组和函数的实践游戏---扫雷游戏(基础版附源码)》
  • 专门做网站的软件是网站着陆页怎么做
  • 南京专业网站制作公司如何申请免费网站空间