Linux vmalloc/vfree函数实现解读

2024-04-20 20:38

本文主要是介绍Linux vmalloc/vfree函数实现解读,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

高端物理地址的分配采用vmalloc/vfree这组函数进行,什么是高端物理内存呢?我们知道Linux给内核预留了一部分虚拟地址空间,这部分虚拟地址如果能全部直接映射到物理地址空间就不存在高端内存。如果这部分内存有一部分不能直接映射到地址空间,那么这部分虚拟地址空间称为高端内存。因此,高端内存是虚拟地址空间中的概念。举个例子:如果你的物理内存为512M,那么就不存在高端内存的分配,如果你的物理地址为2G,那么有1G+128M(预留给VMALLOC区)是属于高端内存的。高端内存的分配即便是逻辑上连续,也不要求物理上是连续的。

       下面我们介绍一下高端内存的分配。分配高端内存是通过调用vmalloc函数。我们来解读一下该函数的分配过程。

首先介绍一下这部分内存管理所需要的数据结构:

struct vm_struct {

       void               *addr; // 虚拟地址的开始

       unsigned long         size; // 分配大小

       unsigned long         flags; // 标志位

       struct page            **pages; // 对应的页面

       unsigned int           nr_pages; // 页面数量

       unsigned long         phys_addr; // 物理地址

       struct vm_struct    *next; // 单链表,指向下一个vm节点

};

 

1.         首先检查请求分配的内存大小有没有超过最大的物理页面数。如果超过返回0,表示分配失败。

       size = PAGE_ALIGN(size);

       if (!size || (size >> PAGE_SHIFT) > num_physpages)

              return NULL;

2.         使用kmallocslab中,分配vm_struct数据结构。

       area = kmalloc_node(sizeof(*area), GFP_KERNEL, node);

3.         在单链表vmlist中查找适合的位置,并将新的vm节点插入到单链表中。

       for (p = &vmlist; (tmp = *p) != NULL ;p = &tmp->next) {

              if ((unsigned long)tmp->addr < addr) {

                     if((unsigned long)tmp->addr + tmp->size >= addr)

                            addr = ALIGN(tmp->size +

                                        (unsigned long)tmp->addr, align);

                     continue;

              }

              if ((size + addr) < addr)

                     goto out; // 地址越界

              if (size + addr <= (unsigned long)tmp->addr)

                     goto found; // 查找成功。进行插入操作

              addr = ALIGN(tmp->size + (unsigned long)tmp->addr, align);

              if (addr > end - size)

                     goto out; // 地址越界

       }

       // 插入新的vm节点到vmlist中去。

       area->next = *p;

       *p = area;

       // 初始化新结点

       area->flags = flags;

       area->addr = (void *)addr;

       area->size = size;

       // 物理内存还未分配。

       area->pages = NULL;

       area->nr_pages = 0;

       area->phys_addr = 0;

 

4.         接下来初始化vm_struct结构中的pagesnr_pages字段。

a)         初始化nr_pages字段

              nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT;

b)        初始化pages数组

计算数组大小

       array_size = (nr_pages * sizeof(struct page *));

如果数组大小大于1个页面,在非连续区进行分配,否则在连续区进行分配

       if (array_size > PAGE_SIZE)

              pages = __vmalloc_node(array_size, gfp_mask, PAGE_KERNEL, node);

       else

              pages = kmalloc_node(array_size, (gfp_mask & ~__GFP_HIGHMEM), node);

area->pages = pages;

 

5.         从伙伴系统中进行物理内存页面的分配

       for (i = 0; i < area->nr_pages; i++) {

              if (node < 0)

                     area->pages[i] = alloc_page(gfp_mask); // 针对UMA

              else

                     area->pages[i] = alloc_pages_node(node, gfp_mask, 0); // 针对NUMA

              if (unlikely(!area->pages[i])) {

                     /* Successfully allocated i pages, free them in __vunmap() */

                     area->nr_pages = i;

                     goto fail;

              }

       }

 

6.    将刚申请的页面映射到页表中。

        if (map_vm_area(area, prot, &pages))           

 

释放内存部分很简单,就是从vmlist当中删除掉对应的节点。然后将内存归还给伙伴系统。

这篇关于Linux vmalloc/vfree函数实现解读的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/921269

相关文章

Linux线程同步/互斥过程详解

《Linux线程同步/互斥过程详解》文章讲解多线程并发访问导致竞态条件,需通过互斥锁、原子操作和条件变量实现线程安全与同步,分析死锁条件及避免方法,并介绍RAII封装技术提升资源管理效率... 目录01. 资源共享问题1.1 多线程并发访问1.2 临界区与临界资源1.3 锁的引入02. 多线程案例2.1 为

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

C语言中%zu的用法解读

《C语言中%zu的用法解读》size_t是无符号整数类型,用于表示对象大小或内存操作结果,%zu是C99标准中专为size_t设计的printf占位符,避免因类型不匹配导致错误,使用%u或%d可能引发... 目录size_t 类型与 %zu 占位符%zu 的用途替代占位符的风险兼容性说明其他相关占位符验证示

MySQL常用字符串函数示例和场景介绍

《MySQL常用字符串函数示例和场景介绍》MySQL提供了丰富的字符串函数帮助我们高效地对字符串进行处理、转换和分析,本文我将全面且深入地介绍MySQL常用的字符串函数,并结合具体示例和场景,帮你熟练... 目录一、字符串函数概述1.1 字符串函数的作用1.2 字符串函数分类二、字符串长度与统计函数2.1

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

Oracle数据库定时备份脚本方式(Linux)

《Oracle数据库定时备份脚本方式(Linux)》文章介绍Oracle数据库自动备份方案,包含主机备份传输与备机解压导入流程,强调需提前全量删除原库数据避免报错,并需配置无密传输、定时任务及验证脚本... 目录说明主机脚本备机上自动导库脚本整个自动备份oracle数据库的过程(建议全程用root用户)总结

Linux如何查看文件权限的命令

《Linux如何查看文件权限的命令》Linux中使用ls-R命令递归查看指定目录及子目录下所有文件和文件夹的权限信息,以列表形式展示权限位、所有者、组等详细内容... 目录linux China编程查看文件权限命令输出结果示例这里是查看tomcat文件夹总结Linux 查看文件权限命令ls -l 文件或文件夹