Linux内核与驱动面试经典“小”问题集锦(4)

news2024/9/21 14:48:28

接前一篇文章:Linux内核与驱动面试经典“小”问题集锦(3)

问题5

问:Linux内核中内存分配都有哪些方式?它们之间的使用场景都是什么?

备注:这个问题是笔者近期参加蔚来面试时遇到的一个问题。这道题说是一道小题,其实应该是一道大题,它考察的是候选者对于Linux内存管理子系统中内存分配这一块的功力深浅。

答:

在Linux内核空间中,申请内存所涉及的函数主要包括kmalloc()、__get_free_pages()和vmalloc()等。其中,kmalloc()和__get_free_pages()(及其类似函数)申请的内存位于DMA和常规区域的映射区,而且在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,因此存在较简单的转换关系;而vmalloc()在虚拟内存空间给出一块连续的内存区。实质上,这片连续的虚拟内存在物理内存中并不一定连续,而vmalloc()申请的虚拟内存和物理内存之间也没有简单的换算关系。

1. kmalloc()

kmalloc函数在include/linux/slab.h中,代码如下:

static __always_inline __alloc_size(1) void *kmalloc(size_t size, gfp_t flags)
{
	if (__builtin_constant_p(size) && size) {
		unsigned int index;

		if (size > KMALLOC_MAX_CACHE_SIZE)
			return kmalloc_large(size, flags);

		index = kmalloc_index(size);
		return kmalloc_trace(
				kmalloc_caches[kmalloc_type(flags, _RET_IP_)][index],
				flags, size);
	}
	return __kmalloc(size, flags);
}

kmalloc函数的第一个参数是要分配的块的大小;第二个参数为分配标志,用于控制kmalloc()的行为。

最常用的分配标志是GFP_KERNEL,其含义是在内核空间的进程中申请内存。kmalloc()的底层依赖于__get_free_pages()来实现,分配标志的前缀GFP正好是这个底层函数的缩写。使用GFP_KERNEL标志申请内存时,若暂时不能满足,则进程会休眠等待页,即会引起阻塞,因此不能在中断上下文或持有自旋锁的时候使用GFP_KERNEL申请内存

备注:这也是经常会被问到的一道经典面试题,即GFP_KERNEL能否用在中断中?或者中断中应该使用哪些标志?

由于在中断处理函数、tasklet和内核定时器等非进程上下文中不能阻塞,所以此时驱动应当使用GFP_ATOMIC标志来申请内存。当使用GFP_ATOMIC标志申请内存时,若不存在空闲页,则不等待,直接返回。

其它的申请标志还包括:

  • GFP_USER:用来为用户空间页分配内存,可能阻塞。
  • GFP_HIGHUSER:类似于GFP_USER,但它从高端内存分配。
  • GFP_DMA:从DMA区域分配内存,
  • GFP_NOIO:不允许任何I/O初始化。
  • GFP_NOFS:不允许任何文件系统调用。
  • __GFP_HIGHMEM:指示分配的内存可以位于高端内存。
  • __GFP_COLD:请求一个较长时间不访问的页。
  • __GFP_NOWARN:当一个分配无法满足时,阻止内核发出警告。
  • __GFP_HIGH:高优先级请求,允许获得被内核保留给紧急情况使用的最后的内存页。
  • __GFP_REPEAT:分配失败,则尽力重复尝试。
  • __GFP_NOFAIL:只许申请成功,不许失败。不推荐使用此标志。
  • __GFP_NORETRY:若申请不到,则立即放弃。

使用kmalloc()申请的内存应该使用kfree()释放,这个函数的用法和用户空间的free()类似。

2. __get_free_pages()

__get_free_pages()系列函数/宏本质上是Linux内核最底层用于获取空闲内存的方法,因为底层的buddy(伙伴)算法以2^n页为单位管理空闲内存,因此最底层的内存申请总是以2^n页为单位的。

__get_free_pages()系列函数/宏包括get_zeroed_page()、__get_free_page()和__get_free_pages()。

  • get_zeroed_page()

该函数返回一个指向新页的指针,并且将该页清零。其在mm/page_alloc.c中,代码如下:

unsigned long get_zeroed_page(gfp_t gfp_mask)
{
	return __get_free_page(gfp_mask | __GFP_ZERO);
}
EXPORT_SYMBOL(get_zeroed_page);
  • __get_free_page();

该宏返回一个指向新页的指针,但该页不清零。其定义在include/linux/gfp.h中,如下:是:

#define __get_free_page(gfp_mask) \
		__get_free_pages((gfp_mask), 0)

它实际上就是调用了下边的__get_free_pages()申请一页。

  • __get_free_pages()

__get_free_pages()也是在mm/page_alloc.c中,代码如下:

/*
 * Common helper functions. Never use with __GFP_HIGHMEM because the returned
 * address cannot represent highmem pages. Use alloc_pages and then kmap if
 * you need to access high mem.
 */
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
{
	struct page *page;

	page = alloc_pages(gfp_mask & ~__GFP_HIGHMEM, order);
	if (!page)
		return 0;
	return (unsigned long) page_address(page);
}
EXPORT_SYMBOL(__get_free_pages);

该函数可分配多个页,并返回所分配内存的首地址。分配的页数为2^order,分配的页不清零。oeder允许的最大值是10(1024页)或者11(2048页),这取决于具体的硬件平台。

__get_free_pages()和get_zeroed_page()在实现中调用了alloc_pages函数,alloc_pages()既可以在内核空间分配,也可以在用户空间分配。该函数也在mm/page_alloc.c中,其原型如下:

struct page *__alloc_pages(gfp_t gfp, unsigned int order, int preferred_nid,
							nodemask_t *nodemask);

其参数含义与__get_free_pages()相似,但它返回分配的第一个页的描述符而非首地址。

3. vmalloc

vmalloc()一般只为存在于软件中(没有对应的硬件意义)的较大的顺序缓冲区分配内存。vmalloc()远大于__get_free_pages()的开销。为了完成vmalloc(),新的页表项需要被建立。因此,只是调用vmalloc()来分配少量的内存(如1页以内的内存)是不妥的。

vmalloc函数在mm/vmalloc.c中,代码如下:

/**
 * vmalloc - allocate virtually contiguous memory
 * @size:    allocation size
 *
 * Allocate enough pages to cover @size from the page level
 * allocator and map them into contiguous kernel virtual space.
 *
 * For tight control over page level allocator and protection flags
 * use __vmalloc() instead.
 *
 * Return: pointer to the allocated memory or %NULL on error
 */
void *vmalloc(unsigned long size)
{
	return __vmalloc_node(size, 1, GFP_KERNEL, NUMA_NO_NODE,
				__builtin_return_address(0));
}
EXPORT_SYMBOL(vmalloc);

vmalloc函数在申请内存时,会进行内存的映射,改变页表项,而不像kmalloc()实际用的是开机过程中就映射好了的DMA和常规区域的页表项。因此,vmalloc()的虚拟地址和物理地址不是一个简单的线性映射。

vmalloc函数不能用在原子上下文中,因为其内部实现使用了标志位GFP_KERNEL的kmalloc()。

这里多说一点。关于kmalloc与vmalloc的区别,参见笔者的这篇文章:中移(苏州)软件技术有限公司面试问题与解答(7)—— kmalloc与vmalloc的区别与联系及使用场景。

以上是从具体的内存分配函数的角度来说的。从更大的层面来讲,Linux内核物理内存分配的一般方式包括

(1)伙伴系统(Buddy System)

伙伴系统将物理内存划分为不同大小的块,每个块大小都是2的幂次。这些块被组织成“伙伴”对,每对伙伴的大小是一样的。

(2)slab分配器

slab分配器用于管理小块内存分配,如内核数据结构的分配。slab分配器将内存划分为不同的对象缓存,以提高内存分配和释放的效率。

(3)CMA(Contiguous Memory Allocator,连续内存分配器)

对于需要连续大块内存的需求,Linux引入了CMA。它可以用于分配连续的物理内存区域,如视频缓冲等。

(4)页分配器

Linux内核将物理内存划分为固定大小的页,通常是4KB。当进程需要内存时,内核会使用页分配器来分配这些页面。

(5)内存回收

Linux内核还会定期执行内存回收,以回收未使用的内存。这包括清除不再使用的页面,并将其返回到内存池中。

可见,本题虽然看似是一道面试小题,但实际上其背后蕴含的知识点是非常丰富的,也是非常考验功力的。

参考资料:

《Linux设备驱动开发详解 —— 基于最新的Linux 4.0内核》 宋宝华 编著,机械工业出版社

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

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

相关文章

【Web - 框架 - Vue】随笔 - 通过`CDN`的方式使用`VUE 2.0`和`Element UI`

通过CDN的方式使用VUE 2.0和Element UI VUE 网址 https://cdn.bootcdn.net/ajax/libs/vue/2.7.16/vue.js源码 https://download.csdn.net/download/HIGK_365/88815507测试 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset&quo…

Spring Boot整合新版Spring Security:Lambda表达式配置优雅安全

文章目录 1. 引言2. 项目依赖配置3. 使用Lambda表达式配置Spring Security4. 自定义身份验证逻辑5. 认证与授权注解5.1 Secured注解5.2 PreAuthorize和PostAuthorize注解 6. 总结 &#x1f389;Spring Boot整合新版Spring Security&#xff1a;Lambda表达式配置优雅安全 ☆* o(…

一文掌握SpringBoot注解之@Configuration知识文集(6)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

2024.2.6 模拟实现 RabbitMQ —— 数据库操作

目录 引言 选择数据库 环境配置 设计数据库表 实现流程 封装数据库操作 针对 DataBaseManager 单元测试 引言 硬盘保存分为两个部分 数据库&#xff1a;交换机&#xff08;Exchange&#xff09;、队列&#xff08;Queue&#xff09;、绑定&#xff08;Binding&#xff0…

使用Volo.Abp读取Sqlite表中数据

书接上文&#xff1a;Abp 从空白的WebApplication中添加EntityFrameworkCore生成数据库 开发环境&#xff1a;.NET6、Volo.Abp 数据库&#xff1a;Sqlite 说明&#xff1a;纯属个人强行入门。我个人觉得按照官网的操作不舒服&#xff0c;所以自己研究着来&#xff0c;请读者…

[NOIP2017 提高组] 宝藏

[NOIP2017 提高组] 宝藏 题目背景 NOIP2017 D2T2 题目描述 参与考古挖掘的小明得到了一份藏宝图&#xff0c;藏宝图上标出了 n n n 个深埋在地下的宝藏屋&#xff0c; 也给出了这 n n n 个宝藏屋之间可供开发的 m m m 条道路和它们的长度。 小明决心亲自前往挖掘所有宝…

Linux 文件比较工具

在Linux系统中&#xff0c;文件比较是一种常见的任务&#xff0c;用于比较两个文件之间的差异。文件比较可以帮助我们找出两个文件的不同之处&#xff0c;或者确定它们是否完全相同。在Linux中&#xff0c;有多种方法可以进行文件比较。 1. diff 在Linux中&#xff0c;diff命…

React+Antd实现省、市区级联下拉多选组件(支持只选省不选市)

1、效果 是你要的效果&#xff0c;咱们继续往下看&#xff0c;搜索面板实现省市区下拉&#xff0c;原本有antd的Cascader组件&#xff0c;但是级联组件必须选到子节点&#xff0c;不能只选省&#xff0c;满足不了页面的需求 2、环境准备 1、react18 2、antd 4 3、功能实现 …

ThreadLocal及阿里(TransmittableThreadLocal,TTL)分析

TTL类关系图 ThreadLocal <- InheritableThreadLocal <- TransmittableThreadLocal 1. ThreadLocal ThreadLocal 类提供线程本地&#xff08;局部&#xff09;变量。每个线程都有自己独立初始化的变量副本。 TheadLocal 允许我们存储仅由特定线程访问的数据&#xff0c;…

JavaWeb后端开发(第一期):Maven基础、Maven的安装配置、如何创建maven项目模块、maven的生命周期

Java后端开发&#xff1a;2024年2月6日 -> LiuJinTao 文章目录 JavaWeb后端开发&#xff08;第一期&#xff09; &#xff1a; maven基础一、 maven介绍1.1 什么maven呢&#xff1a;1.2 maven的作用1.3 maven 模型1.4 maven 仓库 二、maven 安装2.1 配置本地仓库2.2 配置阿里…

c#cad 创建-多线段(三)

运行环境 vs2022 c# cad2016 调试成功 一、程序说明 AutoCAD中创建多段线的。具体解释如下&#xff1a; 获取当前文档和数据库&#xff0c;并创建一个编辑器&#xff08;用于与用户交互&#xff09;。使用事务处理的方式&#xff0c;开始对数据库的操作。打开模型空间&…

LeetCode-第171题-Excel表的序列号

1.题目描述 给你一个字符串 columnTitle &#xff0c;表示 Excel 表格中的列名称。返回 该列名称对应的列序号 。 例如&#xff1a; A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 ... 2.样例描述 3.思路描述 遍历时将每个字母与 A 做减法&…

【开源】SpringBoot框架开发厦门旅游电子商务预订系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 景点类型模块2.2 景点档案模块2.3 酒店管理模块2.4 美食管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 学生表3.2.2 学生表3.2.3 学生表3.2.4 学生表 四、系统展示五、核心代码5.1 新增景点类型5.2 查询推荐的…

Linux——进程间通信:管道

我们在开发过程中&#xff0c;可能会碰到两个或多个进程需要协同进行&#xff0c;这两个进 程之间有着一定的关系&#xff0c;这个进程可能会需要另一个进程的某些消息来达 到自己的目的&#xff0c;或者是一个进程控制着另一个进程&#xff0c;又或者是需要某种资 源的共享。但…

速度规划:s形曲线应用(变速 停车)opencv c++显示(3)

理论篇 先看该篇&#xff0c;这里沿用了里面的变量。 应用推导篇 分为变速和停车两部分&#xff08;字迹潦草&#xff0c;可结合代码看&#xff09; 代码篇 变速函数入口&#xff1a; velocityPlanner vp; vp.SetParameters(0, 1);停车函数入口&#xff1a; ParkingVelo…

挑战杯 python+大数据校园卡数据分析

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于yolov5的深度学习车牌识别系统实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;3分 该项目较为新颖&am…

23.HarmonyOS App(JAVA)堆叠布局StackLayout使用方法

不常用 StackLayout直接在屏幕上开辟出一块空白的区域&#xff0c;添加到这个布局中的视图都是以层叠的方式显示&#xff0c;而它会把这些视图默认放到这块区域的左上角&#xff0c;第一个添加到布局中的视图显示在最底层&#xff0c;最后一个被放在最顶层。上一层的视图会覆盖…

mysql入门到精通005-基础篇-约束

1、概述 1.1 概念 约束是作用于表中字段上的规则&#xff0c;用于限制储存在表中的数据。 1.2 目的 保证数据库中数据的正确性、有效性和完整性。 1.3 常见的约束分类 一旦谈到外键&#xff0c;则至少涉及2张表约束是作用于表中字段上的&#xff0c;可以在创建表/修改表的…

机器学习-梯度下降法

不是一个机器学习算法是一种基于搜索的最优化方法作用&#xff1a;最小化一个损失函数梯度上升法&#xff1a;最大化一个效用函数 并不是所有函数都有唯一的极值点 解决方法&#xff1a; 多次运行&#xff0c;随机化初始点梯度下降法的初始点也是一个超参数 代码演示 impor…

【语音合成】中文-多情感领域-16k-多发音人

模型介绍 语音合成-中文-多情感领域-16k-多发音人 框架描述 拼接法和参数法是两种Text-To-Speech(TTS)技术路线。近年来参数TTS系统获得了广泛的应用&#xff0c;故此处仅涉及参数法。 参数TTS系统可分为两大模块&#xff1a;前端和后端。 前端包含文本正则、分词、多音字预…