mmap对get_free_pages 等的不同做法

`__alloc_pages` 是 Linux 内核内存管理子系统中最核心的函数之一,它是 **页分配器(page allocator)** 的主入口点,负责从物理内存中分配一个或多个连续的物理页面(通常为 4KB 每页)。它被 `kmalloc`、`vmalloc`、`mmap` 等上层接口调用。 --- ## ✅ 函数原型 ```c struct page * __alloc_pages(gfp_t gfp_mask, unsigned int order, struct zonelist *zonelist); ``` ### 参数说明: | 参数 | 含义 | |------|------| | `gfp_mask` | 分配标志,控制分配行为(如是否可睡眠、是否清零等) | | `order` | 要分配的页数以 $2^{order}$ 表示。例如:<br> - `order=0` → 1 页(4KB)<br> - `order=3` → 8 页(32KB) | | `zonelist` | 内存区域列表,决定从哪些 zone 开始尝试分配(如 ZONE_NORMAL、ZONE_DMA) | ### 返回值: - 成功:返回指向 `struct page` 的指针(描述物理页) - 失败:返回 `NULL` --- ## 🔧 工作原理详解 `__alloc_pages` 的实现位于 `mm/page_alloc.c`,其流程非常复杂,支持多种路径和回退机制。 以下是简化版执行流程图: ``` __alloc_pages │ ┌───────────────▼───────────────┐ │ 1. 快速路径(fast path) │ │ 检查 per-cpu 缓存和本地 zone │ └───────────────┬───────────────┘ │ ┌───────────────▼───────────────┐ │ 2. 获取合适的内存区域 │ │ 根据 gfp_mask 和 zonelist │ └───────────────┬───────────────┘ │ ┌───────────────▼───────────────┐ │ 3. 尝试直接分配(direct reclaim)│ │ 如果当前允许回收内存 │ └───────────────┬───────────────┘ │ ┌───────────────▼───────────────┐ │ 4. 回收失败?触发内存压缩 │ │ (kcompactd 唤醒) │ └───────────────┬───────────────┘ │ ┌───────────────▼───────────────┐ │ 5. 最终回退:OOM killer │ │ 如果所有尝试都失败 │ └───────────────────────────────┘ ``` --- ## 📚 代码示例:如何使用? 虽然驱动开发者一般不直接调用 `__alloc_pages`,但理解它的使用方式有助于掌握底层机制。 ### 示例:分配 8 个连续页面(order=3) ```c #include <linux/mm.h> #include <linux/gfp.h> struct page *page; unsigned int order = 3; // 2^3 = 8 pages = 32KB gfp_t mask = GFP_KERNEL; // 可睡眠,常规分配 page = __alloc_pages(mask, order, NODE_DATA(0)->node_zonelists + 0); if (!page) { printk(KERN_ERR "Failed to allocate pages\n"); } else { void *addr = page_address(page); // 获取虚拟地址 memset(addr, 0, (1 << order) * PAGE_SIZE); // 清零 printk(KERN_INFO "Allocated at %p\n", addr); } ``` > ⚠️ 注意:实际开发中应优先使用更高级接口如 `kmalloc()` 或 `vmalloc()`,避免直接操作 `__alloc_pages`。 --- ## 🎯 关键特性解析 ### 1. `gfp_mask` 标志详解 | 标志 | 含义 | |------|------| | `GFP_KERNEL` | 普通内核分配,可睡眠 | | `GFP_ATOMIC` | 原子上下文(中断处理),不可睡眠 | | `GFP_DMA` | 分配可在 DMA 区域使用的内存 | | `__GFP_ZERO` | 自动清零返回页 | | `__GFP_HIGHMEM` | 允许分配高端内存(HighMem) | | `__GFP_NOWARN` | 不打印分配失败警告 | 组合示例: ```c GFP_KERNEL | __GFP_ZERO // 常规分配并清零 GFP_ATOMIC // 中断中快速分配 GFP_KERNEL & ~__GFP_DIRECT_RECLAIM // 禁止阻塞 ``` --- ### 2. `order` 的限制 - 最大 `order` 受限于最大连续空闲块大小。 - 典型最大为 `order=10`(4MB),超过容易失败。 - 使用 `buddy allocator` 管理不同阶的内存块。 --- ### 3. 内存回收与 OOM 当 `__alloc_pages` 发现没有足够空闲页时,会触发以下动作: #### 回收路径(Reclaim Path): - 调用 `kswapd` 回收页缓存(page cache) - 回收 slab 对象(如 dentry、inode) - 如果仍不足,则唤醒 `kcompactd` 进行内存压缩 #### OOM Killer: 如果所有手段都失败,且系统处于严重内存压力下,最终会调用 `oom_killer` 终止某个进程来释放内存。 可通过以下命令查看是否发生过 OOM: ```bash dmesg | grep -i oom # 输出示例: # [12345.67890] Out of memory: Kill process 1234 (myservice) ... ``` --- ## 🔍 调试技巧 ### 查看当前内存状态 ```bash cat /proc/buddyinfo ``` 输出示例: ``` Node 0, zone DMA 1 2 3 ... Node 0, zone Normal 10 20 5 ... Node 0, zone HighMem 0 1 0 ... ``` 每个数字表示对应 `order` 的空闲页块数量。 比如 `order=3` 有 5 个空闲块 → 最多可分配 5 × 8 = 40 个页(不分裂情况下)。 --- ### 跟踪 `__alloc_pages` 调用 使用 ftrace: ```bash echo function > /sys/kernel/debug/tracing/current_tracer echo __alloc_pages > /sys/kernel/debug/tracing/set_ftrace_filter echo 1 > /sys/kernel/debug/tracing/tracing_on cat /sys/kernel/debug/tracing/trace_pipe ``` 输出示例: ``` migration/1-12 [001] .... 1234.567890: __alloc_pages -> 0xffffea0001234567 ``` --- ## ✅ 总结 | 特性 | 说明 | |------|------| | 所在文件 | `mm/page_alloc.c` | | 主要功能 | 分配物理页(基于 buddy system) | | 上层调用者 | `kmalloc`, `__get_free_pages`, `vmalloc`, `mmap` | | 底层机制 | Buddy Allocator + Per-CPU 缓存 + Reclaim + Compaction | | 失败处理 | 回收 → 压缩 → OOM Killer | | 推荐替代方案 | 用户态用 `malloc`;内核用 `kmalloc/vmalloc` | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值