简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长!
优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀
优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀
优质视频课程:AAOS车载系统+AOSP14系统攻城狮入门实战课【原创干货持续更新中……】🚀
人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
🍉🍉🍉文章目录🍉🍉🍉
🌻1.前言
本篇目的:Linux内核之kfree实现及用法实例
🌻2.Linux内核之kfree介绍
- 在Linux内核中,
kfree
是一个用于释放内存的函数,它用于释放通过kmalloc
、kzalloc
、vmalloc
等函数分配的内存。这些分配函数在内核空间中动态地分配内存,而kfree
则用于在不再需要这些内存时将其释放回系统。 - 当一个内核函数需要一些临时内存来存储数据时,它会调用
kmalloc
或kzalloc
(kzalloc
是kmalloc
的一个变体,它会在分配内存后将其清零)。这些函数返回一个指向分配的内存的指针。当这些内存不再需要时,为了防止内存泄漏,必须使用kfree
来释放这些内存。 kfree
函数的原型定义在内核源代码的include/linux/slab.h
文件中,如下所示:
void kfree(const void *objp);
- 这里,
objp
是指向要释放的内存块的指针。kfree
函数会检查这个指针,确定内存块的大小和类型,然后将其返回到适当的内存池中,以便可以重新用于未来的分配请求。 kfree
的工作原理与用户空间中的free
函数类似,但它适用于内核空间。在内核中,内存管理比用户空间更为复杂,因为内核需要处理多种不同类型的内存分配,并且必须保证内存的分配和释放是快速和高效的,以避免影响系统的整体性能。kfree
在执行释放操作时,会执行以下几个步骤:
- 检查传递给
kfree
的指针是否为NULL
。如果是NULL
,则函数直接返回,因为释放NULL
指针没有意义。 - 确定
objp
指向的内存块的大小和类型。这是通过查看内存块的头部信息来实现的,这些信息在内存块被分配时由kmalloc
等函数设置。 - 将内存块从相应的内存池中释放。这涉及到更新内存池的数据结构,以反映内存块的空闲状态。
- 如果可能,将释放的内存块合并到相邻的空闲内存块中,以减少内存碎片。
- 更新内核的内存使用统计信息,以反映释放的内存。
kfree
是Linux内核中非常重要的一个函数,它确保了内核可以有效地管理其内存资源,避免内存泄漏和内存碎片的问题。正确使用kfree
和其他内存分配函数对于维护内核的稳定性和性能至关重要。- 在编写内核代码时,开发人员必须确保对于每一个
kmalloc
或kzalloc
调用,都有一个对应的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");