shrink_list

static unsigned long shrink_list(enum lru_list lru, unsigned long nr_to_scan,
struct lruvec *lruvec, struct scan_control *sc)
{
if (is_active_lru(lru)) {
if (inactive_list_is_low(lruvec, is_file_lru(lru)))
shrink_active_list(nr_to_scan, lruvec, sc, lru);
return 0;
}


return shrink_inactive_list(nr_to_scan, lruvec, sc, lru);

}


static inline int is_active_lru(enum lru_list lru)
{
return (lru == LRU_ACTIVE_ANON || lru == LRU_ACTIVE_FILE);
}


/*
 * The inactive anon list should be small enough that the VM never has
 * to do too much work.
 *
 * The inactive file list should be small enough to leave most memory
 * to the established workingset on the scan-resistant active list,
 * but large enough to avoid thrashing the aggregate readahead window.
 *
 * Both inactive lists should also be large enough that each inactive
 * page has a chance to be referenced again before it is reclaimed.
 *
 * The inactive_ratio is the target ratio of ACTIVE to INACTIVE pages
 * on this LRU, maintained by the pageout code. A zone->inactive_ratio
 * of 3 means 3:1 or 25% of the pages are kept on the inactive list.
 *
 * total     target    max
 * memory    ratio     inactive
 * -------------------------------------
 *   10MB       1         5MB
 *  100MB       1        50MB
 *    1GB       3       250MB
 *   10GB      10       0.9GB
 *  100GB      31         3GB
 *    1TB     101        10GB
 *   10TB     320        32GB
 */
static bool inactive_list_is_low(struct lruvec *lruvec, bool file)
{
unsigned long inactive_ratio;
unsigned long inactive;
unsigned long active;
unsigned long gb;


/*
* If we don't have swap space, anonymous page deactivation
* is pointless.
*/
if (!file && !total_swap_pages)
return false;


inactive = lruvec_lru_size(lruvec, file * LRU_FILE);
active = lruvec_lru_size(lruvec, file * LRU_FILE + LRU_ACTIVE);


gb = (inactive + active) >> (30 - PAGE_SHIFT);
if (gb)
inactive_ratio = int_sqrt(10 * gb);
else
inactive_ratio = 1;


return inactive * inactive_ratio < active;

}


<think>我们正在讨论Linux内核内存管理中的`shrink_folio_list`函数。根据提供的引用和上下文,我们可以分析该函数的作用和应用场景。 ### 上下文回顾 - 引用[1]提到在`shrink_inactive_list`中,当`do_demotion_pages`标志设置时,物理页被加入`demotion_folios`链表,然后传递给`demote_folio_list`进行降级(而不是回收)。 - 引用[2]和[3]描述了页面回收的流程:`shrink_node` -> `shrink_lruvec` -> `get_scan_count`确定扫描页数,然后循环扫描活跃链表(通过`shrink_active_list`)和不活跃链表(通过`shrink_inactive_list`)。 - 引用[4]提到kswapd线程被唤醒以释放内存。 ### `shrink_folio_list`的作用 `shrink_folio_list`是Linux内核中内存回收(reclaim)过程的核心函数之一。它负责处理不活跃链表(inactive list)中的folio(内存页的集合),尝试回收这些页面。具体来说,该函数会: 1. **检查页面状态**:确定页面是否可以被回收(例如,是否为脏页、是否被锁定、是否有其他引用等)。 2. **执行回收操作**:根据页面类型和状态,采取不同的回收策略: - 对于文件缓存页(file page),如果页面是干净的(clean),可以直接丢弃(因为可以从磁盘重新读取);如果是脏的(dirty),则需要写回(writeback)到存储设备,然后才能回收。 - 对于匿名页(anonymous page),如果系统配置了交换空间(swap),则将其写入交换空间,然后回收。 3. **处理不可回收的页面**:如果页面不能被回收(例如,正在被使用或锁定),则将其移回活跃链表(active list)或其他适当的位置。 ### 使用场景 `shrink_folio_list`在以下场景中被调用: - **直接内存回收(Direct Reclaim)**:当进程在分配内存时发现内存不足(低于低水位线),会触发直接回收,此时会调用`shrink_folio_list`尝试立即回收页面。 - **kswapd后台回收**:当内存压力持续存在时,kswapd守护进程会被唤醒,定期扫描并回收页面,其中也会调用`shrink_folio_list`。 - **不活跃链表扫描**:在`shrink_inactive_list`函数中,会将不活跃链表中的folio收集起来,然后传递给`shrink_folio_list`进行实际的回收操作。 ### 函数流程(简化) 1. 遍历传入的folio链表。 2. 对每个folio: a. 检查是否被锁定(locked),如果是则跳过。 b. 检查引用计数(reference count),如果还有其他引用,则不能回收。 c. 根据页面类型(匿名页或文件页)和状态(脏/干净)执行相应操作。 d. 如果回收成功,将folio从LRU链表中移除,并添加到空闲链表(free list)中;否则,根据情况放回活跃或不活跃链表。 3. 返回已回收的页面数量。 ### 与引用内容的关联 - 引用[1]中提到的`demotion_folio_list`是一种替代回收的机制(将页面降级而不是回收),这与`shrink_folio_list`的回收路径不同。当启用降级时,`shrink_inactive_list`会跳过回收逻辑,转而使用降级。 - 引用[3]中`shrink_inactive_list`会调用`shrink_folio_list`(或类似函数)来回收不活跃链表中的页面。 ### 总结 `shrink_folio_list`是Linux内核内存回收的关键函数,负责处理不活跃链表中的页面,尝试回收它们以释放内存。它在直接回收和后台回收(kswapd)过程中被调用,处理文件页和匿名页的回收。 §§相关问题§§ 1. 在Linux内核中,页面回收的完整流程是怎样的? 2. 如何配置和优化内存回收机制以避免系统卡顿? 3. 文件页和匿名页在回收过程中有哪些不同的处理方式? 4. 什么是内存降级(demotion)?它和传统的页面回收有什么区别? 5. 如何监控Linux系统的页面回收活动? [^1]: DETECT/DEMOTION:在核心回收函数 shrink_inactive_list 中,遍历链表中的每个物理页。当检测到 do_demotion_pages 标志时,函数不执行内存回收逻辑,而是将这些物理页加入 demotion_folis 链表,因此不会解除映射或清除页表。最后将 demote_folis 传递给 demote_folio_list 进行实际降级。 [^2]: 在 shrink_node 一文主要分析了 shrink_lruvec,并知道在get_scan_count() 中根据扫描平衡条件,确定了anon/file LRU 扫描页数,并最终会shrink_list() 进行深入的回收处理。本文将继续分析页面回收 shrink_lruvec()部分的 shrink_list() 和 shrink_active_list() 流程。 [^3]: shrink_lruvec 中会计算本次要扫描的页数,然后触发实际的扫描和页回收(shrink_list):实现如下: shrink_lruvec(): // 计算要扫描页数 get_scan_count(lruvec, sc, nr); while 未扫描足够的页数且文件页或匿名页数量都不是0: for 2个 active lru list(file and anon): // 如果指定了may_deactivate,则尝试将部分hot 页移至cold 链表中 shrink_active_list(); for 2个 inactive lru list(file and anon):   shrink_inactive_list(); // 如果匿名页的cold页少,尝试deactivate 一些老的hot匿名页。 shrink_active_list(); 3.get_scan_count [^4]: kswap内存回收机制。当⼀个task在分配内存过程中,如果在Low watermark水线之上,从现有伙伴系统中没有分配到所需的内存时,就会通过wakeup_kswapd()函数来唤醒kswapd线程,以释放部分内存来满⾜当前的内存分配需求。</think>### `shrink_folio_list` 函数的作用及在内存管理中的应用 `shrink_folio_list` 是 Linux 内核内存回收机制中的核心函数,主要负责处理**不活跃内存页(inactive folios)的回收过程**。它在内存压力场景下被调用,通过释放物理页框来满足系统内存需求。以下是详细分析: --- #### 一、核心作用 1. **回收不活跃内存页** 遍历给定的不活跃页链表,尝试回收其中的物理内存页。回收策略根据页类型(文件页/匿名页)动态调整: - **文件页(File Page)**:若为**干净页**(未修改),直接释放;若为**脏页**(已修改),触发回写(writeback)后释放[^1]。 - **匿名页(Anonymous Page)**:若系统启用 Swap,将其写入交换空间后释放[^3]。 2. **处理页面状态转换** - 将无法回收的页重新激活(移回活跃链表)。 - 处理被锁定的页或正在回写的页(等待或跳过)。 3. **统计回收效果** 返回成功回收的页数,供上层回收逻辑(如 `shrink_inactive_list`)决策是否继续扫描[^2]。 --- #### 二、调用场景 该函数在以下内存回收路径中被调用: ```mermaid graph LR A[内存不足] --> B[shrink_node] B --> C[shrink_lruvec] C --> D[get_scan_count 计算扫描页数] D --> E[shrink_inactive_list] E --> F[shrink_folio_list] --> G[实际回收] ``` 1. **直接内存回收(Direct Reclaim)** 进程分配内存时触发(如 `alloc_pages` 失败),**同步执行**回收[^4]。 2. **kswapd 后台回收** 当内存水位低于低水位线(Low Watermark)时,由 kswapd 线程**异步触发**,逐步释放内存[^4]。 3. **主动降级(Demotion)场景** 当启用 `do_demotion_pages` 标志时,跳过回收逻辑,将页转移到降级链表(`demotion_folios`)供后续处理[^1]。 --- #### 三、关键流程 ```c // 伪代码逻辑 int shrink_folio_list(list, reclaim_stat) { while (folio in list) { if (folio is locked || under writeback) skip or wait; // 跳过或等待 if (folio is file page) { if (folio is clean) free_folio(); // 直接释放 else schedule_writeback(); // 触发回写 } else if (folio is anonymous page && swap_enabled) { swap_out(folio); // 交换到磁盘 } else { activate_folio(); // 移回活跃链表 } } return reclaimed_pages; // 返回回收页数 } ``` --- #### 四、数学关系与调优 1. **回收效率模型** 设 $P_r$ 为回收成功率,$N_{\text{scan}}$ 为扫描页数,则实际回收页数: $$ N_{\text{reclaimed}} = P_r \times N_{\text{scan}} $$ $P_r$ 取决于工作负载特征(如脏页比例、访问频率)。 2. **调优参数** - **脏页回写阈值**:通过 `vm.dirty_background_ratio` 控制脏页比例,影响回收延迟。 - **交换倾向**:`vm.swappiness`(值域 0-100)决定匿名页与文件页的回收优先级: - 高值:优先回收文件页($P_r$ 增大)。 - 低值:优先回收匿名页(需 Swap 支持)。 --- #### 五、应用实例 1. **数据库系统** 当 InnoDB 缓冲池过大时,文件页被频繁回收,可通过降低 `vm.swappiness` 保护文件缓存[^1]。 2. **容器环境** 内存超卖(Overcommit)场景下,`shrink_folio_list` 的回收效率直接影响容器 OOM 概率。 --- #### 六、监控方法 ```bash # 查看回收统计 grep -e pgscan -e pgsteal /proc/vmstat # 输出示例 pgscan_kswapd 1200 # kswapd 扫描页数 pgsteal_kswapd 800 # kswapd 成功回收页数 pgscan_direct 300 # 直接回收扫描页数 pgsteal_direct 200 # 直接回收成功页数 ``` - **回收效率比**:$\frac{\text{pgsteal}}{\text{pgscan}}$ 应接近 1(过低表明回收效率差)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值