探索Linux内核中物理内存管理的奥秘
在Linux内核中,页面分配器(Page Allocator)是内存管理的核心组件之一。它负责分配和释放物理内存页,支持从单个页面到多个连续页面的分配。本实验将深入探讨如何分配不同阶数(order)的连续物理页,并通过read操作查看分配的信息。
通过这个实验,您将学习:
Linux内核的页面分配器使用伙伴系统(Buddy System)来管理物理内存。伙伴系统将内存分成多个块,每个块的大小是2的阶数次幂。阶数(order)决定了分配块的大小:
阶数 (Order) | 页面数量 | 大小 (字节, 4KB页) |
---|---|---|
0 | 1 | 4096 |
1 | 2 | 8192 |
2 | 4 | 16384 |
3 | 8 | 32768 |
4 | 16 | 65536 |
5 | 32 | 131072 |
6 | 64 | 262144 |
7 | 128 | 524288 |
8 | 256 | 1048576 |
9 | 512 | 2097152 |
10 | 1024 | 4194304 |
分配连续物理页时,高阶数的分配可能失败,因为系统可能没有足够的连续空闲内存。
我们将创建一个字符设备驱动,实现以下功能:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/mm.h>
#define MAX_ORDER 10
#define DEVICE_NAME "page_alloc_test"
struct page_allocation {
unsigned int order;
struct page *page;
void *virt_addr;
phys_addr_t phys_addr;
bool allocated;
};
static struct page_allocation allocations[MAX_ORDER + 1];
static int allocate_pages(void)
{
int order;
gfp_t gfp_mask = GFP_KERNEL | __GFP_ZERO;
for (order = 0; order <= MAX_ORDER; order++) {
allocations[order].order = order;
allocations[order].page = alloc_pages(gfp_mask, order);
if (!allocations[order].page) {
pr_err("Failed to allocate order %d pages\n", order);
allocations[order].allocated = false;
continue;
}
allocations[order].allocated = true;
allocations[order].virt_addr = page_address(allocations[order].page);
allocations[order].phys_addr = page_to_phys(allocations[order].page);
pr_info("Allocated order %d: %lu pages, virt %p, phys %pap\n",
order, (1UL << order),
allocations[order].virt_addr,
&allocations[order].phys_addr);
}
return 0;
}
static ssize_t dev_read(struct file *filp, char __user *buf,
size_t count, loff_t *f_pos)
{
char output[1024];
int len = 0;
int order;
if (*f_pos > 0)
return 0;
len += snprintf(output + len, sizeof(output) - len,
"Order | Pages | Physical Address | Virtual Address | Status\n");
len += snprintf(output + len, sizeof(output) - len,
"------|-------|------------------|-----------------|--------\n");
for (order = 0; order <= MAX_ORDER; order++) {
if (allocations[order].allocated) {
len += snprintf(output + len, sizeof(output) - len,
"%-5d | %-5lu | %-16pap | %-15p | Success\n",
order, (1UL << order),
&allocations[order].phys_addr,
allocations[order].virt_addr);
} else {
len += snprintf(output + len, sizeof(output) - len,
"%-5d | %-5lu | %-16s | %-15s | Failed\n",
order, (1UL << order), "N/A", "N/A");
}
}
if (copy_to_user(buf, output, len))
return -EFAULT;
*f_pos = len;
return len;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = dev_read,
};
cat /dev/page_alloc_test
注意:在QEMU环境中测试时,可以通过调整虚拟机内存大小来观察不同内存压力下的分配行为。
Order | Pages | Physical Address | Virtual Address | Status
------|-------|------------------|-----------------|--------
0 | 1 | 0x0000000012345000 | 0xffff888012345000 | Success
1 | 2 | 0x0000000012346000 | 0xffff888012346000 | Success
2 | 4 | 0x0000000012348000 | 0xffff888012348000 | Success
3 | 8 | 0x000000001234c000 | 0xffff88801234c000 | Success
4 | 16 | 0x0000000012354000 | 0xffff888012354000 | Success
5 | 32 | 0x0000000012364000 | 0xffff888012364000 | Success
6 | 64 | 0x0000000012384000 | 0xffff888012384000 | Success
7 | 128 | 0x00000000123c4000 | 0xffff8880123c4000 | Success
8 | 256 | 0x0000000012444000 | 0xffff888012444000 | Success
9 | 512 | 0x0000000012544000 | 0xffff888012544000 | Success
10 | 1024 | N/A | N/A | Failed
通过本实验,我们实现了:
扩展思考: