Linux内核有什么之内存管理子系统有什么第六回 —— 小内存分配(4)

接前一篇文章:Linux内核有什么之内存管理子系统有什么第五回 —— 小内存分配(3)

本文内容参考:

linux进程虚拟地址空间

《趣谈Linux操作系统 核心原理篇:第四部分 内存管理—— 刘超》

特此致谢!

二、小内存分配 —— brk与sbrk

上一回在讲sys_brk函数代码的时候,讲到了struct vm_area_struct,本回对于此结构体进行详细解析。

1. brk源码解析

为了便于理解,再次贴出vm_area_struct结构相关代码。struct vm_area_struct的定义也是在include/linux/mm_types.h中,代码如下:

/*
 * This struct describes a virtual memory area. There is one of these
 * per VM-area/task. A VM area is any part of the process virtual memory
 * space that has a special rule for the page-fault handlers (ie a shared
 * library, the executable area etc).
 */
struct vm_area_struct {
	/* The first cache line has the info for VMA tree walking. */

	unsigned long vm_start;		/* Our start address within vm_mm. */
	unsigned long vm_end;		/* The first byte after our end address
					   within vm_mm. */

	struct mm_struct *vm_mm;	/* The address space we belong to. */

	/*
	 * Access permissions of this VMA.
	 * See vmf_insert_mixed_prot() for discussion.
	 */
	pgprot_t vm_page_prot;
	unsigned long vm_flags;		/* Flags, see mm.h. */

	/*
	 * For areas with an address space and backing store,
	 * linkage into the address_space->i_mmap interval tree.
	 *
	 * For private anonymous mappings, a pointer to a null terminated string
	 * containing the name given to the vma, or NULL if unnamed.
	 */

	union {
		struct {
			struct rb_node rb;
			unsigned long rb_subtree_last;
		} shared;
		/*
		 * Serialized by mmap_sem. Never use directly because it is
		 * valid only when vm_file is NULL. Use anon_vma_name instead.
		 */
		struct anon_vma_name *anon_name;
	};

	/*
	 * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
	 * list, after a COW of one of the file pages.	A MAP_SHARED vma
	 * can only be in the i_mmap tree.  An anonymous MAP_PRIVATE, stack
	 * or brk vma (with NULL file) can only be in an anon_vma list.
	 */
	struct list_head anon_vma_chain; /* Serialized by mmap_lock &
					  * page_table_lock */
	struct anon_vma *anon_vma;	/* Serialized by page_table_lock */

	/* Function pointers to deal with this struct. */
	const struct vm_operations_struct *vm_ops;

	/* Information about our backing store: */
	unsigned long vm_pgoff;		/* Offset (within vm_file) in PAGE_SIZE
					   units */
	struct file * vm_file;		/* File we map to (can be NULL). */
	void * vm_private_data;		/* was vm_pte (shared mem) */

#ifdef CONFIG_SWAP
	atomic_long_t swap_readahead_info;
#endif
#ifndef CONFIG_MMU
	struct vm_region *vm_region;	/* NOMMU mapping region */
#endif
#ifdef CONFIG_NUMA
	struct mempolicy *vm_policy;	/* NUMA policy for the VMA */
#endif
	struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
} __randomize_layout;

根据函数说明,vm_area_struct结构描述了一个虚拟内存区域,每个VM区域/任务都有一个此结构。VM area(虚拟内存区域)是进程虚拟内存空间的任何部分,其具有用于页面错误异常处理(page-fault handlers)的特殊规则(即共享库、可执行区域等)。

要想完全弄清楚这个结构,只靠函数注释的寥寥数语是远远不够的,需要补齐相关知识,这就要“从头说起”而“说来话长”了。所谓“从头说起”,要从哪里说起?要由打Linux进程虚拟地址空间说起。

在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中,这个沙盘就是虚拟地址空间(Virtual Address Space)。以32位系统为例,在32位模式下它是一个4GB的内存地址块。在Linux系统中,内核进程和用户进程所占的虚拟内存比例是1:3(比例可调整),而Windows系统则为2:2(通过设置Large-Address-Aware Executables标志也可为1:3)。然而,这并不意味着内核使用那么多物理内存,仅表示它可支配这部分地址空间,根据需要将其映射到物理内存。

Linux进程在虚拟内存中的标准内存段布局如下图所示:

注:

(1)用户地址空间中的蓝色条带对应于映射到物理内存的不同内存段,浅黄绿色区域表示未映射的部分;

(2)Random stack offset和Random mmap offset等随机值意在防止恶意程序。Linux通过对栈、内存映射段、堆的起始地址加上随机偏移量来打乱布局,以免恶意程序通过计算访问栈、库函数等地址。

由上图可以看到,虚拟地址空间整体被划分为用户空间(User Space)和内核空间(Kernel Space)两大部分。当前我们重点关注用户空间部分。

用户进程部分内存区域(分段存储内容)主要可以分为以下几个部分(按地址由低到高递增顺序):

  • 保留区域(Reserved)

位于虚拟地址空间的最低部分,未赋予物理地址。任何对它的引用都是非法的,用于捕捉使用空指针和小整型值指针引用内存的异常情况。它并不是一个单一的内存区域,而是对地址空间中受到操作系统保护而禁止用户进程访问的地址区域的总称。大多数操作系统中,极小的地址通常都是不允许访问的,如NULL。C语言将无效指针赋值为0也是出于这种考虑,因为0地址上正常情况下不会存放有效的可访问数据。

在32位x86架构的Linux系统中,用户进程可执行程序一般从虚拟地址空间0x08048000开始加载。该加载地址由ELF文件头决定,可通过自定义链接器脚本覆盖链接器默认配置,进而修改加载地址。0x08048000以下的地址空间通常由C动态链接库、动态加载器ld.so和内核VDSO(内核提供的虚拟共享库)等占用。通过使用mmap系统调用,可访问0x08048000以下的地址空间。

  • 代码段(Code  Segment / Text Segment)

代码段也称正文段或文本段,通常用于存放程序执行代码(即CPU执行的机器指令)。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读(某些架构也允许代码段为可写,即允许修改程序)。

在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。也就是说,代码段存储的内容包括:可执行代码、字符串字面值、只读变量。

  • 数据段(Data Segment)

数据段通常用于存放程序中已初始化且初值不为0的全局变量和静态局部变量。数据段属于静态内存分配(静态存储区),可读可写。

数据段保存在目标文件中(在嵌入式系统里一般固化在镜像文件中),其内容由程序初始化。

  • BSS段(Block Started by Symbol Segment)

BSS段通常用于存放以下内容:

  • 未初始化的全局变量和静态局部变量;
  • 初始值为0的全局变量和静态局部变量(依赖于编译器实现);
  • 未定义且初值不为0的符号(该初值即common block的大小)。

在C语言中,未显式初始化的静态分配变量被初始化为0(算术类型)或空指针NULL(指针类型)。由于程序加载时,BSS会被操作系统清零,所以未赋初值或初值为0的全局变量都在BSS中。

注:

尽管均放置于BSS段,但初值为0的全局变量是强符号,而未初始化的全局变量是弱符号。若其它地方已定义同名的强符号(初值可能非0),则弱符号与之链接时不会引起重定义错误,但运行时的初值可能并非期望值(会被强符号覆盖);

数据段与BSS段的区别如下:

1)BSS段不占用物理文件尺寸,但占用内存空间;数据段占用物理文件,也占用内存空间;

2)当程序读取数据段的数据时,系统会出发缺页故障,从而分配相应的物理内存;当程序读取BSS段的数据时,内核会将其转到一个全零页面,不会发生缺页故障,也不会为其分配相应的物理内存。

  • 堆(Heap)

堆用于存放进程运行时动态分配的内存段,其大小并不固定,可动态扩张或缩减。堆中内容是匿名的,不能按名字直接访问,只能通过指针间接访问。

当进程调用malloc(C)/ new(C++)等函数分配内存时,新分配的内存动态添加到堆上(扩张);当调用free(C)/ delete(C++)等函数释放内存时,被释放的内存从堆中剔除(缩减)。

堆的末端由break指针标识,当堆管理器需要更多内存时,可通过系统调用brk()和sbrk()来移动break指针以扩张堆,一般由系统自动调用。

  • 栈(Stack)

栈又称堆栈,由编译器自动分配释放,行为类似数据结构中的栈(先进后出、后进先出)。堆栈主要有三个用途:

  • 为函数内部声明的非静态局部变量(C语言中称“自动变量”)提供存储空间;
  • 记录函数调用过程相关的维护性信息,称为栈帧(Stack Frame)或称为过程活动记录(Procedure Activation Record)。其包括:函数返回地址、不适合装入寄存器的函数参数及一些寄存器值。除递归调用外,堆栈并非必需。因为编译时可获知局部变量、参数和返回地址所需空间,并将其分配于BSS段;
  • 临时存储区,用于暂存长算术表达式部分计算结果或alloca()函数分配的栈内内存。

由于后进先出(LIFO)的特点,栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

############################################################################

补充知识:堆与栈的区别

1)管理方式

栈由编译器自动管理

堆由程序员控制,使用方便,但易产生内存泄露。

2)生长方向

栈向低地址扩展(即“向下生长”),是连续的内存区域;

堆向高地址扩展(即“向上生长”),是不连续的内存区域。

3)空间大小

栈顶地址和栈的最大容量由系统预先规定(通常默认2MB或10MB);

堆的大小则受限于计算机系统中有效的虚拟内存,32位Linux系统中堆内存可达2.9G空间。

4)存储内容

栈在函数调用时,首先压入主调函数中下条指令(函数调用语句的下条可执行语句)的地址,然后是函数实参,然后是被调函数的局部变量。本次调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的指令地址,程序由该点继续运行下条可执行语句;

堆通常在头部用一个字节存放其大小,堆用于存储生存期与函数调用无关的数据,具体内容由程序员安排。

5)分配方式

栈可静态分配或动态分配。静态分配由编译器完成,如局部变量的分配;动态分配由alloca函数在栈上申请空间,用完后自动释放;

堆只能动态分配且手工释放。

6)分配效率

栈由计算机底层提供支持。分配专门的寄存器存放栈地址,压栈出栈由专门的指令执行,因此效率较高;

堆由函数库提供,机制复杂,效率比栈低得多。

7)分配后系统响应

只要栈剩余空间大于所申请空间,系统将为程序提供内存,否则报告异常提示栈溢出;

操作系统为堆维护一个记录空闲内存地址的链表。当系统收到程序的内存分配申请时,会遍历该链表寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点空间分配给程序。若无足够大小的空间(可能由于内存碎片太多),有可能调用系统功能去增加程序数据段的内存空间,以便有机会分到足够大小的内存,然后进行返回。

8)碎片问题

栈不会存在碎片问题,因为栈是先进后出的队列,内存块弹出栈之前,在其上面的后进的栈内容已弹出;

而堆则存在碎片问题,因为频繁申请释放操作会造成堆内存空间的不连续,从而造成大量碎片,使程序效率降低。

############################################################################

在应用程序加载到内存空间执行时,操作系统负责代码段、数据段和BSS段的加载,并在内存中为这些段分配空间。栈也由操作系统分配和管理;堆由程序员自己管理,即显式地申请和释放空间。

经过了这么一大段“倒序”即所谓的“说来话长”,终于回到正题。此时再来看结构体注释,是不是种豁然开朗的感觉?

vm_area_struct结构描述了一个虚拟内存区域,每个VM区域/任务都有一个此结构。VM area(虚拟内存区域)是进程虚拟内存空间的任何部分,其具有用于页面错误异常处理(page-fault handlers)的特殊规则(即共享库、可执行区域等)。


说得更明白一些,就是每个vm_area_struct结构对应于虚拟内存空间中的唯一虚拟内存区域 VMA。虚拟内存区域就是上边的代码段(Text区域)、数据段(Data区域)、BSS段(BSS区域)、堆、栈等,它们每一个都对应一个唯一的vm_area_struct结构(实例)。如下图所示:

这样就弄清楚了vm_area_struct结构的总体意义,关于其成员的详细解析,请看下回。

来看具体成员:

  • unsigned long vm_start

vm_mm内的起始地址。

  • unsigned long vm_end

vm_mm结束地址后的第一个字节。

vm_start和vm_end指定了该区域在用户空间中的起始地址和结束地址。

  • struct mm_struct *vm_mm

所属的地址空间。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ps:选区的基本操作

在 Photoshop 中,选区是为处理局部图像而自行定义的一个区域。 定义选区后,操作被限制在选区之内。选区周围显示的虚线边框,俗称“蚂蚁线”。 全选 Select All Ps菜单:选择/全部 Select/All 快捷键:Ctrl A 提示&#…

NI USRP软件无线设备的特点

NI USRP软件无线设备 NI的USRP(Universal Software Radio Peripheral)设备是RF应用中使用的软件无线(SDR)。NI的USRP收发器可以在多个频段发送和接收RF信号,因此可用于通信工程教育和研究。通过与LabVIEW开发环境相结合,USRP可以实现使用无线信号验证无…

C/C++内存管理——“C++”

各位CSDN的uu们你们好呀,好久没有更新小雅兰的C专栏啦,下面,小雅兰继续开始更新C专栏的内容!!!今天,小雅兰的内容是C和C的内存管理,下面,让我们进入C的世界吧&#xff01…

【uniapp】仿微信通讯录列表实现

效果图 代码实现 <view class"main-container"><!-- 成员列表 --><scroll-viewclass"member-list":style"computedHeight":scroll-y"true":enable-back-to-top"true":scroll-with-animation"true"…

vue2+elementui使用MessageBox 弹框$msgbox自定义VNode内容:实现radio

虽说实现下面的效果&#xff0c;用el-dialog很轻松就能搞定。但是这种简单的交互&#xff0c;我更喜欢使用MessageBox。 话不多说&#xff0c;直接上代码~ <el-button type"primary" size"mini" click"handleApply()" >处理申请</el-b…

Python爬虫爬取家纺数据并分析

因为时间的原因&#xff0c;没法写一个详细的教程&#xff0c;但是我可以提供一个基本的框架。你需要根据实际情况进行修改和扩展。以下是使用Python的requests库和BeautifulSoup库来爬取网页内容的基本步骤&#xff1a; # 导入所需的库 import requests from bs4 import Beaut…

k8s二进制(ETCD的部署安装)

角色ip组件k8s-master192.168.11.169kube-apiserver,kube-controller-manager,kube-scheduler,etcdk8s-node1192.168.11.164kubelet,kube-proxy,docker,etcdk8s-node2192.168.11.166kubelet,kube-proxy,docker,etcd 1、为etcd签发证书 1、证书的下载(任意机器上执行都可以) …

Linux:权限篇 (彻底理清权限逻辑!)

shell命令以及运行原理&#xff1a; Linux严格意义上说的是一个操作系统&#xff0c;我们称之为“核心&#xff08;kernel&#xff09;“ &#xff0c;但我们一般用户&#xff0c;不能直接使用kernel。而是通过kernel的“外壳”程序&#xff0c;也就是所谓的shell&#xff0c;来…

故障诊断模型 | Maltab实现RF随机森林的故障诊断

效果一览 文章概述 故障诊断模型 | Maltab实现RF随机森林的故障诊断 模型描述 RF善于处理高维数据,特征遗失数据,和不平衡数据 (1)训练可以并行化,速度快 (2)对高维数据集的处理能力强,它可以处理成千上万的输入变量,并确定最重要的变量,因此被认为是一个不错的降…

【论文阅读】NeROIC:在线图像集合中对象的神经渲染

论文连接&#xff1a; NeROIC: Neural Rendering of Objects from Online Image Collections introduction 从在线图像集合中获取对象表示的新颖方法&#xff0c;从具有不同相机、照明和背景的照片中捕获任意对象的高质量几何形状和材料属性。这使得各种以对象为中心的渲染应…

Mysql数据库 14.SQL语言 视图

一、视图的概念 视图&#xff1a;就是由数据库中一张或多张表根据特定的条件查询出的数据狗造成的虚拟表 二、视图的作用 安全性&#xff0c;简单性 三、视图的语法 语法 create view 视图表 as select_statement; 代码实现 #创建视图 将查询结果创建称为视图&#x…

聊聊测试左移到需求阶段

这是鼎叔的第七十九篇原创文章。行业大牛和刚毕业的小白&#xff0c;都可以进来聊聊。 欢迎关注本公众号《敏捷测试转型》&#xff0c;星标收藏&#xff0c;大量原创思考文章陆续推出。本人新书《无测试组织-测试团队的敏捷转型》​​​​​​​已出版&#xff08;机械工业出版…

JAVA集合学习和源码分析

一、结构 List和Set继承了Collection接口&#xff0c;Collection继承了Iterable Object类是所有类的根类&#xff0c;包括集合类&#xff0c;集合类中的元素通常是对象&#xff0c;继承了Object类中的一些基本方法&#xff0c;例如toString()、equals()、hashCode()。 Collect…

云计算、大数据技术的智慧工地,实现对建筑工地实时监测、管理和控制的一种新型建筑管理方式

智慧工地是利用物联网、云计算、大数据等技术&#xff0c;实现对建筑工地实时监测、管理和控制的一种新型建筑管理方式。 智慧工地架构&#xff1a; 1、终端层&#xff1a; 充分利用物联网技术、移动应用、智能硬件设备提高现场管控能力。通过RFID、传感器、摄像头、手机等终…

Sectigo SSL

Sectigo&#xff08;前身为ComodoCA&#xff09;是全球在线安全解决方案提供商和全球最大的证书颁发机构。为了强调其在SSL产品之外的扩张&#xff0c;Comodo在2018年更名为Sectigo。新名称减少了市场混乱&#xff0c;标志着公司向创新的全方位网络安全解决方案提供商过渡。 S…

RVS—面向目标硬件的软件性能测试工具

产品概述 Rapita Verification Suite&#xff08;简称&#xff1a;RVS&#xff09;&#xff0c;为美国Danlaw公司提供的一款嵌入式系统在板测试套件&#xff0c;主要应用于汽车领域。其产品符合ISO-26262、DO178B/C、IEC-61508等行业标准&#xff0c;兼容Vxworks、Linux、SYSBI…

Windows 安装 JDK 8 和 JDK 17 和多版本JDK切换

下载 JDK JDK 8 下载地址&#xff1a;https://www.aliyundrive.com/s/koYe1SVRg76 JDK 17 下载地址&#xff1a; https://www.aliyundrive.com/s/tBcbUtAHTbg 安装 JDK 点击可执行文件 jdk-8u291-windows-x64.exe 进行安装 如果需要更改JDK安装目录&#xff0c;根据个人偏好…

从零开始搭建React+TypeScript+webpack开发环境-基于lerna的webpack项目工程化改造

项目背景 在实际项目中&#xff0c;我们的前端项目往往是一个大型的Webpack项目&#xff0c;结构较为复杂。项目根目录下包含了各种配置文件、源代码、以及静态资源&#xff0c;整体布局相对扁平。Webpack的配置文件分散在不同的部分&#xff0c;包括入口文件、输出目录、加载…

无需公网IP!部署Apache服务器与内网穿透实现公网访问

Apache服务安装配置与结合内网穿透实现公网访问 文章目录 Apache服务安装配置与结合内网穿透实现公网访问前言1.Apache服务安装配置1.1 进入官网下载安装包1.2 Apache服务配置 2.安装cpolar内网穿透2.1 注册cpolar账号2.2 下载cpolar客户端 3. 获取远程桌面公网地址3.1 登录cpo…

html与django实现多级数据联动

html与django实现多级数据联动 1、流程 1、进入页面后先获取年级数据 2、选择年级后获取院级数据 3、选择院级后获取层次数据 4、选择层次数据后获取专业数据 2、html代码 <p style"margin-top: 10px;"><label>年级</label><select id"…