[Linux内核驱动]内存动态申请

内核空间内存动态申请

更多详细内容可以查看我的github

kmalloc()

函数原型:

void *kmalloc(size_t size, gfp_t flags);

参数说明:

  • size:要分配的内存块的大小,以字节为单位。
  • flags:分配标志,用于指定内存分配的策略和属性。
    • GFP_KERNEL:在内核空间的进程中分配内存
    • GFP_ATOMIC:在原子上下文中分配内存

kmalloc()的底层依赖于__get_free_page()来实现,当使用 GFP_KERNEL 申请内存时,若暂时不能满足,则进程会睡眠等待页,引起阻塞,因此不能在 中断上下文 或持有 自选锁 的时候使用GFP_KERNEL申请内存。对于这些在非进程上下文中不能阻塞的情况,应当使用GFP_ATOMIC申请内存,若不存在空闲页,则不等待,直接返回。

kmalloc()类似,kzalloc() 函数也用于分配内存,区别在于kzalloc()函数会在分配的内存块中清零。

kmalloc()kzalloc()申请的内存都使用 kfree() 函数释放

__get_free_pages()

__get_free_pages()系列函数/宏本质上是Linux内核最底层用户获取空闲内存的方法。因为底层的buddy算法以2n页的方式管理内存,所以底层申请内存是以2n页为单位。n最大为10或11

  • __get_free_pages(gfp_mask, int order):分配2^order页的内存。
  • __get_free_page(gfp_mask):分配一个页(4KB)的内存。
    • 这个宏实际上就是调用__get_free_pages分配1页内存
  • get_zeroed_page(gfp_mask):分配并清零一个页的内存。

在可能阻塞的上下文中(如进程上下文),应使用允许阻塞的标志(GFP_KERNEL)。在不允许阻塞的上下文中(如中断上下文或持有自旋锁时),应使用不允许阻塞的标志(GFP_ATOMICGFP_NOWAIT)。

上面的申请的内存使用free_page(addr)free_pages(addr, order)释放。

vmalloc()

vmalloc()函数用于在内核中分配大的、连续的虚拟地址空间,但物理地址可能不连续。这通常用于需要连续虚拟地址但不需要物理连续性的情况,如大型数据结构或设备驱动程序的缓冲区。vmalloc()需要建立新的页表项,所以其开销很大。此外,vmalloc()不能应用在原子上下文中,因为其内部实现使用了GFP_KERNEL标志的kmalloc()

函数原型:

void *vmalloc(unsigned long size);
void vfree(void *addr);

slab分配器

在Linux中,伙伴分配器(buddy allocator)是以页为单位管理和分配内存。但在内核中的需求,一方面,完全使用页为单位申请内存严重浪费内存;另一方面,Linux运行过程中经常会涉及到大量重复对象的重复生成、使用和释放内存问题(task_struct、inode等)。

为了解决这个问题,Linux内核引入了slab分配器。slab分配器以字节为单位,从 Buddy 分配器中申请内存,之后对申请来的内存细分管理。

struct kmem_cache

struct kmem_cache:这是Slab分配器的一部分,它提供了一种用于快速分配和释放固定大小内存块的机制。Slab分配器为每种大小的对象维护一个缓存池,从而提高了内存分配的效率。

kmem_cache_create()

创建一个新的kmem_cache实例。

函数原型:

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

参数说明:

  • name: 缓存的名称,用于调试和日志记录。
  • size: 缓存中每个对象的大小(以字节为单位)。
  • align: 每个对象的对齐要求(通常设置为0,让slab分配器选择最佳的对齐)。
  • flags: 标志位,用于控制缓存的行为
    • SLAB_HWCACHE_ALIGN:表示对象应该按硬件缓存行对齐,即对齐到一个缓存行
    • SLAB_RECLAIM_DMA:表示在DMA区域中分配
  • ctor: 构造函数(可选),当从缓存中分配对象时,此函数将被调用以初始化对象。

返回值:

  • 成功时返回一个指向新创建的 kmem_cache 结构体的指针。
  • 失败时返回 NULL。

kmem_cache_alloc()

从指定的kmem_cache中分配内存

函数原型:

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

参数说明:

  • cachep: 之前通过 kmem_cache_create() 创建的 kmem_cache 结构体的指针。
  • flags: GFP(Get Free Pages)标志,用于控制内存分配的行为。

返回值:

  • 成功时返回一个指向新分配对象的指针。
  • 失败时(例如,由于内存不足)返回 NULL。

kmem_cache_free()

释放kmem_cache_alloc申请的内存

函数原型:

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

参数说明:

  • cachep: 与对象关联的 kmem_cache 结构体的指针。
  • objp: 要释放的对象的指针。

kmem_cache_destroy()

销毁一个kmem_cache实例。

函数原型:

void kmem_cache_destroy(struct kmem_cache *cachep);

代码

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/gfp.h>
#include <linux/vmalloc.h>

struct mem_dev
{
  char* test;
  struct kmem_cache* cache;
};

struct mem_dev* mem_devp;

static int __init mem_dev_init_module(void)
{
  printk(KERN_INFO "mem_init\n");

  mem_devp = kmalloc(sizeof(struct mem_dev), GFP_KERNEL);

  // kmalloc
  mem_devp->test = kmalloc(1024, GFP_KERNEL);
  if (!mem_devp->test)
  {
    return -ENOMEM;
  }
  printk(KERN_INFO "kmalloc get addr %p\n", mem_devp->test);
  kfree(mem_devp->test);

  // __get_free_page
  mem_devp->test = (void*)__get_free_page(GFP_KERNEL);
  if (!mem_devp->test)
  {
    return -ENOMEM;
  }
  printk(KERN_INFO "__get_free_page get addr %p\n", mem_devp->test);
  free_page((unsigned long)mem_devp->test);

  // vmalloc
  mem_devp->test = vmalloc(PAGE_SIZE * 16);
  if (!mem_devp->test)
  {
    return -ENOMEM;
  }
  printk(KERN_INFO "vmalloc get addr %p\n", mem_devp->test);
  vfree(mem_devp->test);

  // slab
  mem_devp->cache = kmem_cache_create("mem_cache", sizeof(struct mem_dev), 0, SLAB_HWCACHE_ALIGN, NULL);
  if (!mem_devp->cache)
  {
    return -ENOMEM;
  }
  mem_devp->test = kmem_cache_alloc(mem_devp->cache, GFP_KERNEL);
  if (!mem_devp->test)
  {
    return -ENOMEM;
  }
  printk(KERN_INFO "slab get addr %p\n", mem_devp->test);
  kmem_cache_free(mem_devp->cache, mem_devp->test);
  kmem_cache_destroy(mem_devp->cache);

  kfree(mem_devp);
  return 0;
}

static void mem_dev_exit_module(void)
{
  printk(KERN_INFO "mem_dev exit\n");
}

module_init(mem_dev_init_module);
module_exit(mem_dev_exit_module);

MODULE_AUTHOR("lidonghang-02");
MODULE_LICENSE("GPL");

  • 26
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值