Linux内核之kfree实现及用法实例(四十九)

发布于:2024-04-18 ⋅ 阅读:(32) ⋅ 点赞:(0)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长!

优质专栏:Audio工程师进阶系列原创干货持续更新中……】🚀
优质专栏:多媒体系统工程师系列原创干货持续更新中……】🚀
优质视频课程:AAOS车载系统+AOSP14系统攻城狮入门实战课原创干货持续更新中……】🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.

更多原创,欢迎关注:Android系统攻城狮

欢迎关注Android系统攻城狮

🌻1.前言

本篇目的:Linux内核之kfree实现及用法实例

🌻2.Linux内核之kfree介绍

  • 在Linux内核中,kfree是一个用于释放内存的函数,它用于释放通过kmallockzallocvmalloc等函数分配的内存。这些分配函数在内核空间中动态地分配内存,而kfree则用于在不再需要这些内存时将其释放回系统。
  • 当一个内核函数需要一些临时内存来存储数据时,它会调用kmallockzallockzallockmalloc的一个变体,它会在分配内存后将其清零)。这些函数返回一个指向分配的内存的指针。当这些内存不再需要时,为了防止内存泄漏,必须使用kfree来释放这些内存。
  • kfree函数的原型定义在内核源代码的include/linux/slab.h文件中,如下所示:
void kfree(const void *objp);
  • 这里,objp是指向要释放的内存块的指针。kfree函数会检查这个指针,确定内存块的大小和类型,然后将其返回到适当的内存池中,以便可以重新用于未来的分配请求。
  • kfree的工作原理与用户空间中的free函数类似,但它适用于内核空间。在内核中,内存管理比用户空间更为复杂,因为内核需要处理多种不同类型的内存分配,并且必须保证内存的分配和释放是快速和高效的,以避免影响系统的整体性能。
  • kfree在执行释放操作时,会执行以下几个步骤:
  1. 检查传递给kfree的指针是否为NULL。如果是NULL,则函数直接返回,因为释放NULL指针没有意义。
  2. 确定objp指向的内存块的大小和类型。这是通过查看内存块的头部信息来实现的,这些信息在内存块被分配时由kmalloc等函数设置。
  3. 将内存块从相应的内存池中释放。这涉及到更新内存池的数据结构,以反映内存块的空闲状态。
  4. 如果可能,将释放的内存块合并到相邻的空闲内存块中,以减少内存碎片。
  5. 更新内核的内存使用统计信息,以反映释放的内存。
  • kfree是Linux内核中非常重要的一个函数,它确保了内核可以有效地管理其内存资源,避免内存泄漏和内存碎片的问题。正确使用kfree和其他内存分配函数对于维护内核的稳定性和性能至关重要。
  • 在编写内核代码时,开发人员必须确保对于每一个kmallockzalloc调用,都有一个对应的kfree调用,以释放不再需要的内存。

🌻3.kfree调用流程

🐓3.1 kfree函数实现

void kfree(const void *block)
{
	struct page *sp;

	trace_kfree(_RET_IP_, block);

	if (unlikely(ZERO_OR_NULL_PTR(block)))
		return;
	kmemleak_free(block);

	sp = virt_to_page(block);
	if (PageSlab(sp)) {
		int align = max_t(size_t, ARCH_KMALLOC_MINALIGN, ARCH_SLAB_MINALIGN);
		unsigned int *m = (unsigned int *)(block - align);
		slob_free(m, *m + align);
	} else
		__free_pages(sp, compound_order(sp));
}
EXPORT_SYMBOL(kfree);
  • 通过virt_to_page函数将block内核虚拟地址转换为对应的页结构指针,页是管理内存的基本单位,每个物理页帧都有一个对应的struct page结构来描述它。当内核需要操作物理内存时,通常会通过这个结构来进行。
  • 通过__free_pages或slob_free函数来释放内存。

🌻4.kfree代码实例

🐓4.1 基本使用

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>

static int __init kfree_example_init(void)
{
    void *ptr = kmalloc(1024, GFP_KERNEL); // 分配1KB内存
    if (!ptr) {
        pr_err("kmalloc failed\n");
        return -ENOMEM;
    }

    // 使用ptr指向的内存...

    kfree(ptr); // 释放内存
    return 0;
}

static void __exit kfree_example_exit(void)
{
}

module_init(kfree_example_init);
module_exit(kfree_example_exit);

MODULE_LICENSE("GPL");

🐓4.2 错误处理

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>

static int __init kfree_example_init(void)
{
    void *ptr = kzalloc(1024, GFP_KERNEL); // 分配并清零1KB内存
    if (!ptr) {
        pr_err("kzalloc failed\n");
        return -ENOMEM;
    }

    // 使用ptr指向的内存...

    // 假设我们在某个地方遇到了错误,需要释放内存并返回错误
    if (some_error_condition) {
        kfree(ptr);
        return -EFAULT;
    }

    kfree(ptr); // 正常释放内存
    return 0;
}

static void __exit kfree_example_exit(void)
{
}

module_init(kfree_example_init);
module_exit(kfree_example_exit);

MODULE_LICENSE("GPL");

🐓4.3 链表操作

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/list.h>

struct my_data {
    int value;
    struct list_head list;
};

static LIST_HEAD(my_list); // 声明链表头

static int __init kfree_example_init(void)
{
    struct my_data *data;
    struct list_head *pos, *next;

    // 分配并初始化数据结构
    data = kmalloc(sizeof(*data), GFP_KERNEL);
    if (!data) {
        pr_err("kmalloc failed\n");
        return -ENOMEM;
    }
    data->value = 42;
    INIT_LIST_HEAD(&data->list);

    // 将数据添加到链表
    list_add(&data->list, &my_list);

    // 遍历链表并处理数据
    list_for_each_safe(pos, next, &my_list) {
        data = list_entry(pos, struct my_data, list);
        // 使用data...

        // 释放链表中的元素
        list_del(pos);
        kfree(data);
    }

    return 0;
}

static void __exit kfree_example_exit(void)
{
}

module_init(kfree_example_init);
module_exit(kfree_example_exit);

MODULE_LICENSE("GPL");