2023921


内存管理

linux内核设计与实现第12章笔记

1.页

2.区

对相似特性的页分组

目的:某些硬件存在缺陷,无法寻址

  • 只能用特定内存DMA
  • 物理寻址范围大于虚拟寻址范围

四种区

  • ZONE_DMA
  • ZONE_DMA32
  • ZONE_NORMAL
  • ZONE_HIGHEM:高端内存,页不能永久映射到内核地址

不一定要定义所有区

3.获得页

底层接口<linux/gfp.h>,函数:

struct pages *alloc_pages(gfp_mask, order)

分配1<<order个连续的物理页,返回第一个页的struct结构体

void * page_address(struct page *page)

返回指针,指向给定物理页的逻辑地址

unsigned long __get_free_pages(fgp_mask, order)

与alloc_pages一样,返回第一个页的逻辑地址

获得填充为0的页

unsigned long get_zeroed_page(gfp_mask)

释放页

8个页的使用与释放

page = __get_free_pages(GFP_KERNEL, 3);
if (!page)&#123;
    return -ENOMEM;
&#125;
/* page指向逻辑地址 */
free_pages(page, 3);

4.kmalloc

void *kmalloc(size_t size, gfp_t flags)

分配的内存区,物理上连续

如果是NULL,需要处理错误

调用成功,返回逻辑地址,内存块的大小至少为请求的大小

gfp_mask标志

分为行为修饰符,区修饰符及类型

  1. 行为修饰符

    描述分配器的具体特征

    例子

    ptr = kmalloc(size, __GFP_WAIT|__GFP_IO|__GFP_FS);
    

    可以阻塞,执行IO,必要时可以执行FS操作

  2. 区修饰符

    默认从ZONE_NORMAL分配

    不能给get_free_pages(),kmalloc()指定ZONE_HIGHMEM,因为不能返回固定的逻辑地址

  3. 类型标志

    指定所需的行为和区描述符(多个的复合)

    标志 修饰符标志
    GFP_KERNEL (__GFP_WAIT|__GFP_IO|__GFP_FS)
    GFP_ATOMIC __GFP_HIGH
    GFP_NOIO __GFP_WAIT
    GFP_NOFS __GFP_WAIT|__GFP_IO

    GFP_KERNEL:可能睡眠,普通优先级,最常用

    可以用在可以重新安全调度的进程context

    GFP_ATOMIC: 不能干任何事,分配成功机会较小,适用于不能睡眠的情况(中断处理程序,软中断,tasklet)

    GFP_NOFS:用在FS代码中,如果没有用GFP_NOFS,会引起更多的文件系统操作,形成套娃,最后死锁

    绝大多数代码中,要么GFP_KERNEL,要么GFP_ATOMIC

kfree()

5.vmalloc()

类似kmalloc(),前者的虚拟地址连续,物理地址不需要连续

大多数情况,只有硬件设备需要物理地址连续的设备

kmalloc有更好的性能,因此vmalloc不得已才会使用-》获得大段内存

例如模块被动态插入到内核中

用法与用户空间一样:void *vmalloc(unsigned long size)

void vfree(const void*addr)

这个函数可以睡眠,没有返回值

6.slab层

空闲链表:相当于对象高速缓存

slab扮演通用数据结构缓存层的角色

设计思想

不同的对象划分为高速缓存组,每种对象对应一个高速缓存

kmalloc建立在slab层上,使用一组cache

slab:一个多个物理上连续的页组成,一般情况下仅仅一页

把用的,不用的对象组织起来

例子:inode用slab管理, struct inode用inode_cachep高速缓存

每个高速缓存用kmem_cache结构表示,包含三个链表:slabs_full,partial,empty

每个slab用slab描述符 struct slab表示

struct slab {
    struct list_head list;
    unsigned long colouroff; //着色的偏移量
    void *s_mem; //第一个对象
    unsigned inuse; //已分配的对象
    kmem_bufctl_t free; //第一个空闲对象
};

slab描述符:要么在slab外另行分配,要么在slab自身开始的地方。

slab为高速缓存创建新的slab,通过__get_free_pages()

void *kmem_getpages(struct kmem_cahce *cachep, gfp_t flags, int nodeid)

nodeid:用于NUMA系统,提供较好性能

返回一个slab大小的空页

slab层的管理:在每个高速缓存的基础上,给整个内核一个简单的接口完成

通过接口,创建撤销新的高速缓存

slab分配器的接口

高速缓存的创建

struct kmem_cache * kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor) (void*));

flags:

  • SLAB_HWCACHE_ALIGN:所有对象,行对齐,防止错误的共享
  • SLAB_POISON:用a5a5a5a5填充slab
  • SLAB_RED_ZONE:红色警戒区,检测缓冲越界
  • SLAB_CACHE_DMA:用于DMA

ctor:构造函数,linux不需要

不能在中断context中使用->可能睡眠

高速缓存的撤销

int kmem_cache_destroy(struct kmem_cache *cachep)

调用条件:

  • 所有slab为空,不能被分配

  • 调用destroy过程中不再访问这个高速缓存

1.从缓存中分配

创建cachep后可从下面函数获取对象:

void * kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags)

从指定的cachep获得一个指向对象的指针

flags:针对cachep没有空闲对象时, getpages的情况

释放对象

void * kmem_cache_free(struct kmem_cache *cachep, void *objp)

2.task_struct使用实例


文章作者: N1co5in3
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 N1co5in3 !
  目录