linux进程地址空间

【Linux】进程的地址空间 地址是一个很重要的名词,我们的每一个进程在内存中运行都会有若干个地址。在之前我们学习进程的时候,学过一个函数——fork(),这个函数仅仅被调用一次,却能够返回两次。这是为什么呢?那么这一篇博客将会解释这种现象——进程的地址空间。 阅读详情

我们知道,在32位机器上linux操作系统中的进程的地址空间大小是4G,其中0-3G是用户空间,3G-4G是内核空间。其实,这个4G的地址空间是不存在的,也就是我们所说的虚拟内存空间。

那虚拟内存空间是什么呢,它与实际物理内存空间又是怎样对应的呢,为什么有了虚拟内存技术,我们就能运行比实际物理内存大的应用程序,它是怎么做到的呢?呵呵,这一切的一切都是个迷呀,下面我们就一步一步解开心中的谜团吧!

进程使用虚拟内存中的地址,由操作系统协助相关硬件,把它“转换”成真正的物理地址。虚拟地址通过页表(Page Table)映射到物理内存,页表由操作系统维护并被处理器引用。内核空间在页表中拥有较高特权级,因此用户态程序试图访问这些页时会导致一个页错误(page fault)。在Linux中,内核空间是持续存在的,并且在所有进程中都映射到同样的物理内存。内核代码和数据总是可寻址,随时准备处理中断和系统调用。与此相反,用户模式地址空间的映射随进程切换的发生而不断变化。

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

我们来看看,当我们写好一个应用程序,编译后它都有什么东东?

例如:

用命令size a.out会得到:

其中text是放的是代码,data放的是初始化过的全局变量或静态变量,bss放的是未初始化的全局变量或静态变量。

由于历史原因,C程序一直由下列几部分组成:

A、正文段。这是由cpu执行的机器指令部分。通常,正文段是可共享的,所以即使是经常执行的程序(如文本编辑程序、C编译程序、shell等)在存储器中也只需要有一个副本,另外,正文段常常是只读的,以防止程序由于意外事故而修改器自身的指令。

B、初始化数据段。通常将此段称为数据段,它包含了程序中需赋初值的变量。例如,C程序中任何函数之外的说明:

int maxcount = 99;(全局变量)

C、非初始化数据段。通常将此段称为bss段,这一名称来源于早期汇编程序的一个操作,意思是"block started by symbol",在程序开始执行之前,内核将此段初始化为0。函数外的说明:

long sum[1000];

使此变量存放在非初始化数据段中。

D、栈。自动变量以及每次函数调用时所需保存的信息都存放在此段中。每次函数调用时,其返回地址、以及调用者的环境信息(例如某些机器寄存器)都存放在栈中。然后,新被调用的函数在栈上为其自动和临时变量分配存储空间。通过以这种方式使用栈,C函数可以递归调用。

E、堆。通常在堆中进行动态存储分配。由于历史上形成的惯例,堆位于非初始化数据段顶和栈底之间。

从上图我们看到栈空间是下增长的,堆空间是从下增长的,他们会会碰头呀?一般不会,因为他们之间间隔很大,如:

复制代码
     #include <stdio.h>
        #include <stdlib.h>
        int bss_var;
        int data_var0 = 1;
        int main()
        {
                printf("Test location:\n");
                printf("\tAddress of main(Code Segment):%p\n",main);
                printf("_____________________________________\n");
                int stack_var0 = 2;
                printf("Stack location:\n");
                printf("\tInitial end of stack:%p\n",&stack_var0);
                int stack_var1 = 3;
                printf("\tNew end of stack:%p\n",&stack_var1);
                printf("_____________________________________\n");
                printf("Data location:\n");
                printf("\tAddress of data_var(Data Segment):%p\n",&data_var0);
                static int data_var1 = 4;
                printf("\tNew end of data_var(Data Segment):%p\n",&data_var1);
                 printf("_____________________________________\n");
                printf("BSS location:\n");
                printf("\tAddress of bss_var:%p\n",&bss_var);
                printf("_____________________________________\n");
                 printf("Heap location:\n");
                char *p = (char *)malloc(10);
                printf("\tAddress of head_var:%p\n",p);
                return 0;
        }
复制代码

运行结果如下:

呵呵,这里我们看到地址了,这个地址是虚拟地址,这些地址时怎么来的呢?其实在我们编译的时候,这些地址就已经确定了,如下图中红线。

也就是说,我们不论我们运行a.out程序多少次这些地址都是一样的。我们知道,linux操作系统每个进程的地址空间都是独立的,其实这里的独立说得是物理空间上得独立。进程可以使用相同的虚拟地址,这不奇怪,因为转换后的物理地址并非相同的。那相同的虚拟地址,不同的物理地址,他们之间是怎样联系起来的呢?我们继续探究....

在linux操作系统中,每个进程都通过一个task_struct的结构体描叙,每个进程的地址空间都通过一个mm_struct描叙,c语言中的每个段空间都通过vm_area_struct表示,他们关系如下 :

当运行一个程序时,操作系统需要创建一个进程,这个进程和程序之间都干了些什么呢?

当一个程序被执行时,该程序的内容必须被放到进程的虚拟地址空间(注意,是虚拟地址空间),对于可执行程序的共享库也是如此。可执行程序并非真正读到物理内存中,而只是链接到进程的虚拟内存中(此时都是在进程的虚拟地址空间)。

当一个可执行程序映射到进程虚拟地址空间时,一组vm_area_struct数据结构将被产生。每个vm_area_struct数据结构表示可执行印象的一部分;是可执行代码,或是初始化的数据,以及未初始化的数据等。

linux操作系统是通过sys_exec对可执行文件进行映射以及读取的,有如下几步:

1、创建一组vm_area_struct;

2、圈定一个虚拟用户空间,将其起始结束地址(elf段中已设置好)保存到vm_start和vm_end中;

3、将磁盘file句柄保存在vm_file中;

4、将对应段在磁盘file中的偏移值(elf段中已设置好)保存在vm_pgoff中;

5、将操作该磁盘file的磁盘操作函数保存在vm_ops中;

注意:这里没有对应 的页目录表项创建页表,更不存在设置页表项了。

假设现在程序中有一条指令需要读取上面vm_start--vm_end之间的某内容

例如:mov [0x08000011],%eax,那么将会执行如下序列:

1、cpu依据CR3(current->pgd)找到0x08000011地址对应的pgd[i],由于该pgd[i]内容保持为初始化状态即为0,导致cpu异常。

2、.do_page_fault被调用,在该函数中,为pgd[i]在内存中分配一个页表,并让该表项指向它,如下图所示:

注意:这里i为0x08000011高10位,j为其中间10位,此时pt表项全部为0(pte[j]也为0);

3、为pte[j]分配一个真正的物理内存页面,依据vm_area_struct中的vm_file、vm_pgoff和vm_ops,调用filemap_nopage将磁盘file中vm_pgoff偏移处的内容读入到该物理页面中,如下图所示:

①分配物理内存页面;

②从磁盘文件中将内容读取到物理内存页面中

从上面我们可以知道,在进程创建的过程中,程序内容被映射到进程的虚拟内存空间,为了让一个很大的程序在有限的物理内存空间运行,我们可以把这个程序的开始部分先加载到物理内存空间运行,因为操作系统处理的是进程的虚拟地址,如果在进行虚拟到物理地址的转换工程中,发现物理地址不存在时,这个时候就会发生缺页异常(nopage),接着操作系统就会把磁盘上还没有加载到内存中的数据加载到物理内存中,对应的进程页表进行更新。也许你会问,如果此时物理内存满了,操作系统将如何处理?

下面我们看看linux操作系统是如何处理的:

如果一个进程想将一个虚拟页装入物理内存,而又没有可使用的空闲物理页,操作系统就必须淘汰物理内存中的其他页来为此页腾出空间。

在linux操作系统中,物理页的描叙如下:

struct mem_map

{

1、本页使用计数,当该页被许多进程共享时计数将大于1

2、age描叙本页的年龄,用来判断该页是否为淘汰或交换的好候选

3、map_nr描叙物理页的页帧号

}

如果从物理内存中被淘汰的页来自于一个映像或数据文件,并且还没有被写过,则该页不必保存,它可以丢掉。如果有进程在需要该页时就可以把它从映像或数据文件中取回内存。

然而,如果该页被修改过,操作系统必须保留该页的内容以便晚些时候在被访问。这种页称为"脏(dirty)页",当它被从内存中删除时,将被保存在一个称为交换文件的特殊文件中。

相对于处理器和物理内存的速度,访问交换文件要很长时间,操作系统必须在将页写到磁盘以及再次使用时取回内存的问题上花费心机。

如果用来决定哪一页被淘汰或交换的算法不够高效的话,就可能出现称为"抖动"的情况。在这种情况下,页面总是被写到磁盘又读回来,操作系统忙于此而不能进行真正的工作。

linux使用"最近最少使用(Least Recently Used ,LRU)"页面调度技巧来公平地选择哪个页可以从系统中删除。这种设计系统中每个页都有一个"年龄",年龄随页面被访问而改变。页面被访问越多它越年轻;被访问越少越老。年老的页是用于交换的最佳候选页。

Linux: 进程地址空间究竟是什么?进程地址空间存在意义何在? 一、内存究竟是什么?分为哪些? 二、内存是真实物理空间? 三、进程地址空间(虚拟地址) 3.1 为何同一个变量地址相同,保存的数据却不同? 四、为什么需要地址地址空间和页表的存在? 4.1 真实内存无序便有序 4.2 进行内存安全检查 4.3 进程管理和内存管理藕接 五、其他细节问题 5.1 操作系统如何获取进程页表相关信息 5.2 操作系统如何确定内存中是否分配空间、是否存在内容? 阅读详情

相关推荐

样本不均衡时AUC反而下降?用imbalanced-learn库实战解决分类陷阱

本文探讨了样本不均衡时AUC指标下降的现象,并通过imbalanced-learn库实战演示了如何解决这一分类陷阱。文章详细分析了不同采样技术(如SMOTE、ADASYN)对模型性能的影响,并提供了XGBoost和逻辑回归模型的优化建议,帮助数据科学家在实际项目中做出更明智的决策。

weixin_29174013的博客 343

linux 进程地址空间的一步步探究

我们知道,在32位机器上linux操作系统中的进程的地址空间大小是4G,其中0-3G是用户空间,3G-4G是内核空间。其实,这个4G的地址空间是不存在的,也就是我们所说的虚拟内存空间。 那虚拟内存空间是什么呢,它与实际物理内存空间又是怎样对应的呢,为什么有了虚拟内存技术,我们就能运行比实际物理内存大的应用程序,它是怎么做到的呢?呵呵,这一切的一切都是个迷呀,下面我们就一步一步解开心中的谜团吧! ...

weixin_34138521的博客 207

AI市场风起云涌,ai浏览器是最佳的落地项目,现在ai市场的ai浏览器竞争加剧,得ai浏览器者得天下!

从场景覆盖度来看,AI 浏览器已突破 “网页浏览” 的传统边界,成为连接个人生活、工作、消费的 “超级入口”,这是其他 AI 应用难以比拟的落地优势。在个人生活场景,AI 浏览器可基于用户行为数据主动提供服务 —— 当用户浏览马拉松赛事信息时,浏览器能自动生成 “训练计划 + 装备推荐 + 能量补给方案”,并关联预订链接;在工作场景,Opera Neon 浏览器已实现 “网页内容提取 - 代码生成 - 报告撰写” 的全流程辅助,开发者可直接在浏览器内完成网站开发初稿;

zhang9880000的博客 1086

linux线程大小,Linux中进程/线程的大小

Linux中进程/线程的大小是多少?创建进程/线程时,还有task_struct和其中的其他数据结构,还有什么吗?进程/线程初始化(固定大小)时是否分配了进程/线程的堆栈?或者在必要时分配(如虚拟内存)?在内存中创建标准进程/线程时,如何知道它的大小?解决方法:当在Linux上首次分配大块内存(> pagesize = 4096字节)时,它在页面表中使用了特殊的“空”内存页面,这些内存页面没...

weixin_39994806的博客 318

从波士顿动力SpotMini拆解看四足机器人机械设计:关节布局与电机选型的5个关键决策点

本文深入解析波士顿动力SpotMini四足机器人的机械设计,聚焦关节布局与电机选型的5个关键决策点。通过拆解髋关节偏移距、膝关节传动链、电机热管理等核心设计,揭示商业级四足机器人的工程智慧,为工业巡检、物流机器人等应用提供实用参考。

postgres8guard的博客 893

Python+Seaborn可视化:双因素方差分析结果这样呈现更专业

本文详细介绍了如何利用Python中的Seaborn和statsmodels库,专业地执行并可视化双因素方差分析结果。通过模拟实验数据,演示了从模型构建、统计检验(包括交互作用分析)到诊断验证的全流程。重点展示了如何使用Seaborn绘制交互效应图、组合分布图及热图,将复杂的统计结果转化为直观、富有洞察力的可视化图表,助力科研论文与商业报告的专业呈现。

weixin_29274969的博客 137

linux地址空间(虚拟地址)

本节我们将涉及虚拟地址,物理地址,页表以及CPU获取数据流程

菜菜成长为后端开发大佬 1638

ROS2_Moveit2_Ur5e_Grasp项目详解(二):核心模块功能解析

本文深入解析了ROS2_Moveit2_Ur5e_Grasp项目的核心模块功能。重点阐述了OctoMap建图模块如何为机械臂提供实时三维环境感知,并详细介绍了Robotiq夹爪与MoveIt2的集成控制、UR5e的运动规划策略,以及视觉模块如何协同工作,最终实现从目标检测到动态抓取的完整闭环。

weixin_29254029的博客 112

Linux下进程虚拟地址空间详解

进程虚拟地址空间一,程序地址空间进程虚拟地址空间写时拷贝存储管理方式--分页式存储管理方式--分段式存储管理方式--段页式 一,程序地址空间 计算机物理内存的大小是固定的,就是计算机主板内存槽上的实际物理内存,cpu可以直接进行寻址,物理内存的容量是固定的,但是寻址的空间取决于cpu地址线的数量。在32位系统上,线性地址空间可达4G(2^32);这4G一般是按照3:1的比例进行分配,用户进程享有3G的空间,而内核独自享有剩下的1G内存。 早期的内存分派方式: 最早的时候,计算机还没有虚拟机制,程序

qq_931027577 5775

在工作台 LS-DYNA 中检索和变换反作用力分量

在结构仿真中,通常需要由施加的位移或速度边界条件产生的反作用力。此外,通常需要根据用户定义的坐标系解析反作用力分量。在本文中,详细介绍了在Workbench (WB) LS-DYNA 中,相对于用户定义的坐标系检索和变换反作用力分量的过程。

jkh920184196的博客 885

给跨代引用记一本账:Java GC 的分配路径、卡表写屏障与收集器版图快速回顾

本文把 Java GC 的零散名词串成一条推理链路:从 TLAB 指针碰撞分配、逃逸分析,到 Eden、Survivor 与晋升的四条路,重点拆解卡表与写屏障如何为老年代指向新生代的引用记账,及 Minor GC 如何只扫脏卡。基于 JDK 8 实测:分散写使 Young GC 停顿涨 10~15 倍,条件卡标记消除卡表伪共享,计数循环让 System.gc 等待 2.7 秒,Survivor 过小使晋升量增加约 400 倍。文末梳理 JDK 8 到 27 的收集器演进、选型路径与排查顺序。

曾经“等你生日那天”都遥远得像未来,如今却可欢愉的挥手说“下个十年见” 17万+

Linux 虚拟地址空间(通俗易懂)

该文章主要是真对面试做大致的了解,不做详细介绍!!! 下面由高地址到低地址依次介绍:内核空间为内核保留,不允许应用程序读写该区域的内容或直接调用内核代码定义的函数。可以通过对Linux系统进行系统调用。栈是由高地址向低地址扩展的连续内存,栈的大小一般为 2M 或者 10M。栈内存是由系统分配,系统释放的,以函数为单位进行栈内存分配,函数栈帧,局部变量,形参变量等都存放在栈内存上。栈的内存分配释放速度快效率高,内存都是连续的。堆是由低地址向高地址扩展的非连续内存,堆的大小影响的因素比较多,和系统虚拟内存的大

热爱数据库的小胖 2104

TVBox源总失效?不如自己搭建一个!告别不稳定!手把手教你自建TVBox源,全网资源尽掌握!

🚀 TVBox源总失效?别慌!手把手教你自建TVBox源,打造一个稳定、海量、无广告的影视宝库!从此告别片荒,尽享丝滑观影体验!

XiaoqiangClub的博客 5458

【Linux】进程地址空间

进程地址空间的组织和它的优势,以及页表的一些补充说明

s_little_monster的博客 8994

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Linux进程地址空间

我们知道对于C/C++程序来说,程序占用的内存地址分为栈区,堆区,静态区等等,每一个程序在编译时都会分配这些空间,这些空间只是规划的虚拟地址空间,并不是分配的真实的物理空间,这样划分虚拟地址空间是操作系统为了更好的管理和使用内存空间而做的,那么虚拟地址(也称逻辑地址)是如何与物理地址联系起来的呢?本节将为您介绍!进程地址空间的介绍到这里就告一段落了,相信大家认识了Linux底层的虚拟地址空间后,对很多进程地址相关的问题一定有所解答!

ARMCSKGT的博客 5511

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

【Linux系统】进程地址空间详解

进程地址空间是操作系统为每个运行中的进程分配的一个虚拟内存视图,它是所能访问的所有内存地址的集合。这给抽象层使得每个进程看起来都像是占用了整个计算机的内存资源,而实际上这些需您地址会v欸操作系统映射到物理内存中。本篇我将介绍进程地址空间的概念及使用这种方法带来的好处。在学习这块之前,我们需要一些背景知识作为铺垫学完上面的我们就可以知道为什么说进程相互独立:进程的PCB结构体是独立的、进程地址空间是独立的、页表是独立的、对进程切换时只需要切换PCB结构体对象就可以了。

2301_80774875的博客 2023

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Linux —— 虚拟进程地址空间

页表是虚拟内存系统的核心数据结构,是连接虚拟地址和物理地址的“地图”或“翻译官”;是Kernel为每个进程维护的一个映射表,它记录了该进程的虚拟内存页对应到物理内存帧的映射关系简单来说,它的工作就是回答这个问题:“这个进程看到的虚拟地址 X,实际上在物理内存的哪个地方?OS 会将进程物理地址隐藏起来,我们只能观测到进程的虚拟地址。

2401_87692970的博客 1959

Linux:进程地址空间

对进程地址空间的认识

2301_82256131的博客 1829

Linux系统-进程地址空间

Linux进程地址空间零、前言一、程序内存空间二、进程地址空间1、引入及概念2、进程地址空间3、相关问题 零、前言 本章主要讲解学习进程地址空间的知识 一、程序内存空间 在学习C/C++中我们知道了程序内存的空间开辟以及内存分区的基本概念 示图: 各分区作用: 内核空间:用户代码无法读写 命令行参数环境变量:储存命令行参数环境变量 栈区:存放运行函数而分配的局部变量、函数参数、返回数据、 返回地址等,栈区地址向下生长 共享区:储存文件映射,匿名映射,动态库 堆区:存放

~ 1320

Linux进程——进程地址空间

到这里我们的进程地址空间也接近尾声了,地址空间让进程管理和内存管理互不干涉,起到了很大作用。结束进程地址空间,我们的Linux进程概念到这里也结束了,后面我将带大家走进进程控制。

EterNity_TiMe_的计算机学习之路 950

linux进程概念——虚拟地址空间——重新认识进程!!!

linux进程篇进入新的小节——地址空间

strive_mianyang的博客 4540

【Linux】深挖进程地址空间

学习每一个语言都跟地址扯不开关系,这也是学习每一种语言的必修课,学习好地址空间对语言的掌握熟练度就是质的飞越,我们学习代码知识只是语言的表面,像地址空间就比较深层,有点抽象,不好理解,基于这种情况博主给大家带来【Linux】深挖进程地址空间。⭐主体我们从以下学习【Linux】深挖进程地址空间😋😋。简单来讲就是是什么,为什么,怎么做😜😜。再次回顾 C/C++ 学习地址空间时候:地址空间是什么?是内存么?我们举个栗子:int main()while (1)

AAlykk的博客 3152

【Linux】初始进程地址空间

Linux进程地址空间!

小小小智_x0__0x_的博客 3687
上一篇: Linux设备树解析
下一篇: Linux块设备驱动
LinuxWorking
博客等级 码龄10年 24粉丝 32原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值