Linux程序地址空间

引言

以下为示意草图 

下面以代码验证一下: 

  1 #include<stdio.h>                                                                                                                     2 #include<stdlib.h>3                                       4 int un_gval;                          5 int init_gval=666;                    6                                       7 int main()                            8 {                                     9   printf("code addr: %p\n", main);    10   const char *str = "hello Linux";    11                                       12    printf("read only char addr: %p\n", str);13    printf("init global value addr: %p\n", &init_gval);14    printf("uninit global value addr: %p\n", &un_gval);15                                                       16    char *heap1 = (char*)malloc(100);                  17    printf("heap1 addr : %p\n", heap1);18     printf("stack addr : %p\n", &str);19   return 0;                           20 }

其中堆区与栈区之间有一大块镂空,堆区向上增长,栈区向下增长,堆栈相对而生

代码验证堆区向上增长:

 16    char *heap1 = (char*)malloc(100);17    char *heap2 = (char*)malloc(100);18    char *heap3 = (char*)malloc(100);19    char *heap4 = (char*)malloc(100);20    printf("heap1 addr : %p\n", heap1);21    printf("heap2 addr : %p\n", heap2);22    printf("heap3 addr : %p\n", heap3);23    printf("heap4 addr : %p\n", heap4);

 

代码验证栈区向下增长:

25    printf("stack addr : %p\n", &str);26    printf("stack addr : %p\n", &heap1);27    printf("stack addr : %p\n", &heap2);28    printf("stack addr : %p\n", &heap3);29    printf("stack addr : %p\n", &heap4); 

 

在栈区上开辟的变量,整体是向下增长的,但是每个对象的使用是局部向上使用

以该对象的的最低地址作为地址

  7 struct s  8 {  9   int a;  10   int b;  11   int c;  12 }obj;13   printf("%p\n",&obj.a);14   printf("%p\n",&obj.b);15   printf("%p\n",&obj.c);    

 

在栈区之上是命令行参数与环境变量存储区域

代码验证:

int main(int argc, char *argv[], char *env[])
{ int i= 0;for(; argv[i]; i++){printf("argv[%d]: %p\n",i, argv[i]);}for(i=0; env[i]; i++){printf("env[%d]: %p\n", i, env[i]);}return 0;
}

 

以下图所示真的是我们认为的内存吗? 

 来看一段代码:

int g_val = 555;int main()
{pid_t id = fork();if(id == 0){//childint cnt = 5;while(1){printf("child, Pid: %d, Ppid: %d, g_val: %d, &g_val=%p\n", getpid(), getppid(), g_val, &g_val);sleep(1);if(cnt == 0){g_val=888;printf("child change g_val: 555->888\n");}cnt--;}}else{//fatherwhile(1){printf("father, Pid: %d, Ppid: %d, g_val: %d, &g_val=%p\n", getpid(), getppid(), g_val, &g_val);sleep(1);}}sleep(100);return 0;
}

这里有一个问题:对同一个地址进行读取,竟然读取到了不同的内容

变量内容不一样,所以父子进程输出的变量绝对不是同一个变量
但地址值是一样的,说明,该地址绝对不是物理地址
在Linux地址下,这种地址叫做 虚拟地址

结论:C/C++看到的地址,绝对不是物理地址,物理地址,用户一概看不到,由OS统一管理(OS必须负责将 虚拟地址 转化成 物理地址

           我们平时用到的地址,都是虚拟地址/线性地址 

什么是进程地址空间

每一个进程运行之后,都会有一个进程地址空间的存在,也都要在系统层面有自己的页表映射结构 ,进程地址空间不存储任何数据,所有数据都存储在物理内存中

每个进程都有自己的task_struct结构体

 

子进程尾修改数据前与父进程共享代码和数据,它们的虚拟地址是一样的,根据虚拟地址在页表映射出的物理地址也是一样的,所以前期打印出来的g_val的值相同

当子进程要修改g_val,那么发生写时拷贝,子进程页表中的g_val的物理地址改变,虚拟地址不变,所以子进程打印g_val=888 父进程打印g_val=555 ,&g_val父子进程打印出来都相同 

因为我们打印出来的不是真正的物理地址,而是虚拟地址

结论:1 写时拷贝发生在物理内存中  2 这份工作由操作系统来完成 3 知道与否不会影响上层语言

每个进程要被OS管理(先描述再组织),所以每个进程都有各自的task_struct, 每个进程都有的地址空间,所以地址空间也要被OS管理 (先描述再组织)-->地址空间最终一定是一个内核的数据结构对象,就是一个内核结构体

在linux中,这个进程/虚拟地址空间,叫做:mm_struct 

其中每个进程的地址空间都有区域划分,有每个区域的start与end

 挑重点:

每个进程被创建时,既有自己的PCB即task_struct 也有自己的地址空间即mm_struct 

 为什么要有地址空间

1 让进程以统一的视角看待内存,所以任意一个进程,可以通过地址空间+页表将乱序的内存数据,变成有序,分门别类地规划好

2  存在虚拟地址空间,可以有效地进行进程访问内存的安全检查

页表结构中除却虚拟地址与物理地址外,还存在访问权限字段

举个例子:我们常说字符常量区不可被修改,所以以下代码是不被通过的:

char *str = "hello Linux";
*str = 'H';

为什么呢? 那曾经又是怎么被加载的?内存不是可以被读写的吗?

原因:字符串常量区的数据在页表映射时,它的访问权限字段被设置为"r"(只读),所以试图修改字符串常量区数据的写入操作在页表映射阶段就被拦截,所以无法修改。

每个进程有各自的地址空间,各自的页表,那么在众多页表中,每个进程怎么找到属于自己的那一张呢?

原因:进程进行各种虚拟地址到物理地址的转换,各种访问内存,一定是这个进行正在CPU上运行

CPU内有一个叫做CR3的寄存器,会存储该进程页表的物理地址,当该进程在CPU上加载时,会把自己的上下文数据中存有的页表地址加载到CR3中,当该进程要切换走时,会把CR3寄存器中存储的页表地址一并带走,所以每个进程都有自己的页表

3 将进程管理与内存管理进行解耦 

页表结构中还存在一个字段用于:内存是否分配以及是否有内容

此外,通过页表让进程的代码和数据映射到不同的物理内存处,从而实现进程的独立性质

进程=内核数据结构+进程的代码和数据

最后,起始完整的地址空间如下图:

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

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

相关文章

Git合并多个commit

git rebase -i commitId 假设想要合并最后3个commit&#xff0c; git log显示 commit id 1 commit id 2 commit id 3 commit id 4 则执行git rebase -i commitId4. 注意是4&#xff0c;不是3. 然后&#xff0c;pick最老的commit (commit id 3). https://blog.csdn.net/qiao…

java设计模式之中介者模式

中介者模式&#xff08;Mediator Pattern&#xff09; 基本介绍 中介者模式&#xff0c;用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用&#xff0c;从而使其解耦松散。而且可以独立地改变它们之间的交互。中介者模式属于行为型模式。比如MVC模…

动漫风博客介绍页面源码

动漫风博客介绍页面源码&#xff0c;HTML源码&#xff0c;图片背景有淡入切换特效 蓝奏云&#xff1a;https://wfr.lanzout.com/iIDZu1nrmjve

Python 小白的 Leetcode Daily Challenge 刷题计划 - 20240209(除夕)

368. Largest Divisible Subset 难度&#xff1a;Medium 动态规划 方案还原 Yesterdays Daily Challenge can be reduced to the problem of shortest path in an unweighted graph while todays daily challenge can be reduced to the problem of longest path in an unwe…

SparkJDBC读写数据库实战

默认的操作 代码val df = spark.read.format("jdbc").option("url", "jdbc:postgresql://localhost:5432/testdb").option("user", "username").option("password", "password").option("driver&q…

基于鲲鹏服务器的LNMP配置

基于鲲鹏服务器的LNMP配置 系统 Centos8 # cat /etc/redhat-release CentOS Linux release 8.0.1905 (Core) 卸载已经存在的旧版本的安装包 # rpm -qa | grep php #查看已经安装的PHP旧版本# rpm -qa | grep php | xargs rpm -e #卸载已经安装的旧版&#xff0c;如果提示有…

在面试中如何回复擅长vue还是react

当面试官问及这个问题的时候&#xff0c;我们需要思考面试官是否是在乎你是掌握vue还是react吗&#xff1f;&#xff1f;&#xff1f; 在大前端的一个环境下&#xff0c;当前又有AI人工智能的加持辅助&#xff0c;我们是不是要去思考企业在进行前端岗位人员需求的时候&#xf…

阿里云幻兽帕鲁服务器免费搭建解决方法,白嫖阿里云

阿里云幻兽帕鲁服务器免费搭建方案&#xff0c;先在阿里云高校计划「云工开物」活动领取学生专享300元无门槛代金券&#xff0c;幻兽帕鲁专用服务器4核16G配置26元1个月、149元半年&#xff0c;直接使用这个无门槛300元代金券抵扣即可免费搭建幻兽帕鲁服务器。阿里云服务器网al…

产品效果图为何要用渲染100农场?渲染100邀请码1a12

产品效果图很重要&#xff0c;它能帮助设计人员和消费者理解产品特点&#xff0c;是不可或缺的一步。产品效果图渲染耗时耗力&#xff0c;不仅慢而且容易出错&#xff0c;在这种情况下&#xff0c;使用渲染农场就成了必备选择&#xff0c;以目前国内最好的渲染农场渲染100为例&…

C++进阶(十三)异常

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、C语言传统的处理错误的方式二、C异常概念三、异常的使用1、异常的抛出和捕获2、异常的重新…

LabVIEW工业监控系统

LabVIEW工业监控系统 介绍了一个基于LabVIEW软件开发的工业监控系统。系统通过虚拟测控技术和先进的数据处理能力&#xff0c;实现对工业过程的高效监控&#xff0c;提升系统的自动化和智能化水平&#xff0c;从而满足现代工业对高效率、高稳定性和低成本的需求。 随着工业自…

视觉开发板—K210自学笔记(二)

视觉开发板—K210 一、开发之前的准备 工欲善其事必先利其器。各位同学先下载下面的手册&#xff1a; 1.Sipeed-Maix-Bit 资料下载&#xff1a;https://dl.sipeed.com/shareURL/MAIX/HDK/Sipeed-Maix-Bit/Maix-Bit_V2.0_with_MEMS_microphone 2.Sipeed-Maix-Bit 规格书下载&…