Linux内核内存分配函数kmalloc、kzalloc和vmalloc

news2025/1/11 19:52:36

在内核环境中,常用的内存分配函数主要有kmalloc、kzalloc和vmalloc这三个。既然这三函数都能在内核申请空间,那么这三个函数有什么区别呢?如何选用呢?

kmalloc

首先是kmalloc,其函数原型为

// /include/linux/slab.h
void *kmalloc(size_t size, gfp_t flags)

函数的特点:
申请的内存虚拟地址和物理地址都是连续的,允许申请的内存大小较小,具体的数值限制由平台和配置决定,具体可见kmalloc允许申请的内存大小。

常用的内存分配方法flags:

  • GFP_ATOMIC --表明分配内存的过程是原子的,不会被高优先级的进程或者中断打断
  • GFP_KERNEL – 常规分配
  • GFP_DMA – 给DMA分配内存时使用,使用该标志时分配的虚拟地址和物理地址都是连续的

常见的flags组合

  • 普通进程中,分配内存时可以睡眠: GFP_KERNEL
  • 普通进程中,分配内存时不可以睡眠: GFP_ATOMIC
  • 中断处理程序,tasklet:GFP_ATOMIC
  • DMA,可以睡眠:GFP_DMA | GFP_KERNEL
  • DMA,不可以睡眠:GFP_DMA | GFP_ATOMIC

kzalloc

与kmalloc基本一致,就是kmalloc的特例,是后者的封装,在flags与上了__GFP_ZERO这个标志,表示在分配完内存后对分配的内存进行清零,所以使用起来比较方便。

// /include/linux/slab.h
static inline __alloc_size(1) void *kzalloc(size_t size, gfp_t flags)
{
	return kmalloc(size, flags | __GFP_ZERO);
}

kzalloc和kmalloc所对应的内存释放函数都是kfree

void kfree(const void *objp);

vmalloc

函数原型

// /include/linux/vmalloc.h
void *vmalloc(unsigned long size);

vmalloc申请的内存的虚拟地址是连续的,但其对应的物理地址不是连续的,因此其申请的内存大小没有限制,因此申请较大的内存时可以使用这个函数。vmalloc申请内存的过程中可以睡眠,因此不能用于中断上下文中。

对应的内存释放函数为

void vfree(const void *addr);

一般来说,为了性能,通常使用kzalloc/kmalloc分配内存,如果要分配的内存过大则使用vmalloc

示例程序

//mem_alloc.c
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");

static int __init mem_alloc_init(void);
static void __exit mem_alloc_exit(void);

#define KMALLOC_SIZE 131072
#define KZALLOC_SIZE 131072
#define VMALLOC_SIZE 4194304 //4096 * 1024 bytes

char* kmalloc_pt = NULL;
char* kmalloc_pt_large = NULL;
char* kzalloc_pt = NULL;
char* vmalloc_pt = NULL;

int __init mem_alloc_init(void)
{
    printk("####### Enter Module #######\n");
    kmalloc_pt = (char*) kmalloc(KMALLOC_SIZE, GFP_KERNEL);
    if (kmalloc_pt == NULL)
    {
        return -1;
    }
    else 
    {
        printk("kmalloc 128 KB memory successfully!, addr=0x%lx\n", (unsigned long)kmalloc_pt);
    }
    //用kmalloc分配4096kb内存,会失败
    kmalloc_pt_large = (char*) kmalloc(VMALLOC_SIZE, GFP_KERNEL);
    if (kmalloc_pt_large == NULL)
    {
        printk("kmalloc 4096 KB memory failed.\n");
    }
    else 
    {
        printk("kmalloc 4096 KB memory successfully!, addr=0x%lx\n", (unsigned long)kmalloc_pt_large);
    }
    kzalloc_pt = (char*) kzalloc(KZALLOC_SIZE, GFP_KERNEL);
    if (kzalloc_pt == NULL)
    {
        return -1;
    }
    else 
    {
        printk("kzalloc 128 KB memory successfully!, addr=0x%lx\n", (unsigned long)kzalloc_pt);
    }
    //用vmalloc分配4096kb内存,成功
    vmalloc_pt = (char*) vmalloc(VMALLOC_SIZE);
    if (vmalloc_pt == NULL)
    {
        return -1;
    }
    else 
    {
        printk("vmalloc 4096 KB memory successfully!, addr=0x%lx\n", (unsigned long)vmalloc_pt);
    }
    printk("#####################\n");
    return 0;
}

void __exit mem_alloc_exit(void)
{
    printk("####### Exit Module #######\n");
    if (kmalloc_pt)
    {
        kfree(kmalloc_pt);
        printk("kfree kmalloc_pt\n");
        kmalloc_pt = NULL;
    }
    if (kmalloc_pt_large)
    {
        kfree(kmalloc_pt_large);
        printk("kfree kmalloc_pt_large\n");
        kmalloc_pt_large = NULL;
    }
    if (kzalloc_pt)
    {
        kfree(kzalloc_pt);
        printk("kfree kzalloc_pt\n");
        kzalloc_pt = NULL;
    }
    if (vmalloc_pt)
    {
        vfree(vmalloc_pt);
        printk("vfree vmalloc_pt\n");
        vmalloc_pt = NULL;
    }
    printk("exit module.\n");
    printk("#####################\n");
}

module_init(mem_alloc_init);
module_exit(mem_alloc_exit);

编译完后使用insmod命令加载模块,不使用该模块时使用rmmod命令卸载模块
加载和卸载模块
使用dmesg命令查看模块加载和卸载的输出
加载输出
加载卸载输出
从输出中可以看到,在我的电脑上kmallockzalloc可以成功分配128kb内存,但在分配更大的4096kb内存时则失败了,而vmalloc则可以成功分配4096kb的内存。

关于kmalloc/kzalloc具体最大可以分配多大的内存由平台和配置决定,不是一个固定的值,想要弄清楚需要阅读源码。

由上,在内核编程时,申请较小内存时应该优先使用kmalloc/kzalloc,在申请较大内存时使用vmalloc

参考

https://www.coolcou.com/linux-kernel/linux-kernel-memory-management-api/the-linux-kernel-kzalloc.html
https://blog.csdn.net/lu_embedded/article/details/51588902

https://blog.csdn.net/lunhui2016/article/details/114297346

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/143621.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

acwing基础课——质数

由数据范围反推算法复杂度以及算法内容 - AcWing 常用代码模板4——数学知识 - AcWing 基本思想&#xff1a; 首先&#xff0c;我们给出质数的定义&#xff0c;指在大于1的自然数中&#xff0c;除了1和该数自身外&#xff0c;无法被其他自然数整除的数。这里考虑三个问题&…

笔记-鼠标悬浮展示图标

鼠标悬浮展示图标 .primaryLink {color: primary-color-dark;}.primaryLink:hover {cursor: pointer;color: link-hover-color-dark;}.itemAction {display: none; }.itemMenu:hover .itemAction {display: block; }

【数据结构进阶】并查集

并查集 正如它的名字一样&#xff0c;并查集&#xff08;Union-Find&#xff09;就是用来对集合进行 合并&#xff08;Union&#xff09; 与 查询&#xff08;Find&#xff09; 操作的一种数据结构。 合并 就是将两个不相交的集合合并成一个集合。 查询 就是查询两个元素是否属…

链表常见OJ题汇总(持续更新)

目录前言一、移除链表中的元素&#xff08;多指针法&#xff09;二、反转链表&#xff08;多指针法&头插法&#xff09;三、链表的中间结点&#xff08;算数法和双指针法&#xff09;四、链表中的第K个结点&#xff08;算数法&双指针法&#xff09;五、合并两个有序链表…

vue 父子组件设置 scoped, 如何导致滚动条失效的

vue父组件的页面结构 // 调用子组件 <process-time-line :nodeArr"nodeArr"></process-time-line> 父组件的样式 <style lang"scss" scoped> ::-webkit-scrollbar {width: 0px;height: 0px;} </style>子组件的页面结构 <div …

学习C语言笔记:字符串和格式化输入/输出

学习内容&#xff1a;1.函数——strlen()&#xff1b;2.关键字——const&#xff1b;3.字符串&#xff1b;4..如何创建、存储字符串&#xff1b;5.如何使用strlen()函数获取字符串的长度&#xff1b;6.用C预处理器指令#define和ANSIC的const修饰符创建符号常量。与程序交互和使…

《Linux运维实战:Centos7.6基于docker-compose一键离线部署redis6.2.8之哨兵集群》

一、部署背景 由于业务系统的特殊性&#xff0c;我们需要面向不通的客户安装我们的业务系统&#xff0c;而作为基础组件中的redis针对不同的客户环境需要多次部署哨兵集群&#xff0c;作为一个运维工程师&#xff0c;提升工作效率也是工作中的重要一环。所以我觉得有必要针对re…

(Java高级教程)第三章Java网络编程-第一节3:网络编程必备网络知识3之IP地址、端口号

文章目录一&#xff1a;网络传输基本流程&#xff08;1&#xff09;数据包&#xff08;2&#xff09;网络传输的基本流程&#xff08;3&#xff09;具体处理过程A&#xff1a;发送数据B&#xff1a;路由转发C&#xff1a;接受数据二&#xff1a;网络中的地址&#xff08;1&…

Elasticsearch-使用入门

_cat /_cat/nodes&#xff1a;查看所有节点 接口&#xff1a;GET http://192.168.177.134:9200/_cat/nodes /_cat/health&#xff1a;查看ES健康状况 接口&#xff1a;GET http://192.168.177.134:9200/_cat/health /_cat/master&#xff1a;查看主节点信息 接口&#xff1a;G…

【Azure 架构师学习笔记】-Azure Logic Apps(3)-演示1

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Logic Apps】系列。 接上文【Azure 架构师学习笔记】-Azure Logic Apps&#xff08;2&#xff09;-组件介绍 前言 前面两篇文章大概介绍了一些理论知识&#xff0c;但是为用而学才是最重要的&#xff0c;所以接下来做…

word排版时如何保证每张图片大小一样?

问题描述 为了保证文档的美观性&#xff0c;在对图片进行排版时&#xff0c;最好保证图片的大小一致&#xff0c;尤其是多张图片组成一张大图时。 如果一张张图片调整大小&#xff0c;那真的是毫无技术含量的耗时工作。 解决方案 在这提出一种借助表格的解决办法。比如有4张…

Parasoft发布最广泛的MISRA规则覆盖-C/C++test最新版本正式上线!

作为拥有30多年自动化软件测试经验的全球领导者Parasoft宣布发布Parasoft C/Ctest的最新2022.2版本&#xff0c;支持MISRA C:2012修正案3和MISRA C 202x的草案版本。Parasoft针对C和C软件开发的统一、完全集成的测试解决方案的最新版本&#xff0c;帮助团队实现自动化静态分析和…

【java入门系列三】java基础-控制结构

学习记录&#x1f914;分支控制if-elseswitch分支接收字符for循环控制while循环do-while打印金字塔break终止-可以用label&#xff1a;表明continue与break类似return循环中表示直接退出方法(函数)&#xff0c;主方法直接结束字符串比较trick讨论总结谢谢点赞交流&#xff01;(…

外观模式

外观模式 1.外观模式介绍 1.外观模式&#xff08;Facade&#xff09;&#xff0c;也叫“过程模式&#xff1a;外观模式为子系统中的一组接口提供一个一致的界面&#xff0c;此模式定义了一个高层接口&#xff0c;这个接口使得这一子系统更加容易使用 2.外观模式通过定义一个一…

Linux(06)之获取内核代码

Linux(06)之获取内核代码 Author&#xff1a;OnceDay Date&#xff1a;2023年1月5日 漫漫长路&#xff0c;有人对你微笑过嘛… 参考文档&#xff1a; 《Linux内核设计和实现》 1.概述 linux内核的基本架构如下&#xff1a; 所以每个处理器运行的地方只有以下可能&#xf…

带你玩转指针——指针进阶(二)

上次我们说到了函数指针&#xff0c;对于函数指针大家还不太清楚的参考&#xff0c;指针进阶&#xff08;一&#xff09;http://t.csdn.cn/z5cjM函数指针数组数组是存放相同类型的空间&#xff0c;前面我们已经学习了指针数组int* arr[10] 每个元素是int*那么我们把函数的地址存…

grpc实现c++异步非阻塞stream

grpc实现c异步非阻塞stream 参考文章 Non-blocking single-threaded streaming C servergRPC C async api doc and sample codegrpc异步stream server端demo 序言 原来一直是用着同步阻塞的grpc stream。由于不想再创建新的线程来监听grpc stream的新消息了&#xff0c;所以就…

怎么提高程序设计能力?可以参考程序-设计原则,程序-设计模式

怎么提高程序设计能力&#xff1f; 简单说下我的方式方法&#xff1a; 【程序架构】 借鉴设计模式和设计原则 【程序业务】 多理解客户需求&#xff0c;理解后&#xff0c;做竞品逻辑分析&#xff0c;分析出其逻辑结构&#xff0c;和数据结构 &#xff1b; 再根据客户需求…

自己有工厂,怎样接外贸订单?

很多做外贸的小工厂和小型加工厂&#xff0c;除了传统的营销渠道外&#xff0c;也不知道如何做、才能接到外贸订单。小工厂想获得外贸订单&#xff0c;可通过以下7个方法&#xff1a;1、注册一些外贸B2B平台&#xff0c;发布产品&#xff0c;等待客户询盘外贸B2B平台太多了&…

商务车改装之奔驰威霆改装

今天来看看这台车的改装效果&#xff0c;首先外观改成GLS的一个包围。同时大灯换了一个三道杠的运动大灯。运动大灯加上包围&#xff0c;是不是时尚了很多。再来看看威霆内饰&#xff0c;白红相间的色彩搭配&#xff0c;仪表台换成一个大连屏的仪表台&#xff0c;带着飞机一样的…