元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
进程管理
▶
进程调度
调度策略
完全公平调度器
实时调度机制
▶
进程通信
信号机制
共享内存
管道与命名管道
▶
进程状态
上下文切换
僵尸进程处理
进程生命周期
▶
内存管理
▶
虚拟内存
页表机制
地址空间布局
TLB管理
▶
物理内存
伙伴系统
slab分配器
内存碎片处理
▶
文件系统
▶
VFS抽象层
inode机制
文件描述符
挂载机制
▶
具体文件系统
Ext4日志机制
Btrfs特性
Proc文件系统
▶
设备驱动
▶
驱动模型
设备树解析
sysfs接口
热插拔处理
▶
网络栈
▶
协议栈架构
sk_buff结构
Netfilter框架
套接字实现
▶
内核同步机制
▶
锁机制
自旋锁实现
RCU机制
Mutex与信号量
▶
启动过程
▶
引导阶段
GRUB交互
内核解压缩
早期初始化
▶
系统调用
▶
调用机制
陷入门实现
vsyscall优化
安全审计
▶
虚拟化和容器支持
▶
容器技术
命名空间隔离
cgroups实现
OverlayFS原理
发布时间:
2025-03-23 10:44
↑
☰
# Linux伙伴系统详解 伙伴系统(Buddy System)是Linux物理内存管理的核心机制之一,它通过将连续的物理页面分组为不同的阶(order)来实现高效的内存分配和回收。本文将详细介绍Linux系统中的伙伴系统机制。 ## 基本概念 ### 页面管理 1. 页面大小 ```c #define PAGE_SHIFT 12 #define PAGE_SIZE (1UL << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) ``` 2. 页面描述符 ```c struct page { unsigned long flags; // 页面标志 atomic_t _count; // 引用计数 atomic_t _mapcount; // 映射计数 unsigned long private; // 私有数据 struct address_space *mapping; // 地址空间 pgoff_t index; // 页面索引 struct list_head lru; // LRU链表 void *virtual; // 虚拟地址 }; ``` ### 伙伴系统结构 1. 空闲区域 ```c struct free_area { struct list_head free_list[MIGRATE_TYPES]; unsigned long nr_free; }; ``` 2. 管理结构 ```c struct zone { struct free_area free_area[MAX_ORDER]; unsigned long free_pages; unsigned long spanned_pages; unsigned long present_pages; // ... }; ``` ## 内存分配 ### 分配流程 1. 基本分配函数 ```c struct page *alloc_pages(gfp_t gfp_mask, unsigned int order) { struct page *page; // 从伙伴系统分配页面 page = __alloc_pages_node(numa_node_id(), gfp_mask, order); return page; } ``` 2. 分配标志 ```c // 分配标志 #define GFP_KERNEL 0x10u // 内核分配 #define GFP_ATOMIC 0x20u // 原子分配 #define GFP_USER 0x40u // 用户分配 #define GFP_HIGHUSER 0x80u // 高端内存 ``` ### 分配策略 1. 快速路径 ```c static struct page * get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags, const struct alloc_context *ac) { struct zone *zone; struct page *page; // 遍历内存域 for_each_zone_zonelist_nodemask(zone, z, ac->zonelist, ac->high_zoneidx, ac->nodemask) { // 检查空闲页面 page = rmqueue(ac->preferred_zoneref->zone, order, gfp_mask); if (page) return page; } return NULL; } ``` 2. 慢速路径 ```c static struct page * __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, struct alloc_context *ac) { struct page *page; // 唤醒kswapd进行回收 page = wake_all_kswapd(order, ac); if (page) return page; // 直接回收 page = __perform_reclaim(gfp_mask, order, ac); if (page) return page; // OOM处理 out_of_memory(ac->zonelist, gfp_mask, order, ac->nodemask, false); return NULL; } ``` ## 内存释放 ### 释放流程 1. 基本释放函数 ```c void __free_pages(struct page *page, unsigned int order) { if (put_page_testzero(page)) { // 释放到伙伴系统 free_the_page(page, order); } } ``` 2. 合并处理 ```c static inline void free_the_page(struct page *page, unsigned int order) { // 获取页面所在区域 struct zone *zone = page_zone(page); // 合并伙伴页面 __free_one_page(page, pfn_to_page(page_to_pfn(page)), zone, order, migratetype); } ``` ### 合并策略 1. 伙伴查找 ```c static inline unsigned long find_buddy_pfn(unsigned long pfn, unsigned int order) { return pfn ^ (1 << order); } ``` 2. 页面合并 ```c static inline void __free_one_page(struct page *page, unsigned long pfn, struct zone *zone, unsigned int order, int migratetype) { unsigned long buddy_pfn; unsigned long combined_pfn; struct page *buddy; // 循环合并伙伴 while (order < MAX_ORDER-1) { buddy_pfn = find_buddy_pfn(pfn, order); buddy = page + (buddy_pfn - pfn); // 检查伙伴是否可合并 if (!page_is_buddy(page, buddy, order)) break; // 从空闲链表移除伙伴 list_del(&buddy->lru); zone->free_area[order].nr_free--; // 合并页面 combined_pfn = buddy_pfn & pfn; page = page + (combined_pfn - pfn); pfn = combined_pfn; order++; } // 将合并后的页面加入空闲链表 list_add(&page->lru, &zone->free_area[order].free_list[migratetype]); zone->free_area[order].nr_free++; } ``` ## 内存碎片化 ### 碎片类型 1. 内部碎片 - 分配单元内部的未使用空间 - 由于固定大小分配导致 2. 外部碎片 - 空闲内存不连续 - 无法满足大内存请求 ### 反碎片化 1. 迁移类型 ```c enum migratetype { MIGRATE_UNMOVABLE, // 不可移动 MIGRATE_MOVABLE, // 可移动 MIGRATE_RECLAIMABLE, // 可回收 MIGRATE_PCPTYPES, // per-cpu类型 MIGRATE_TYPES // 类型数量 }; ``` 2. 页面迁移 ```c int migrate_pages(struct list_head *from, struct list_head *to, new_page_t get_new_page, free_page_t put_new_page, unsigned long private, enum migrate_mode mode, int reason) { int retry = 1; int nr_failed = 0; int nr_succeeded = 0; int pass = 0; struct page *page; struct page *page2; // 迁移页面 while (retry) { retry = 0; list_for_each_entry_safe(page, page2, from, lru) { if (!migrate_page(get_new_page, put_new_page, private, page, mode)) { nr_succeeded++; } else { nr_failed++; } } pass++; if (pass > 2) break; retry = nr_failed; } return nr_failed; } ``` ## 调试与监控 ### 内存信息 1. /proc文件系统 ```bash # 查看伙伴系统信息 cat /proc/buddyinfo # 查看内存使用信息 cat /proc/meminfo ``` 2. 内存统计 ```c struct zone_stats { atomic_long_t nr_alloc_failed; atomic_long_t nr_alloc_total; atomic_long_t nr_free_pages; }; ``` ### 调试支持 1. 内存检查 ```c void check_free_page_type(struct page *page, unsigned int order) { // 检查页面类型 if (page_is_guard(page)) bad_page(page); // 检查引用计数 if (page_count(page) != 0) bad_page(page); // 检查映射计数 if (PageMappedToDisk(page)) bad_page(page); } ``` 2. 错误追踪 ```c void bad_page(struct page *page) { static unsigned long last_bad = 0; // 打印错误信息 if (time_before(last_bad, jiffies - 60*HZ)) { printk("Bad page state in process '%s'\n", current->comm); dump_page(page); last_bad = jiffies; } } ``` ## 最佳实践 1. 内存分配 - 选择合适的分配标志 - 避免大块内存分配 - 注意内存对齐 2. 内存释放 - 及时释放不用的内存 - 正确处理引用计数 - 避免内存泄漏 3. 碎片管理 - 使用合适的迁移类型 - 定期进行内存压缩 - 监控碎片状况 ## 总结 伙伴系统是Linux物理内存管理的基础,它通过将连续的物理页面分组为不同的阶来实现高效的内存分配和回收。理解伙伴系统的工作原理对于开发高性能的Linux应用程序和进行系统优化非常重要。在实际应用中,我们应该合理使用内存分配函数,注意内存碎片化问题,并通过各种工具进行监控和调试。