Skip to content

内存管理 API

本文档介绍 NoobKernel 内存管理模块的主要 API。

物理内存管理

页面分配

#include <mm/pm.h>

// 分配单个物理页
void *page_alloc(u8 flags);

// 释放物理页
int page_free(void *ptr);

// 地址转换为 page 结构
struct page *addr2page(void *addr);

// page 结构转换为地址
void *page2addr(struct page *page);

// 获取空闲页数
size_t get_free_pages_num();

flags 参数: - PM_BUDDY:由 Buddy 分配器管理 - PM_SLAB:由 Slab 分配器管理

示例

// 分配页面
void *page = page_alloc(PM_BUDDY);
if (!page) {
    errorf("Failed to allocate page");
    return -ENOMEM;
}

// 使用页面
memset(page, 0, PAGE_SIZE);

// 释放页面
page_free(page);

Buddy 分配器

#include <mm/buddy.h>

// 分配连续内存
void *buddy_alloc(size_t size);

// 释放内存
void buddy_free(void *ptr);

大小限制:4 KB ~ 8 MB(PAGE_SIZE ~ PAGE_SIZE << BUDDY_MAX_ORDER

示例

// 分配 64 KB
void *mem = buddy_alloc(64 * 1024);
if (!mem) {
    return -ENOMEM;
}

// 使用内存
...

// 释放
buddy_free(mem);

Slab 分配器

#include <mm/slab.h>

// 初始化 kmem_cache
int kmem_cache_init(struct kmem_cache *kmem, const char *name, 
                    size_t obj_size, bool with_initial_alloc);

// 从缓存分配对象
void *kmem_cache_alloc(struct kmem_cache *kmem);

// 释放对象到缓存
void kmem_cache_free(void *addr);

// 刷新缓存
void kmem_cache_flush(struct kmem_cache *kmem);

示例

static struct kmem_cache my_cache;

// 初始化缓存
kmem_cache_init(&my_cache, "my_object", sizeof(struct my_object), true);

// 分配对象
struct my_object *obj = kmem_cache_alloc(&my_cache);
if (!obj) {
    return -ENOMEM;
}

// 使用对象
obj->value = 42;

// 释放对象
kmem_cache_free(obj);

内核内存分配

#include <mm/kalloc.h>

// 分配内存
void *kmalloc(size_t size);

// 分配并清零
void *kzalloc(size_t size);

// 释放内存
void kfree(void *ptr);

大小限制: - ≤ 4 KB:使用 Slab 分配器 - ≤ 8 MB:使用 Buddy 分配器 - > 8 MB:不支持

示例

// 分配 256 字节
void *buf = kmalloc(256);
if (!buf) {
    return -ENOMEM;
}

// 分配并清零
struct my_struct *s = kzalloc(sizeof(struct my_struct));

// 释放
kfree(buf);
kfree(s);

页表管理

#include <mm/pagetable.h>

// 创建页表
pagetable_t pagetable_create(void);

// 销毁页表
void pagetable_destroy(pagetable_t pagetable);

// 映射页面
int mappages(pagetable_t pagetable, uintptr_t va, uintptr_t pa, 
             size_t npages, int perm);

// 取消映射
int unmappages(pagetable_t pagetable, uintptr_t va, size_t npages);

// 查找 PTE
pte_t *va2pte(pagetable_t pagetable, uintptr_t va, bool alloc);

// 虚拟地址转物理地址
uintptr_t walkaddr(pagetable_t pagetable, uintptr_t va);

权限标志: - PTE_R:可读 - PTE_W:可写 - PTE_X:可执行 - PTE_U:用户态可访问 - PTE_G:全局映射 - PTE_V:有效

示例

// 创建页表
pagetable_t pt = pagetable_create();

// 映射 16 页,可读写
int ret = mappages(pt, 0x10000000, 0x80000000, 16, PTE_R | PTE_W);
if (ret < 0) {
    errorf("mappages failed");
}

// 销毁页表
pagetable_destroy(pt);

虚拟内存区域

#include <mm/vma.h>

// 创建 VMA
struct vma *vma_create(uintptr_t start, size_t length, int perm, int type);

// 插入 VMA 到进程
int vma_insert(struct proc *proc, struct vma *vma);

// 查找 VMA
struct vma *vma_find(struct proc *proc, uintptr_t addr);

// 删除 VMA
void vma_delete(struct proc *proc, struct vma *vma);

VMA 类型: - VMA_ANON:匿名内存 - VMA_FILE:文件映射 - VMA_STACK:栈 - VMA_DEV:设备映射

缓冲区缓存

#include <mm/bcache.h>

// 读块
struct buf *bread(dev_t dev, u64 blockno);

// 写块
void bwrite(struct buf *b);

// 释放块
void brelse(struct buf *b);

示例

// 读取块 0
struct buf *b = bread(root_dev, 0);
if (!b) {
    return -EIO;
}

// 处理数据
process_data(b->data);

// 修改数据
b->data[0] = 0xFF;
b->dirty = true;
bwrite(b);

// 释放
brelse(b);

早期内存分配

#include <mm/early.h>

// 早期分配
void *early_alloc(size_t size);

// 检查是否为早期内存
bool is_early_mem(void *ptr);

使用场景:在 Slab/Buddy 初始化前使用。

内存布局

#include <mm/layout.h>

// 内核边界
extern char skernel[], ekernel[];

// 段边界
extern char s_text[], e_text[];
extern char s_rodata[], e_rodata[];
extern char s_data[], e_data[];
extern char s_bss[], e_bss[];

// 物理内存
#define PM_START  0x80000000ULL
#define PM_END    (PM_START + MEM_SIZE)
#define PAGE_SIZE 4096
#define PAGE_NUM  (MEM_SIZE / PAGE_SIZE)

错误码

内存管理相关错误码:

#include <misc/errno.h>

#define ENOMEM  2   // 内存不足
#define EINVAL  1   // 无效参数
#define EFAULT  14  // 地址错误

最佳实践

检查返回值

void *ptr = kmalloc(size);
if (!ptr) {
    errorf("kmalloc failed");
    return -ENOMEM;
}

匹配分配和释放

// 正确
void *p = kmalloc(256);
kfree(p);

// 错误:混用分配器
void *p = buddy_alloc(4096);
kfree(p);  // 错误!应该用 buddy_free

避免内存泄漏

// 使用 goto 清理
int foo() {
    void *a = kmalloc(256);
    if (!a) return -ENOMEM;

    void *b = kmalloc(256);
    if (!b) {
        kfree(a);
        return -ENOMEM;
    }

    // 使用 a 和 b
    ...

    kfree(b);
    kfree(a);
    return 0;
}

使用 kzalloc

// 推荐:清零初始化
struct my_struct *s = kzalloc(sizeof(struct my_struct));

// 不推荐:手动清零
struct my_struct *s = kmalloc(sizeof(struct my_struct));
memset(s, 0, sizeof(struct my_struct));