前言:交换系统的"物流管理中心"
想象一下Linux的交换系统就像一个高效的物流中心:当物理内存紧张时,它需要将不常用的"货物"(内存页面)暂时存放到"仓库"(交换空间)中,等需要时再快速取回。这个过程中涉及复杂的"库存管理"、"货物追踪"和"空间优化"
Linux通过一套精密的交换管理机制实现了这一切:
- 交换条目就像仓库的货架编号系统
- 交换结构相当于实时库存管理系统
- 引用计数确保货物不会被错误处理
- 锁机制防止并发操作导致数据混乱
本文将深入解析这套交换系统的核心组件,揭示Linux如何高效管理虚拟内存与物理存储之间的数据流动
核心架构:四级管理层次
第一层:交换条目编码
c
// 交换条目编码原理
swp_entry_t entry = swp_entry(type, offset); // 编码
unsigned type = swp_type(entry); // 解码类型
pgoff_t offset = swp_offset(entry); // 解码偏移
设计精妙之处:
- 架构无关:32位/64位系统使用相同格式
- 基数树优化:偏移量在低位,提高缓存局部性
- 边界安全:自动确保值在有效范围内
第二层:交换信息管理
c
// 交换设备验证流程
swap_info_get(entry)
↓
类型检查 → 设备状态检查 → 偏移量检查 → 引用计数检查 → 锁获取
完整性验证:
- 检查交换文件是否存在且启用
- 验证偏移量在有效范围内
- 确认交换槽位确实被使用
- 更新全局交换设备优先级
第三层:引用计数管理
c
// 引用计数语义
0: 槽位空闲
1: 单一使用者
2+: 多个共享者
SWAP_MAP_MAX: 槽位失效
释放时的智能处理:
c
swap_entry_free(p, offset)
↓
递减计数 → 如果归零 → 更新偏移边界位 → 更新统计信息
第四层:缓存管理 - "库存优化系统"
c
// 独占页面检查条件
remove_exclusive_swap_page(page) 需要满足:
1. 页面在交换缓存中
2. 不在回写过程中
3. 引用计数恰好为2
4. 交换映射计数为1
工作流程:从释放到回收
场景1:普通页面释放
free_swap_and_cache(entry)
↓
swap_info_get(entry) // 验证交换条目
↓
swap_entry_free() // 释放交换槽位
↓
查找对应页面 // 在交换缓存中查找
↓
条件检查 // 独占或空间紧张
↓
delete_from_swap_cache() // 从缓存移除
场景2:独占页面优化释放
remove_exclusive_swap_page(page)
↓
前置条件检查 // 锁定状态、缓存状态等
↓
双重验证 // 锁保护下的重新检查
↓
__delete_from_swap_cache() // 安全移除
↓
资源释放 // 交换条目和页面
安全机制:并发访问防护
锁的层次结构
c
// 锁获取顺序
swap_list_lock() // 全局交换列表锁
swap_device_lock(p) // 特定设备锁
swapper_space.tree_lock // 交换缓存树锁
page_lock // 页面锁
防止死锁的关键:
- 严格按照层次顺序获取锁
- 逆序释放锁
- 锁保护下的重新验证
竞态条件防护
双重检查模式:
c
// 第一次检查(无锁)
if (page_count(page) == 2) {
// 第二次检查(有锁保护)
spin_lock_irq(&lock);
if (page_count(page) == 2) {
// 执行操作
}
spin_unlock_irq(&lock);
}
智能优化策略
内存压力响应
c
// 交换空间紧张时的积极释放
if (vm_swap_full()) {
// 即使页面还有多个使用者,也尝试释放
// 缓解交换空间压力
}
惰性释放策略
- 首选:只有唯一使用者时才释放
- 备选:交换空间满时强制释放
- 避免:页面正在回写时不释放
根据交换条目获取对应的交换信息结构swap_info_get
c
static struct swap_info_struct * swap_info_get(swp_entry_t entry)
{
struct swap_info_struct * p;
unsigned long offset, type;
if (!entry.val)
goto out;
type = swp_type(entry);
if (type >= nr_swapfiles)
goto bad_nofile;
p = & swap_info[type];
if (!(p->flags & SWP_USED))
goto bad_device;
offset = swp_offset(entry);
if (offset >= p->max)
goto bad_offset;
if (!p->swap_map[offset])
goto bad_free;
swap_list_lock();
if (p->prio > swap_info[swap_list.next].prio)
swap_list.next = type;
swap_device_lock(p);
return p;
bad_free:
printk(KERN_ERR "swap_free: %s%08lx\n", Unused_offset, entry.val);
goto out;
bad_offset:
printk(KERN_ERR "swap_free: %s%08lx\n", Bad_offset, entry.val);
goto out;
bad_device:
printk(KERN_ERR "swap_free: %s%08lx\n", Unused_file, entry.val);
goto out;
bad_nofile:
printk(KERN_ERR "swap_free: %s%08lx\n", Bad_file, entry.val);
out:
return NULL;
}
代码功能概述
这个函数根据交换条目(swp_entry_t)获取对应的交换信息结构,并进行全面的有效性验证
代码逐段解析
函数声明和变量定义
c
static struct swap_info_struct * swap_info_get(swp_entry_t entry)
{
struct swap_info_struct * p;
unsigned long offset, type;
- 参数 :
entry- 交换条目,包含类型和偏移量信息 - 局部变量 :
p:指向交换信息结构的指针offset:交换条目中的偏移量type:交换条目中的类型
基本有效性检查
c
if (!entry.val)
goto out;
- 检查交换条目是否有效 :如果
entry.val为0,表示无效的交换条目 - 跳转到
out标签返回NULL
c
type = swp_type(entry);
if (type >= nr_swapfiles)
goto bad_nofile;
- 提取类型并检查 :
type = swp_type(entry):从交换条目中提取类型字段type >= nr_swapfiles:检查类型是否超出有效范围- 如果超出范围,跳转到
bad_nofile错误处理
c
p = & swap_info[type];
if (!(p->flags & SWP_USED))
goto bad_device;
- 获取交换信息结构 :
p = &swap_info[type]:从全局交换信息数组中获取对应类型的结构!(p->flags & SWP_USED):检查该交换设备是否正在使用- 如果未使用,跳转到
bad_device错误处理
偏移量有效性检查
c
offset = swp_offset(entry);
if (offset >= p->max)
goto bad_offset;
- 提取并检查偏移量 :
offset = swp_offset(entry):从交换条目中提取偏移量offset >= p->max:检查偏移量是否超出交换设备的最大容量- 如果超出,跳转到
bad_offset错误处理
c
if (!p->swap_map[offset])
goto bad_free;
- 检查交换槽是否被使用 :
!p->swap_map[offset]:检查对应偏移量的交换映射是否为0- 如果为0,表示该交换槽未被使用,跳转到
bad_free错误处理
锁操作和返回
c
swap_list_lock();
if (p->prio > swap_info[swap_list.next].prio)
swap_list.next = type;
swap_device_lock(p);
return p;
- 获取全局交换列表锁 :
swap_list_lock()保护全局交换列表 - 更新交换列表 :如果当前交换设备的优先级更高,更新
swap_list.next - 获取设备锁 :
swap_device_lock(p)锁定特定的交换设备 - 返回有效指针:返回获取到的交换信息结构指针
错误处理部分
c
bad_free:
printk(KERN_ERR "swap_free: %s%08lx\n", Unused_offset, entry.val);
goto out;
- 未使用交换槽错误:打印错误信息,跳转到out
c
bad_offset:
printk(KERN_ERR "swap_free: %s%08lx\n", Bad_offset, entry.val);
goto out;
- 偏移量越界错误:打印错误信息,跳转到out
c
bad_device:
printk(KERN_ERR "swap_free: %s%08lx\n", Unused_file, entry.val);
goto out;
- 未使用设备错误:打印错误信息,跳转到out
c
bad_nofile:
printk(KERN_ERR "swap_free: %s%08lx\n", Bad_file, entry.val);
out:
return NULL;
}
- 错误文件错误:打印错误信息
- 统一返回:所有错误路径都返回NULL
函数功能总结
核心功能:根据交换条目安全地获取对应的交换信息结构
主要作用:
- 有效性验证:全面检查交换条目的所有组成部分
- 状态检查:验证交换设备和交换槽的使用状态
- 锁管理:获取必要的锁来保护并发访问
- 优先级更新:维护全局交换设备的选择顺序
- 错误诊断:提供详细的错误信息和分类
交换条目编码和解码swp_entry/swp_type/swp_offset
c
#define SWP_TYPE_SHIFT(e) (sizeof(e.val) * 8 - MAX_SWAPFILES_SHIFT)
#define SWP_OFFSET_MASK(e) ((1UL << SWP_TYPE_SHIFT(e)) - 1)
/*
* Store a type+offset into a swp_entry_t in an arch-independent format
*/
static inline swp_entry_t swp_entry(unsigned long type, pgoff_t offset)
{
swp_entry_t ret;
ret.val = (type << SWP_TYPE_SHIFT(ret)) |
(offset & SWP_OFFSET_MASK(ret));
return ret;
}
/*
* Extract the `type' field from a swp_entry_t. The swp_entry_t is in
* arch-independent format
*/
static inline unsigned swp_type(swp_entry_t entry)
{
return (entry.val >> SWP_TYPE_SHIFT(entry));
}
/*
* Extract the `offset' field from a swp_entry_t. The swp_entry_t is in
* arch-independent format
*/
static inline pgoff_t swp_offset(swp_entry_t entry)
{
return entry.val & SWP_OFFSET_MASK(entry);
}
代码功能概述
这段代码定义了交换条目(swp_entry_t)的架构无关格式,实现了类型和偏移量的编码、解码操作
代码逐段解析
c
#define SWP_TYPE_SHIFT(e) (sizeof(e.val) * 8 - MAX_SWAPFILES_SHIFT)
- 计算类型移位量 :
sizeof(e.val) * 8:获取e.val的位数(如32位或64位)MAX_SWAPFILES_SHIFT:最大交换文件数的位宽(通常为5,支持32个交换文件)- 结果:类型字段应该左移的位数
c
#define SWP_OFFSET_MASK(e) ((1UL << SWP_TYPE_SHIFT(e)) - 1)
- 计算偏移量掩码 :
1UL << SWP_TYPE_SHIFT(e):创建只有类型位设置的掩码-1:得到所有偏移量位的掩码- 示例:32位系统中,
SWP_TYPE_SHIFT = 27,掩码为0x07FFFFFF
swp_entry 函数 - 编码
c
/*
* Store a type+offset into a swp_entry_t in an arch-independent format
*/
static inline swp_entry_t swp_entry(unsigned long type, pgoff_t offset)
{
swp_entry_t ret;
- 函数功能:将类型和偏移量编码为架构无关的交换条目
- 参数 :
type:交换文件类型(0-31)offset:页面在交换文件中的偏移量
- 返回值 :
swp_entry_t结构
c
ret.val = (type << SWP_TYPE_SHIFT(ret)) |
(offset & SWP_OFFSET_MASK(ret));
- 编码操作 :
type << SWP_TYPE_SHIFT(ret):将类型移到高位offset & SWP_OFFSET_MASK(ret):确保偏移量在有效范围内|:将两部分合并
c
return ret;
}
swp_type 函数 - 解码类型
c
/*
* Extract the `type' field from a swp_entry_t. The swp_entry_t is in
* arch-independent format
*/
static inline unsigned swp_type(swp_entry_t entry)
{
return (entry.val >> SWP_TYPE_SHIFT(entry));
}
- 函数功能:从交换条目中提取类型字段
- 操作 :将
entry.val右移SWP_TYPE_SHIFT位,得到类型值
swp_offset 函数 - 解码偏移量
c
/*
* Extract the `offset' field from a swp_entry_t. The swp_entry_t is in
* arch-independent format
*/
static inline pgoff_t swp_offset(swp_entry_t entry)
{
return entry.val & SWP_OFFSET_MASK(entry);
}
- 函数功能:从交换条目中提取偏移量字段
- 操作:用掩码提取偏移量部分
详细技术说明
为什么需要架构无关格式?
问题:不同架构可能有不同的字长和字节序
- 32位系统:4字节
- 64位系统:8字节
- 大端序 vs 小端序
解决方案:
c
// 架构相关(不好)
struct swp_entry_t {
unsigned short type;
unsigned long offset;
};
// 架构无关(好)
typedef struct {
unsigned long val;
} swp_entry_t;
通过位操作在统一的val字段中编码所有信息,确保在不同架构间的一致性
基数树优化原理
基数树(Radix Tree)特性:
- 基于键的位模式进行分层查找
- 低阶位密集的键有更好的缓存局部性
交换条目的键就是entry.val:
c
// 在基数树中查找
page = radix_tree_lookup(&swapper_space.page_tree, entry.val);
通过将偏移量放在低位,确保:
- 相同交换文件的页面在基数树中相邻存储
- 提高空间局部性,减少缓存失效
函数功能总结
核心功能:提供交换条目的架构无关编码和解码机制
主要作用:
- 统一编码:将类型和偏移量编码为单一的架构无关值
- 精确解码:从编码值中准确提取类型和偏移量
- 边界安全:确保编码值在有效范围内
- 性能优化:为基数树存储优化位布局
swap_info_put/swap_entry_free/swap_free
c
static void swap_info_put(struct swap_info_struct * p)
{
swap_device_unlock(p);
swap_list_unlock();
}
static int swap_entry_free(struct swap_info_struct *p, unsigned long offset)
{
int count = p->swap_map[offset];
if (count < SWAP_MAP_MAX) {
count--;
p->swap_map[offset] = count;
if (!count) {
if (offset < p->lowest_bit)
p->lowest_bit = offset;
if (offset > p->highest_bit)
p->highest_bit = offset;
nr_swap_pages++;
p->inuse_pages--;
}
}
return count;
}
/*
* Caller has made sure that the swapdevice corresponding to entry
* is still around or has not been recycled.
*/
void swap_free(swp_entry_t entry)
{
struct swap_info_struct * p;
p = swap_info_get(entry);
if (p) {
swap_entry_free(p, swp_offset(entry));
swap_info_put(p);
}
}
代码功能概述
这段代码实现了交换条目的释放机制,管理交换空间的使用计数和空闲页面统计
代码逐段解析
swap_info_put 函数
c
static void swap_info_put(struct swap_info_struct * p)
{
swap_device_unlock(p);
swap_list_unlock();
}
- 函数功能:释放交换信息结构相关的锁
- 操作顺序 :
swap_device_unlock(p):先释放设备锁swap_list_unlock():后释放列表锁
- 锁顺序原则:按获取的逆序释放,防止死锁
swap_entry_free 函数
c
static int swap_entry_free(struct swap_info_struct *p, unsigned long offset)
{
int count = p->swap_map[offset];
- 函数功能:释放指定的交换槽位
- 参数 :
p:交换信息结构offset:交换槽位偏移量
- 获取当前计数 :
p->swap_map[offset]获取该槽位的引用计数
c
if (count < SWAP_MAP_MAX) {
count--;
p->swap_map[offset] = count;
- 检查是否可递减 :
count < SWAP_MAP_MAX确保不是特殊值 - 递减引用计数 :
count--减少使用计数 - 更新映射表 :
p->swap_map[offset] = count写回新的计数值
c
if (!count) {
if (offset < p->lowest_bit)
p->lowest_bit = offset;
if (offset > p->highest_bit)
p->highest_bit = offset;
- 检查是否完全释放 :
!count表示计数降为0(槽位空闲) - 更新最低位 :如果新释放的槽位比当前最低位还低,更新
lowest_bit - 更新最高位 :如果新释放的槽位比当前最高位还高,更新
highest_bit
c
nr_swap_pages++;
p->inuse_pages--;
}
}
return count;
}
- 更新全局统计 :
nr_swap_pages++增加全局空闲交换页面数 - 更新设备统计 :
p->inuse_pages--减少该设备的在用页面数 - 返回新计数:返回释放后的引用计数值
swap_free 函数
c
void swap_free(swp_entry_t entry)
{
struct swap_info_struct * p;
- 调用者已确保交换设备仍然存在且未被回收
- 函数功能:释放交换条目
- 参数 :
entry- 要释放的交换条目
c
p = swap_info_get(entry);
if (p) {
swap_entry_free(p, swp_offset(entry));
swap_info_put(p);
}
}
- 获取交换信息 :
swap_info_get(entry)验证并获取对应的交换信息结构 - 条件执行 :如果成功获取交换信息(
p != NULL)- 调用
swap_entry_free(p, swp_offset(entry))释放槽位 - 调用
swap_info_put(p)释放锁
- 调用
- 空指针安全 :如果
swap_info_get返回NULL,静默跳过(交换条目可能已无效)
关键数据结构说明
交换映射计数语义
c
// p->swap_map[offset] 的含义:
0: 槽位空闲
1: 只有一个使用者
2+: 多个使用者共享
SWAP_MAP_MAX: 特殊值,表示槽位失效
交换信息结构关键字段
c
struct swap_info_struct {
unsigned char *swap_map; // 交换映射数组
unsigned long lowest_bit; // 最低空闲位(搜索起点)
unsigned long highest_bit; // 最高空闲位(搜索终点)
unsigned int inuse_pages; // 在用页面数
// ...
};
全局变量
c
unsigned int nr_swap_pages; // 全局空闲交换页面总数
函数功能总结
核心功能:安全地释放交换条目,管理交换空间的使用计数和空闲资源
主要作用:
- 引用计数管理:递减交换槽位的使用计数
- 资源回收:当计数降为0时,标记槽位为空闲
- 统计维护:更新全局和设备级别的统计信息
- 优化信息:维护边界位以加速后续分配
交换缓存独占页面检查与释放remove_exclusive_swap_page
c
int remove_exclusive_swap_page(struct page *page)
{
int retval;
struct swap_info_struct * p;
swp_entry_t entry;
BUG_ON(PagePrivate(page));
BUG_ON(!PageLocked(page));
if (!PageSwapCache(page))
return 0;
if (PageWriteback(page))
return 0;
if (page_count(page) != 2) /* 2: us + cache */
return 0;
entry.val = page->private;
p = swap_info_get(entry);
if (!p)
return 0;
/* Is the only swap cache user the cache itself? */
retval = 0;
if (p->swap_map[swp_offset(entry)] == 1) {
/* Recheck the page count with the swapcache lock held.. */
spin_lock_irq(&swapper_space.tree_lock);
if ((page_count(page) == 2) && !PageWriteback(page)) {
__delete_from_swap_cache(page);
SetPageDirty(page);
retval = 1;
}
spin_unlock_irq(&swapper_space.tree_lock);
}
swap_info_put(p);
if (retval) {
swap_free(entry);
page_cache_release(page);
}
return retval;
}
代码功能概述
这个函数检查一个交换缓存页面是否只有当前引用,如果是则从交换缓存中移除并释放它
代码逐段解析
函数声明和变量定义
c
int remove_exclusive_swap_page(struct page *page)
{
int retval;
struct swap_info_struct * p;
swp_entry_t entry;
- 参数 :
page- 要检查的页面指针 - 局部变量 :
retval:操作结果(1=成功移除,0=失败)p:交换信息结构指针entry:交换条目,标识页面在交换空间中的位置
前置条件检查
c
BUG_ON(PagePrivate(page));
BUG_ON(!PageLocked(page));
- 第一个BUG_ON :
PagePrivate(page)检查页面是否被设置为PG_privatepage->private字段在交换缓存中用于存储交换条目,在文件页面中用于存储文件系统私有数据- 也就是说
PG_private和PG_swapcache这两个标志是冲突的
- 第二个BUG_ON :
!PageLocked(page)检查页面是否被被设置为PG_locked- 如果页面未锁定,触发内核BUG,因为下面的操作需要页面锁保证原子性
c
if (!PageSwapCache(page))
return 0;
- 检查是否在交换缓存中:如果页面不在交换缓存中,直接返回0(失败)
c
if (PageWriteback(page))
return 0;
- 检查是否正在回写:如果页面正在写入交换空间,不能移除,返回0
c
if (page_count(page) != 2) /* 2: us + cache */
return 0;
- 检查引用计数 :期望的引用计数是2
- 1个引用:来自交换缓存本身
- 1个引用:来自当前函数调用者
- 如果计数不等于2,说明有其他使用者,返回0
获取交换信息
c
entry.val = page->private;
p = swap_info_get(entry);
if (!p)
return 0;
- 获取交换条目 :
page->private存储了交换条目的值 - 获取交换信息 :
swap_info_get(entry)根据交换条目获取对应的交换信息结构 - 错误检查:如果获取失败(交换条目无效),返回0
核心检查逻辑
c
/* Is the only swap cache user the cache itself? */
retval = 0;
if (p->swap_map[swp_offset(entry)] == 1) {
- 初始化返回值 :
retval = 0(假设失败) - 检查交换映射计数 :
p->swap_map[swp_offset(entry)] == 1swp_offset(entry):从交换条目中提取偏移量p->swap_map[]:交换空间使用计数数组== 1:表示只有当前页面在使用这个交换槽
c
/* Recheck the page count with the swapcache lock held.. */
spin_lock_irq(&swapper_space.tree_lock);
- 获取交换缓存锁:在持有锁的情况下重新检查条件
spin_lock_irq:禁用中断并获取自旋锁,防止并发访问
c
if ((page_count(page) == 2) && !PageWriteback(page)) {
- 在锁保护下重新检查 :
- 引用计数仍然为2
- 页面不在回写状态
c
__delete_from_swap_cache(page);
SetPageDirty(page);
retval = 1;
- 从交换缓存删除 :
__delete_from_swap_cache(page)将页面从交换缓存中移除 - 标记页面为脏 :
SetPageDirty(page)因为内存中的页面内容可能已修改,需要确保正确写回 - 设置成功标志 :
retval = 1表示成功移除
c
}
spin_unlock_irq(&swapper_space.tree_lock);
}
- 释放交换缓存锁 :
spin_unlock_irq恢复中断状态并释放锁
资源清理
c
swap_info_put(p);
- 释放
swap_list_lock和swap_device_lock
c
if (retval) {
swap_free(entry);
page_cache_release(page);
}
- 成功时的清理 :如果
retval == 1(成功移除)swap_free(entry):释放交换条目,减少交换空间使用计数page_cache_release(page):释放页面引用,可能真正释放页面
c
return retval;
}
- 返回操作结果:1表示成功移除,0表示失败
函数功能总结
核心功能:检查并释放只有当前引用的交换缓存页面
主要作用:
- 条件验证:验证页面是否满足独占释放的条件
- 竞态防护:通过锁和双重检查防止并发问题
- 资源回收:安全地从交换缓存移除页面并释放资源
- 状态维护:正确维护页面和交换空间的状态
从交换缓存删除页面__delete_from_swap_cache
c
/*
* This must be called only on pages that have
* been verified to be in the swap cache.
*/
void __delete_from_swap_cache(struct page *page)
{
BUG_ON(!PageLocked(page));
BUG_ON(!PageSwapCache(page));
BUG_ON(PageWriteback(page));
radix_tree_delete(&swapper_space.page_tree, page->private);
page->private = 0;
ClearPageSwapCache(page);
total_swapcache_pages--;
pagecache_acct(-1);
INC_CACHE_INFO(del_total);
}
代码功能概述
这个函数负责从交换缓存中安全地移除页面,是页面换入和交换缓存清理的关键操作
代码逐段解析
函数声明和注释
c
/*
* This must be called only on pages that have
* been verified to be in the swap cache.
*/
void __delete_from_swap_cache(struct page *page)
- 这个函数只能对已经确认在交换缓存中的页面调用
- 函数名 :
__delete_from_swap_cache中的__通常表示内部函数,调用者需要确保前置条件 - 参数 :
page- 要从交换缓存中移除的页面
前置条件检查(BUG_ON)
c
BUG_ON(!PageLocked(page));
- 检查页面锁:确保页面已被锁定
c
BUG_ON(!PageSwapCache(page));
- 检查交换缓存标志:确保页面确实在交换缓存中
c
BUG_ON(PageWriteback(page));
- 检查写回状态:确保页面不在写回过程中
从基数树删除
c
radix_tree_delete(&swapper_space.page_tree, page->private);
- 核心删除操作:从交换空间的基数树中移除页面
- 参数 :
&swapper_space.page_tree:全局交换空间的基数树page->private:页面的交换条目,作为基数树的键
- 作用:断开页面与交换缓存的数据结构链接
页面元数据清理
c
page->private = 0;
- 清空私有字段 :将
page->private设为0 - 重要性:这个字段之前存储的是交换条目,现在页面不再属于交换缓存
- 为重用做准备:清除后,这个字段可以用于其他用途
c
ClearPageSwapCache(page);
- 清除交换缓存标志 :将页面的
PG_swapcache标志位清零 - 状态更新:页面正式不再属于交换缓存
- 语义变化:页面现在可以视为普通的内存页面
统计信息更新
c
total_swapcache_pages--;
- 减少全局计数:递减全局交换缓存页面计数器
- 系统级统计:跟踪系统中交换缓存页面的总数
c
pagecache_acct(-1);
- 页面缓存统计:更新页面缓存相关的内存记账信息
- 资源管理:帮助内核跟踪内存使用情况
c
INC_CACHE_INFO(del_total);
- 增加缓存操作统计:递增删除操作计数器
- 性能监控:用于调试和性能分析
关键数据结构说明
1. 交换空间结构(swapper_space)
c
struct address_space swapper_space = {
.page_tree = RADIX_TREE_INIT(GFP_ATOMIC|__GFP_NOWARN),
.i_mmap = RB_ROOT,
.a_ops = &swap_aops,
// ...
};
这是全局的交换缓存管理结构,所有交换缓存页面都存储在其基数树中。
2. 页面标志位
c
#define PG_swapcache 12 /* Page is in swap cache */
PageSwapCache宏检查的就是这个标志位。
3. 页面private字段的语义变化
在交换缓存中时:
c
page->private = swp_entry_t.val // 存储交换条目
从交换缓存删除后:
c
page->private = 0 // 可被重新用于其他目的
函数功能总结
核心功能:从交换缓存中安全地移除页面,恢复为普通内存页面
主要作用:
- 数据结构清理:从交换缓存基数树中移除页面引用
- 状态重置:清除页面的交换缓存相关标志和字段
- 资源更新:准确维护系统统计信息和记账数据
交换条目和页面缓存释放free_swap_and_cache
c
/*
* Free the swap entry like above, but also try to
* free the page cache entry if it is the last user.
*/
void free_swap_and_cache(swp_entry_t entry)
{
struct swap_info_struct * p;
struct page *page = NULL;
p = swap_info_get(entry);
if (p) {
if (swap_entry_free(p, swp_offset(entry)) == 1) {
spin_lock_irq(&swapper_space.tree_lock);
page = radix_tree_lookup(&swapper_space.page_tree,
entry.val);
if (page && TestSetPageLocked(page))
page = NULL;
spin_unlock_irq(&swapper_space.tree_lock);
}
swap_info_put(p);
}
if (page) {
int one_user;
BUG_ON(PagePrivate(page));
page_cache_get(page);
one_user = (page_count(page) == 2);
/* Only cache user (+us), or swap space full? Free it! */
if (!PageWriteback(page) && (one_user || vm_swap_full())) {
delete_from_swap_cache(page);
SetPageDirty(page);
}
unlock_page(page);
page_cache_release(page);
}
}
函数功能
释放交换条目(swap entry),并且如果这是最后一个使用者,也尝试释放对应的页面缓存条目
代码分段详解
函数声明和变量定义
c
void free_swap_and_cache(swp_entry_t entry)
{
struct swap_info_struct * p;
struct page *page = NULL;
entry: 要释放的交换条目,包含交换分区信息和偏移量p: 指向交换信息结构的指针page: 指向对应页面的指针,初始化为NULL
获取交换信息结构
c
p = swap_info_get(entry);
swap_info_get(entry): 根据交换条目获取对应的交换信息结构- 这会获取交换信息结构的锁
交换条目释放检查
c
if (p) {
if (swap_entry_free(p, swp_offset(entry)) == 1) {
- 如果成功获取交换信息结构
swap_entry_free(p, swp_offset(entry)): 释放交换条目,返回剩余引用计数- 返回值1的含义: 表示这是最后一个引用,交换条目现在可以完全释放了
查找对应的页面缓存
c
spin_lock_irq(&swapper_space.tree_lock);
page = radix_tree_lookup(&swapper_space.page_tree, entry.val);
if (page && TestSetPageLocked(page))
page = NULL;
spin_unlock_irq(&swapper_space.tree_lock);
spin_lock_irq(&swapper_space.tree_lock): 获取交换空间的树锁,禁用中断radix_tree_lookup(&swapper_space.page_tree, entry.val): 在基数树中查找交换条目对应的页面TestSetPageLocked(page): 尝试锁定页面,如果页面已被锁定则返回true- 如果页面被锁定,将page设为NULL(避免等待)
spin_unlock_irq(&swapper_space.tree_lock): 释放锁,恢复中断
释放交换信息结构引用
c
swap_info_put(p);
}
swap_info_put(p): 释放交换信息结构的锁- 与之前的
swap_info_get配对使用
页面处理准备
c
if (page) {
int one_user;
BUG_ON(PagePrivate(page));
page_cache_get(page);
one_user = (page_count(page) == 2);
- 如果找到了对应的页面
BUG_ON(PagePrivate(page)): 确保页面没有私有标志(交换缓存页面不应该有)- 如果有标志
PagePrivate,那么page->private存储的将不是交换条目,而是文件系统私有数据
- 如果有标志
page_cache_get(page): 增加页面缓存引用计数page_count(page) == 2: 检查是否只有一个用户- 引用计数为2表示:页面缓存引用 + 当前函数临时引用
页面释放条件判断
c
/* Only cache user (+us), or swap space full? Free it! */
if (!PageWriteback(page) && (one_user || vm_swap_full())) {
!PageWriteback(page): 页面没有在回写过程中one_user: 只有一个用户(页面缓存)vm_swap_full(): 交换空间已满- 条件: 页面不在回写中,并且(只有一个用户或交换空间满)
从交换缓存中删除页面
c
delete_from_swap_cache(page);
SetPageDirty(page);
功能:
delete_from_swap_cache(page): 从交换缓存中删除页面SetPageDirty(page): 将页面标记为脏页,确保后续正确处理
清理工作
c
unlock_page(page);
page_cache_release(page);
}
}
unlock_page(page): 解锁页面(对应之前的TestSetPageLocked)page_cache_release(page): 释放页面缓存引用(对应之前的page_cache_get)
关键设计要点
- 引用计数管理: 精细控制交换条目和页面的引用计数
- 锁顺序: 正确获取和释放各种锁
- 条件释放: 只在特定条件下才从交换缓存中删除页面
- 内存回收优化: 在交换空间满时更积极地释放页面