Linux内核学习(八)—— 内存管理(基于Linux 2.6内核)

目录

一、页(page)

二、区(zone)

三、页操作

四、kmalloc()

五、vmalloc() 

六、slab 分配器

七、在栈上的静态分配


一、页(page)

内核把物理页作为内存管理的基本单位。尽管处理器的最小可寻        址单位通常为字或字节,但是,内存管理单元(MMU,管理内存并把虚拟地址转换为物理地址的硬件)通常以页为单位进行处理。MMU 以页为单位来管理系统中的页表。从虚拟内存的角度来看,页就是最小单位。

大多数 32 位体系结构支持 4KB 的页,而 64 位体系结构一般会支持 8KB 的页。

内核用 struct page 结构表示系统中的每个物理页,位于 <linux/mm_types.h> 中,定义如下:

  • flags 用来存放页的状态,比如页是否是脏的(内存数据页和磁盘数据页内容不一致),是不是被锁定在内存中等。flags 每一位都可以表示一种状态,状态标志定义在 <linux/page-flags.h> 中。
  • _count 存放页的引用计数,也就是这一页被引用了多少次。当计数值为 -1 时,就说明当前内核并没有引用这一页,在新的分配中就可以使用它。内核代码不应该直接查看这个参数,而是调用 page_count() 函数进行检查,传入 page 结构,返回 0 表示空闲。
  • virtual 存放页的虚拟地址。 通常情况下,它就是页在虚拟内存中的地址。但有些内存(高端内存)并不永久地映射到内核地址空间上。这种情况下 virtual 的值为 NULL。

page 结构与物理页相关,而并非与虚拟页相关。内核用这一结构来管理系统中所有的页,因为内核需要知道一个页是否空闲。一个页的拥有者可能是用户空间进程、动态分配的内核数据、静态内核代码或页高速缓存。

二、区(zone)

由于硬件的限制,有些页位于内存中的特定的物理地址上,所以不能将其用于一些特定的任务,于是内核把页划分为不同的区(zone) ,由此对具有相似特性的页进行分组。Linux 必须处理如下两种由于硬件存在缺陷而引起的内存寻址问题:

  • 一些硬件只能用某些特定的内存地址来执行 DMA(直接内存访问)。
  • 一些体系结构的内存的物理地址寻址范围比虚拟寻址范围大得多,这样就有一些内存不能永久地映射到内核空间上。

因此,Linux 划分了四种区:

  • ZONE_DMA:这个区包含的页能用来执行 DMA 操作。
  • ZONE_DMA32:这个区包含的页能用来执行 DMA 操作,但这些页面只能被 32 位设备访问。
  • ZONE_NORMAL:这个区包含的都是能正常映射的页。
  • ZONE_HIGHEM:这个区包含高端内存(high memory),其中的页并不能永久地映射到内核地址空间。系统内其余内存就是所谓的低端内存(low memory)。

Linux 把系统的页划分为分区,形成不同的内存池,这样就可以根据用途进行分配了。区是内存为了管理页而采取的一种逻辑上的分组。

三、页操作

内核提供了几种请求页的接口,所有接口都以页为单位分配内存,定义于 <linux/gfp.h> 中,最核心的是:

struct page *alloc_pages(gfp_t gfp_mask, unsigned int order)

该函数分配 2^{order} 个连续的物理页,并返回一个指针,指向第一个页的 page 结构体。出错则返回 NULL。

可以用下面这个函数把给定的页的 page 转换成它的逻辑地址:

void *page_address(struct page *page)

该函数返回一个指针,指向给定物理页当前所在的逻辑地址。

当你不在需要页时可以用下面的函数释放它们:

void __free_pages(struct page *page, unsigned int order)
void free_pages(unsigned long addr, unsigned int order)
void free_page(unsigned long addr)

四、kmalloc()

kmalloc() 函数与用户空间的 malloc() 类似,不过它多了一个 flags 参数,这个函数可以获得以字节为单位的一块内核内存。其在 <linux/slab.h> 中声明:

void *kmalloc(size_t size, gfp_t flags)

这个函数返回一个指向 size 大小的内存块指针,并且所分配的内存区在物理地址上是连续的(虚拟地址自然也是连续的)。出错时则返回 NULL。

使用 kfree() 函数可以释放由 kmalloc() 分配出来的内存块:

void kfree(const void *ptr)

如果想要释放的内存不是由 kmalloc() 分配的,或者想要释放的内存早就被释放了,调用这个函数就会有很严重的后果。

五、vmalloc() 

vmalloc() 函数类似于 kmalloc(),但是 vmalloc() 函数分配的内存虚拟地址是连续的,而物理地址则无需连续。malloc() 函数也一样,其在虚拟地址空间内是连续的,但是并不保证它们在物理 RAM 中也是连续的。

void *vmalloc(unsigned long size)
void vfree(const void *addr)

大多数情况下,只有硬件设备用到的内存区必须是物理上连续的块。而供软件使用的内存块就可以使用只有虚拟地址连续的内存块。

六、slab 分配器

分配和释放数据结构是所有内核中最普遍的操作之一,空闲链表包含可供使用的、已分配好的数据结构块,当代码需要一个新的数据结构实例时,就可以从空闲链表中抓取一个,而不需要重新分配内存。空闲链表相当于对象高速缓存,可以快速存储频繁使用的对象类型。

但空闲链表无法全局控制,当可用内存紧缺时,内核无法通知每个空闲链表,让其释放一些内存。所以为了弥补这一缺陷,Linux 内核提供了 slab 层(slab 分配器)slab 分配器扮演了通用数据结构缓存层的角色

  • 下面是 slab 分配器的基本原则:
  • 频繁使用的数据结构应该缓存它们。
  • 频繁分配和回收会导致内存碎片化,为了避免这种情况,空闲链表的缓存会连续地存放,释放的数据结构又会放回空闲链表。
  • 回收的对象可以立即投入下一次分配。
  • 对存放对象进行着色(color),以防止多个对象映射到相同的高速缓存行(cache line)。

slab 分配器把不同的对象划分为不同的高速缓存组,每个高速缓存组都存放不同类型的对象。比如,一个高速缓存用于存放进程描述符(task_struct 结构的一个空闲链表),一个用于存放索引节点对象(struct inode)。

然后,这些高速缓存又被划分为多个 slab,slab由一个或多个物理上连续的页组成。每个 slab 都包含一些被缓存的数据结构,并且每个 slab 都处于三种状态之一:满、部分满或空(注意这里满的意思是所有的数据结构都被使用中,无法分配)。当内核的某一部分需要一个新的对象时,先从部分满的 slab 中进行分配,其次再从空的 slab 中进行分配,如果没有空的 slab,那么就需要创建一个新 slab了。

下面是三者之间的关系:

slab 负责内存紧缺情况下所有底层的对齐、着色、分配、释放和回收,如果要频繁的创建很多相同类型的对象,那么就应该考虑使用 slab 高速缓存。

七、在栈上的静态分配

用户空间能负担起非常大的栈,而且栈空间还能动态增长,但内核却不能这么奢侈,内核栈小而且固定。当给每个进程分配一个固定大小的栈后,不但可以减小内存的消耗,而且内核也无需负担太重的栈管理任务。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全新土地销售活动 Turkishverse——在数字十字路口占据一席之地

准备好与来自该地区的众多世界知名合作伙伴一起探索土耳其文化和历史吧&#xff01; 简单介绍 ● 在这个弘扬土耳其文化和历史的新社区中&#xff0c;共有 433 块 LAND 可供出售&#xff0c;其中包括 □ 380 块标准 LAND □ 48 块优质 LAND □ 5 个 Estate ● LAND 销售抽…

营销数字化|企业级 AIGC 工具的「iPhone 时刻」

2007 年&#xff0c;乔布斯发布了第一款 iPhone&#xff0c;从此彻底改变了手机行业的市场走向。iPhone 成功的背后&#xff0c;一个很重要的原因是&#xff1a;它让用户以更简单、更符合直觉的方式来使用手机。 如今&#xff0c;AIGC 工具也在等待它的「iPhone 时刻」&#xf…

lwIP更新记10:IP 冲突检测

lwip-2.2.0-rc1 版本于 2023 年 6 月 29 日发布&#xff0c;带来了我期盼已久的 IPv4 冲突检测 功能。 lwip-2.2.0-rc1 版本重新回归了 master 分支&#xff08;主分支&#xff09;&#xff0c;不再使用单独的稳定分支。 master 分支 是一个 Git&#xff08;版本控制程序&…

C++信息学奥赛1145:字符串p型编码

#include <iostream> #include <string> using namespace std; int main() {string str;// 输入一个字符串cin>>str;int nstr.length();for(int i0;i<n;i){int a1;while(str[i]str[i1]){i;a;}cout<<a<<str[i]; // 输出连续相同字符的个数和该…

在百度地图中添加自定义全屏控件

百度地图中添加全屏控件 前置知识&#xff1a; 进入整个页面的全屏模式 &#xff1a;document.documentElement.requestFullscreen() 进入特定元素的全屏模式 &#xff1a; document.getElementById("ID").requestFullscreen() 退出全屏&#xff1a;document.exitFu…

Kyligence Copilot 登陆海外,斩获 Product Hunt 日榜 TOP 2

8月14日&#xff0c;AI 数智助理 Kyligence Copilot 在全球知名科技产品平台 Product Hunt 上线&#xff0c;其以出色的产品创新实力&#xff0c;在激烈的竞争中脱颖而出&#xff0c;仅仅在 24 小时内收获了超过 400 个投票和近 200 条支持评论&#xff0c;荣登当日产品榜排名第…

MyBatis分页查询与特殊字符处理

目录 目录 一、引言 1.1 简介Mybatis 1.2分页查询的重要性 1.3MyBatis特殊字符处理的挑战 挑战1&#xff1a;SQL注入漏洞 挑战2&#xff1a;查询结果异常 挑战3&#xff1a;数据完整性问题 挑战4&#xff1a;跨平台兼容性 挑战5&#xff1a;用户体验 如何应对挑战 二…

视频云存储/安防监控AI视频智能分析平台——智慧煤矿解决方案

一、方案背景 煤矿业是一个高风险行业&#xff0c;存在着许多潜在的安全隐患和风险。互联网、物联网、人工智能等新兴技术高速发展&#xff0c;为传统行业带来颠覆性变革&#xff0c;将高新技术与传统技术装备、管理相融合&#xff0c;实现产业转型升级已经成为煤矿行业发展趋…

Linux学习记录——이십유 多线程(3)

文章目录 1、生产者消费者模型1、基于BlockingQueue的生产者消费者模型2、对模型全面的认识3、多生产者多消费者 2、信号量1、POSIX信号量2、基于环形队列的生产消费模型3、多生产者多消费者 3、多生产者多消费者模型的意义 1、生产者消费者模型 顾名思义&#xff0c;就像是供…

Ubuntu20.04安装软件报错:The following packages have unmet dependencies

Ubuntu20.04更换阿里云源后安装软件都会报错&#xff1a;The following packages have unmet dependencies 查看资料&#xff0c;大概是ubuntu本身的源比较版本较老&#xff0c;而阿里云的源比较新&#xff0c;因此版本不匹配造成依赖的库不匹配&#xff0c;所以只要将阿里云的…

使用 Nacos 作为 Spring Boot 配置中心

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

JMeter性能测试(上)

一、基础简介 界面 打开方式 双击 jmeter.bat双击 ApacheJMeter.jsr命令行输入 java -jar ApacheJMeter.jar 目录 BIN 目录&#xff1a;存放可执行文件和配置文件 docs目录&#xff1a;api文档&#xff0c;用于开发扩展组件 printable-docs目录&#xff1a;用户帮助手册 li…