Linux内核源码剖析之kmem_cache_create

news2025/1/16 1:07:31

写在前面: 

版本信息:

Linux内核2.6.24(大部分centos、ubuntu应该都在3.1+。但是2.6的版本适合学习,后续版本本质变化也不是很大)

一个操作系统对于内存的管理是非常的重要,关乎到整个系统的运行效率和内存最大使用率。内存管理从宏观来看,无非就是使内存达到最大使用率,减少内存的内/外碎片。而在Linux内核中使用伙伴算法(Buddy)管理一组页,使用Slab算法管理单独页,伙伴算法+Slab算法合理的避免内存的内/外碎片。那么今天介绍的kmem_cache_create方法就是Slab算法。

kmem_cache_create作用:

上文提及到内核使用Slab算法来切割页(内核默认页大小为4096字节),所以需要把4096字节切割成多少块,每块多大呢?这是一个内核需要考虑的问题,如果使用不恰当,会大大提升内存内碎片问题和内存使用率降低。接下来,我们看一个列子~

默认的前置条件:

页大小:4096Byte

Slab切割:每块64Byte

Slab切割块数:4096/64 = 2^12 / 2^6 = 2^6 = 64块

切割后每块64字节,一页总共又64块。此时,内核需要使用内存来开辟一个结构体,结构体大小为32字节,如下图:

 

当在一块64字节的内存中开辟32字节的内存,此时会浪费32字节(这称为内存内碎片),此时我们考虑一下,如果这个32字节的结构体一直频繁的开辟,那么就会出现32字节 * N的内存浪费。如果说此32字节的结构体使用频率特别低,甚至只会使用几次,那么一点点的内存浪费是可以忽略的,但是使用频繁的话一定要做定制化或者特殊化处理,减少内存浪费。kmem_cache_create方法就是用来做特殊化处理的。

 

假设说,Slab算法默认一块大小为64字节,而某个频繁使用的结构体为32字节,那我们把Slab算法默认块大小给修改成32字节不就完美了么,就不存在内存浪费问题了。接下来介绍kmem_cache_create方法。

kmem_cache_create源码:

include/linux/slab.h 文件中,定义了kmem_cache_create方法的原型。

// const char * 此slab缓存的名字
// size_t 大小
// size_t 对齐大小
// unsigned long 标志位
// void (*)(struct kmem_cache *, void *) 回调初始化函数
struct kmem_cache *kmem_cache_create(const char *, size_t, size_t,
			unsigned long,
			void (*)(struct kmem_cache *, void *));

mm/slab.c 文件中,实现了kmem_cache_create方法。

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

	………… // 省略Debug信息

	………… // 省略标志位判断信息,如果需要详细的标志位判断请自行看源码。

	cachep = kmem_cache_zalloc(&cache_cache, GFP_KERNEL);

	// 设置参数
	cachep->colour_off = cache_line_size();
	if (cachep->colour_off < align)
		cachep->colour_off = align;
	cachep->colour = left_over / cachep->colour_off;
	cachep->slab_size = slab_size;
	cachep->flags = flags;
	cachep->gfpflags = 0;
	if (CONFIG_ZONE_DMA_FLAG && (flags & SLAB_CACHE_DMA))
		cachep->gfpflags |= GFP_DMA;
	cachep->buffer_size = size;
	cachep->reciprocal_buffer_size = reciprocal_value(size);
	cachep->ctor = ctor;
	cachep->name = name;
	// 添加到链表中。
	list_add(&cachep->next, &cache_chain);

	return cachep;
}
EXPORT_SYMBOL(kmem_cache_create);

此方法篇幅特别长,不过都是大部分都是Debug信息和标志位的判断,为了篇幅的简单,这里做一部分的忽略,当然标志位肯定会影响到参数,如果需要详细的标志位判断请自行看源码。

在kmem_cache_create方法中,会创建kmem_cache结构体,此结构体承载着Slab算法的数据,当然最终kmem_cache_create方法传入的参数会添加到kmem_cache结构体中,并且kmem_cache结构体在内核中使用双向链表链起来,方便后续开辟空间时使用。

kmem_cache_create案例

在本文的最后给出内核中经常使用的结构体对于kmem_cache_create方法使用。

task_struct

task_struct结构体对于内核来说太常见了,进程/线程的承载体,一个正常运行的服务器中进程/线程的数量都是几千上万,频繁的开辟和销毁进程/线程,所以task_struct结构体肯定是非常平凡使用,所以对于Slab而言,肯定要适配与task_struct结构体,所以肯定会使用kmem_cache_create方法。

kernel/fork.c 文件中fork_init方法。

void __init fork_init(unsigned long mempages)
{

	task_struct_cachep =
		kmem_cache_create("task_struct", sizeof(struct task_struct),
			ARCH_MIN_TASKALIGN, SLAB_PANIC, NULL);

	…………	// 省略其他与Slab无关代码
}

再看一下开辟空间时,如何使用。

// task_struct_cachep结构体是初始化调用kmem_cache_create方法返回的kmem_cache结构体。
# define alloc_task_struct()	kmem_cache_alloc(task_struct_cachep, GFP_KERNEL)

static struct task_struct *dup_task_struct(struct task_struct *orig)
{
	struct task_struct *tsk;
	struct thread_info *ti;
	int err;


	tsk = alloc_task_struct();
	if (!tsk)
		return NULL;

	…………	//省略与开辟空间无关代码

	return tsk;
}

在开辟task_struct结构体时,使用task_struct_cachep结构体,task_struct_cachep结构体是初始化过程中调用kmem_cache_create方法返回的kmem_cache结构体 

socket

在Linux内核中,网络通信都离不开最上层的socket暴露层,所以socket结构体肯定会非常频繁的使用。

net/socket.c文件中init_inodecache方法。

static int init_inodecache(void)
{
	// Linux一切皆文件,socket也需要满足VFS的规范。
	sock_inode_cachep = kmem_cache_create("sock_inode_cache",
					      sizeof(struct socket_alloc),
					      0,
					      (SLAB_HWCACHE_ALIGN |
					       SLAB_RECLAIM_ACCOUNT |
					       SLAB_MEM_SPREAD),
					      init_once);
	if (sock_inode_cachep == NULL)
		return -ENOMEM;
	return 0;
}

struct socket_alloc {
	struct socket socket;
	struct inode vfs_inode;
};

这里做一点说明,Linux一切皆文件,socket也需要满足VFS虚拟文件的规范。所以这里传入的结构体是socket_alloc,而不是socket。socket_alloc结构体包含了socket结构体和inode结构体,是为了满足VFS虚拟文件规范~!

static struct inode *sock_alloc_inode(struct super_block *sb)
{
	struct socket_alloc *ei;

	// 使用slab算法开辟空间
	// sock_inode_cachep是初始化调用kmem_cache_create方法返回的kmem_cache结构体。
	ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL);
	if (!ei)
		return NULL;
	init_waitqueue_head(&ei->socket.wait);

	// 初始化工作
	ei->socket.fasync_list = NULL;
	ei->socket.state = SS_UNCONNECTED;
	ei->socket.flags = 0;
	ei->socket.ops = NULL;
	ei->socket.sk = NULL;
	ei->socket.file = NULL;

	return &ei->vfs_inode;
}

当开辟socket结构体时(这里是socket_alloc,因为满足VFS,做了包装,内部还是socket),会使用到 kmem_cache_create方法返回的kmem_cache结构体来做空间开辟。

inode

在Linux中一切皆文件,所以众多模块需要接入VFS虚拟文件的规范,所以inode结构体是非常非常使用频繁,所以一定要做适配。

fs/inode.c中inode_init方法。

void __init inode_init(void)
{

	// slab对于inode结构体的优化。
	inode_cachep = kmem_cache_create("inode_cache",
					 sizeof(struct inode),
					 0,
					 (SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|
					 SLAB_MEM_SPREAD),
					 init_once);

	…………	// 省略其他与slab无关的代码。
	
}

 再看一下开辟空间时,如何使用。

static struct inode *alloc_inode(struct super_block *sb)
{
	static const struct address_space_operations empty_aops;
	static struct inode_operations empty_iops;
	static const struct file_operations empty_fops;
	struct inode *inode;

	// 如果当前实现VFS规范的super_block传入了alloc_inode 的函数就使用传入的(上文socket就实现了)
	if (sb->s_op->alloc_inode)
		inode = sb->s_op->alloc_inode(sb);
	else
		// 使用默认的inode的slab开辟
		inode = (struct inode *) kmem_cache_alloc(inode_cachep, GFP_KERNEL);

	// 省略与开辟inode无关代码。
	return inode;
}

与上面的例子一摸一样。

总结

针对性的创建slab使用 kmem_cache_create方法,该方法返回kmem_cache结构体, 在开辟内存时,使用kmem_cache_alloc,并传入kmem_cache结构体。

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

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

相关文章

C++新经典 | C语言

目录 一、基础之查漏补缺 1.float精度问题 2.字符型数据 3.变量初值问题 4.赋值&初始化 5.头文件之<> VS " " 6.逻辑运算 7.数组 7.1 二维数组初始化 7.2 字符数组 8.字符串处理函数 8.1 strcat 8.2 strcpy 8.3 strcmp 8.4 strlen 9.函数 …

一篇掌握高级交换技术原理与配置(一):vlan聚合

一、概述 VLAN聚合&#xff08;VLAN Aggregation&#xff0c;也称Super-VLAN&#xff09;: 指在一个物理网络内&#xff0c;用多个VLAN&#xff08;称为Sub-VLAN&#xff09;隔离广播域&#xff0c;并将这些Sub-VLAN聚合成一个逻辑的VLAN&#xff08;称为Super-VLAN&#xff0…

寻找RocketMQ首席评测官【阿里云产品测评】

寻找RocketMQ首席评测官【阿里云产品测评】 前言版权推荐寻找RocketMQ首席评测官开始任务一&#xff1a;免费领取资源任务二&#xff1a;了解评测活动体验普通消息场景体验顺序消息场景体验定时消息场景体验事务消息场景体验消息堆积场景体验消息重投场景体验总结未完待续 最后…

Ubuntu升级Cmake、gcc、g++

背景 最近要安装llvm&#xff0c;我选择的是从源码安装&#xff0c;所以要使用Cmake进行构建项目。但是服务器上的Cmake、gcc、g的版本都太低了&#xff0c;不符合要求&#xff0c;所以要对此进行升级。在本博客中采用的升级方法不一定是最好的方法&#xff08;因为我也是参考…

EXCEL数据处理

1. 自定义数字格式 选中数字--右键--设置单元格格式--自定义--shang ↑ 2.条件格式 如果。。。。就。。。。 选中某列--开始--条件格式--突出显示--大于/小于/等于。。。--设置为&#xff08;可选自定义格式&#xff09; 选中区域--条件格式--清除规则--清除所选单元格的规…

外贸人看过来,这里是WhatsApp宝藏使用技巧!

如今从事外贸的宝子普遍都会用到WhatsApp这款全球即时聊天工具。其免费&#xff0c;且可以直接跟陌生人聊天&#xff0c;是一个跟海外客户建立联系的重要阵地。但是有些宝子刚接触WhatsApp&#xff0c;不知道怎么去使用&#xff0c;今天小S就整理出了几个宝藏使用技巧&#xff…

探讨C#、C++和Java这三门语言在嵌入式的地位

我理解对于初入嵌入式领域的担忧。你是想选择一款通用性最广的语言专心学习&#xff0c;但是不知如何选择&#xff0c;视频后方提供了免费的嵌入式学习资源&#xff0c;内容涵盖入门到进阶&#xff0c;需要的到后方免费获取。因为我也曾是一名计算机专业毕业生。通过一段时间的…

2023-08-29 衣品-甄别与筛选

摘要: 外在形象可以说是内在自我的具象化表现, 自我定位与自我认知的表现便是一个人的形象. 所以对于衣品的甄别, 在很大程度上是体验该衣服所表现出来与内在潜意识契合的地方. 明白了这一点, 那么在做甄别和筛选的时候就能明白很多东西. 本文一方面做一定程度的练习, 一方…

生态环境保护3D数字展厅提供了一个线上环保知识学习平台

在21世纪的今天&#xff0c;科技与环保的交汇点提供了无数令人兴奋的可能性。其中&#xff0c;生态环境保护3D数字展厅就是一个绝佳的例子。这个展厅以其独特的3D技术&#xff0c;为我们带来了一个全新的、互动的学习环境&#xff0c;让我们能够更直观地了解和理解我们的环境。…

架构师日记-软件工程里的组织文化 | 京东云技术团队

一 引言 本文是京东到家自动化测试体系建设过程中的一些回顾和总结&#xff0c;删减了部分系统设计与实践的章节&#xff0c;保留了组织与文化相关的内容&#xff0c;整理成文&#xff0c;以飨读者。 下面就以QA&#xff08;Quality Assurance&#xff09;的视角来探讨工作中经…

一文看懂java集合(图文详细)

java集合框架图 看图可知&#xff0c;主要分为两类&#xff1a;Collection 和 Map&#xff0c;Collection主要用于存储一组对象&#xff0c;Map用于存储键-值对。 对这二者再细分 Collection接口&#xff1a; Map接口 集合框架总结 一、Collection 接口的接口 对象的集合&a…

重要提醒!亚马逊卖家们需关注:Review政策发生重大变化

在电商领域&#xff0c;Review&#xff08;评价&#xff09;对卖家而言至关重要&#xff0c;是客户是否下单的核心考量之一。亚马逊的Review政策变化一直备受卖家关注&#xff0c;任何Review方面的动向都会引发卖家们的关切。近期&#xff0c;亚马逊对Review政策进行了调整&…

鸿鹄企业工程项目管理系统 Spring Cloud+Spring Boot+前后端分离构建工程项目管理系统源代码

鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部工程管…

ST SR5E1 22KW OBC 3KW DC DC Combo System 二合一车载充电器解决方案

ST SR5E1 22KW OBC & 3KW DC DC Combo System 二合一车载充电器解决方案 电动车内一般有两个不同电压等级的电池&#xff0c;高压电池用于驱动电机&#xff0c;低压电瓶用于车内电子设备供电&#xff0c;两个电池之间需要一个DCDC变换器来实现功率的流动&#xff0c;根据主…

springcloud-nacos简述

Spring Cloud alibaba: nacos服务注册中心&#xff0c;配置中心 服务注册中心 1.项目父工程添加springcloudalibaba依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><ve…

ZooKeeper与Paxos

Apache ZooKeeper是由Apache Hadoop的子项目发展而来&#xff0c;于2010年11月正式成为了Apache的顶级项目。ZooKeeper为分布式应用提供了高效且可靠的分布式协调服务&#xff0c;提供了诸如统一命名服务、配置管理和分布式锁等分布式的基础服务。在解决分布式数据一致性方面&a…

亿发浙江生产工厂信息化建设管理平台,实现生产智能化、数字化

在全球化、科技深刻变革的时代&#xff0c;浙江省信息化建设正迎来新的发展机遇。以物联网、人工智能大数据、为代表的新技术应用&#xff0c;为人类社会带来了智能、便捷&#xff0c;也标志着新一代信息化浪潮已经到来。特别是在生产型企业中&#xff0c;智能制造是生产型企业…

嵌入式Linux开发实操(十三):GPIO接口开发

从版本4.8开始,Linux内核引入了一个新的基于字符设备的用户空间API,用于管理和控制GPIO(通用输入/输出),在Linux内核4.8之前,在用户空间中管理GPIO的唯一接口是sysfs接口,pio通过/sys/class/gpio中的导出文件进行配置和控制,可以通过该接口执行的基本GPIO操作,比如: …

【论文】2102.DALL-E: Zero-Shot Text-to-Image Generation(文字生成各种各样充满想象图像的开端)

主要参考&#xff1a; openai官网&#xff1a;https://openai.com/blog/dall-e/ 2102.DALLE: Zero-Shot Text-to-Image Generation 2204.DALLE-2 &#xff1a; Hierarchical Text-Conditional Image Generation with CLIP Latents 论文资源网盘下载&#xff1a;https://pan.ba…

独立站在线聊天插件,打造24小时在线客服

独立站的每一个访客都是潜在客户。通过在线聊天&#xff0c;可以提升访客的信任&#xff0c;进一步转化为真实的客户。但是独立站管理者不可能每时每刻都盯着后台&#xff0c;等着客户来提问&#xff0c;然后回复客户。所以独立站拥有在线聊天的功能是有必要的。 而SaleSmartl…