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

news2024/11/24 20:59:29

接前一篇文章: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

所属的地址空间。

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

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

相关文章

SpringData、SparkStreaming和Flink集成Elasticsearch

本文代码链接:https://download.csdn.net/download/shangjg03/88522188 1 Spring Data框架集成 1.1 Spring Data框架介绍 Spring Data是一个用于简化数据库、非关系型数据库、索引库访问,并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快…

vue项目pdf文件的预览

1.下载 您可以在以下网址下载pdfjsLib&#xff1a;https://github.com/mozilla/pdf.js pdfjsLib是一个开源项目&#xff0c;您可以在GitHub上找到其源代码和相关资源。 2.放置文件位置 3.进入 在index.html引入 <script src"<% BASE_URL %>static/pdfjs-dist/b…

赛宁网安入选国家工业信息安全漏洞库(CICSVD)2023年度技术组成员单

近日&#xff0c;由国家工业信息安全发展研究中心、工业信息安全产业发展联盟主办的“2023工业信息安全大会”在北京成功举行。 会上&#xff0c;国家工业信息安全发展研究中心对为国家工业信息安全漏洞库&#xff08;CICSVD&#xff09;提供技术支持的单位授牌表彰。北京赛宁…

WPF ToggleButton 主题切换动画按钮

WPF ToggleButton 主题切换动画按钮 仿造最近看到的html中的一个效果&#xff0c;大致思路是文章这样&#xff0c;感觉还可以再雕琢一下。 代码如下 XAML: <UserControl x:Class"WPFSwitch.AnimationSwitch"xmlns"http://schemas.microsoft.com/winfx/200…

8255 boot介绍及bring up经验分享

这篇文章会简单的介绍8255的启动流程&#xff0c;然后着重介绍8255在实际项目中新硬件上的bring up工作&#xff0c;可以给大家做些参考。 8255 boot介绍 下面这些信息来自文档&#xff1a;《QAM8255P IVI Boot and CoreBSP Architecture Technical Overview》 80-42847-11 R…

Django debug page XSS漏洞复现_(CVE-2017-12794)

Django debug page XSS漏洞复现_(CVE-2017-12794) 复现过程 首先进入靶场环境 按照他写的&#xff0c;需要给这个变量赋值 创建一个用户&#xff0c;用弹窗做用户名 http://10.4.7.137:8000/create_user/?username<script>alert(1)</script>返回&#xff0c;然…

Unity中Shader的间接光的产生Meta Pass

文章目录 前言Unity中Shader的间接光的产生Meta Pass&#xff0c;这也是属于全局光照 GI 的内容。主要实现像现实生活中&#xff0c;光线照到有颜色的物体后&#xff0c;该物体有反射出该颜色的光的效果。 一、我们先使用Unity自带的Shader看看间接光效果1、先按照如下设置搭建…

C++ | 继承和多态

目录 继承 继承的概念及用法 继承的作用域 向上转型和向下转型 继承过程中的默认生成函数 菱形继承及其解决方案 - 虚继承 虚继承的原理 - 虚基类表 继承和组合 多态 虚函数 多态的定义及使用 纯虚函数与抽象类 多态的原理 小点补充 虚表的位置 父类指针new一个…

聊聊测试左移到需求阶段

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

单链表的基本操作代码

#include<stdio.h> #include<stdlib.h> typedef int Status;//函数类型&#xff08;当return值为函数结果状态时用&#xff09;需要自定义&#xff01; typedef int ElemType;//相当于给 int起个别名 typedef struct LinkList{ ElemType data; struct L…

JMeter实现持续压测websocket

1、安装插件&#xff1a;JMeter WebSocket Samplers pjtr / JMeter WebSocket Samplers / Downloads — Bitbuckethttps://bitbucket.org/pjtr/jmeter-websocket-samplers/downloads/ 将下载的Jar包放在安装jmeter的/lib/ext路径下&#xff0c;重启生效 查看测试计划--》配置…

python爬虫怎么翻页

爬虫程序的代码实现如下&#xff1a; #include <iostream> #include <string> #include <curl/curl.h>int main() {CURL *curl;CURLcode res;std::string readBuffer;curl_global_init(CURL_GLOBAL_DEFAULT);curl curl_easy_init();if(curl) {curl_easy_se…

ChatGPT、GPT-4 Turbo接口调用(stream模式)

接口地址 https://chat.xutongbao.top/api/light/chat/createChatCompletion 请求方式 post 请求参数 model可选值&#xff1a; “gpt-3.5-turbo-1106”、 “gpt-3.5-turbo-16k” 、 “gpt-4”、“gpt-4-1106-preview”。 默认值为&#xff1a; “gpt-3.5-turbo-1106” to…

LeetCode 17. 电话号码的字母组合 中等

题目 - 点击直达 1. 17. 电话号码的字母组合 中等1. 题目详情1. 原题链接2. 题目要求3. 基础框架 2. 解题思路1. 思路分析2. 时间复杂度3. 代码实现 3. 知识与收获 1. 17. 电话号码的字母组合 中等 1. 题目详情 1. 原题链接 LeetCode 17. 电话号码的字母组合 中等 2. 题目要…

后端架构选择:构建安全强大的知识付费小程序平台

构建知识付费小程序平台需要考虑后端架构&#xff0c;确保系统安全性、性能和可扩展性。以下是一些常见的后端技术和最佳实践&#xff0c;能帮助您构建强大且安全的知识付费小程序平台。 1. 服务器端语言和框架选择 选择流行、成熟的后端语言和框架&#xff0c;如Node.js、P…

基于php js+mysql+laravel技术架构的手术麻醉管理系统源码 手麻系统源码

PHP手术麻醉管理系统源码 手麻系统源码 手术麻醉管理系统定义&#xff1a; 手术麻醉系统主要是由麻醉信息管理和监护设备数据采集系统两个部分组成&#xff0c;主要是将麻醉信息和从监护仪器上采集到的数据以及手术信息进行统计。 手术麻醉系统是指专用于住院患者手术与麻醉…

AR工业眼镜:智能化生产新时代的引领者!!

科技飞速发展&#xff0c;人工智能与增强现实&#xff08;AR&#xff09;技术结合正在改变生活工作方式。AR工业眼镜在生产领域应用广泛&#xff0c;具有实时信息展示、智能导航定位、远程协作培训、智能安全监测等功能&#xff0c;提高生产效率、降低操作风险&#xff0c;为企…

网络通信TCP、UDP详解

目录 IP 和端口 网络传输中的 2 个对象&#xff1a;server 和 client 两种传输方式&#xff1a;TCP/UDP TCP 和 UDP 原理上的区别 为何存在 UDP 协议 TCP/UDP 网络通信大概交互图 IP 和端口 所有的数据传输&#xff0c;都有三个要素 &#xff1a;源、目的、长度。 怎么表…

开机自启动笔记本的小键盘

虽然电脑开机次数不多&#xff0c;但每次开机都要摁下小键盘的开关&#xff0c;好烦 终于忍不住了&#xff1a; 将下面文件命名为 XXX.bat echo off rem 禁用批处理文件中的命令回显&#xff0c;以使输出更整洁rem 查询注册表中 "InitialKeyboardIndicators" 的值 r…

devops完整搭建教程(gitlab、jenkins、harbor、docker)

devops完整搭建教程&#xff08;gitlab、jenkins、harbor、docker&#xff09; 文章目录 devops完整搭建教程&#xff08;gitlab、jenkins、harbor、docker&#xff09;1.简介&#xff1a;2.工作流程&#xff1a;3.优缺点4.环境说明5.部署前准备工作5.1.所有主机永久关闭防火墙…