< Linux >:进程地址空间

news/2024/11/26 2:44:09/

目录

一、验证进程地址空间

二、感知进程地址空间的存在


一、验证进程地址空间

我们之前学的 C/C++ 程序地址空间是物理内存吗?

        答:不是物理内存,甚至叫做程序地址空间都不太准确,应该叫做进程地址空间,因此根本就不是 C/C++ 上的概念,而是操作系统上的概念、 

在 Linux 操作系统下验证进程地址空间:

在 Winodws 操作系统下无法验证进程地址空间,原因:

1、与 Windows 操作系统本身的设置有关、

2、与 Windows 操作系统所使用的编译器也有关系,编译器会对代码地址空间做调整,防止代码被恶意猜测,比如:栈随机化策略、

[LCC@hjmlcc ~]$ ls
a.out  hjm.c  process.c  test.c
[LCC@hjmlcc ~]$ ./a.out -a -b -c
Code addr           :0x40057d
Init global addr    :0x60103c
Uninit global addr  :0x601044
Heap addr           :0x812010
Stack addr          :0x7ffc3f1db3c0
Argv addr           :0x7ffc3f1db7c6
Argv addr           :0x7ffc3f1db7ce
Argv addr           :0x7ffc3f1db7d1
Argv addr           :0x7ffc3f1db7d4
Env addr            :0x7ffc3f1db7d7
Env addr            :0x7ffc3f1db7ed
Env addr            :0x7ffc3f1db7fd
Env addr            :0x7ffc3f1db808
Env addr            :0x7ffc3f1db818
Env addr            :0x7ffc3f1dbfe6
[LCC@hjmlcc ~]$ 

验证堆区和栈区的增长方向的问题:


二、感知进程地址空间的存在

        注意:使用 Linux 操作系统提供的系统调用接口 fork 创建出当前进程的子进程,若启动当前进程,那么当前进程和当前进程的子进程启动的先后顺序是不确定的,到底是谁先谁后启动,取决于 CPU 先运行了谁,通过实验可知,一般都是父进程先被启动,子进程再被启动,但并代表父进程一定会先被启动,记住即可、

#include<stdio.h>
#include<unistd.h>
int g_val=100;
int main()
{pid_t id=fork();//不考虑创建当前进程子进程失败的情况、if(id == 0){//子进程while(1){printf("我是子进程:%d,ppid:%d,g_val:%d,&g_val:%p\n",getpid(),getppid(),g_val,&g_val);sleep(1);}}else{//父进程while(1){printf("我是父进程:%d,ppid:%d,g_val:%d,&g_val:%p\n",getpid(),getppid(),g_val,&g_val);sleep(2);                                                                                                                }                                                     }                                   return 0;                                          
}    

注意:此时父子进程读取到的普通全局变量是 同一个 变量、

现做如下修改:

  1 #include<stdio.h>2 #include<unistd.h>3 //普通全局变量、4 int g_val=100;//以普通局部变量举例也是可以的、5 int main()6 {7   pid_t id=fork();//不考虑创建当前进程子进程失败的情况、8   if(id == 0)9   {10     //子进程11     int flag=0;12     while(1)13     {14       printf("我是子进程:%d,ppid:%d,g_val:%d,&g_val:%p\n",getpid(),getppid(),g_val,&g_val);15       sleep(1);16       flag++;17       if(flag==5)18       {19         g_val=200;20         printf("我是子进程,普通全局变量的值已被修改,请注意\n");21       }22     }23   }                                                                                                                            24   else{25     //父进程26     while(1)27     {28       printf("我是父进程:%d,ppid:%d,g_val:%d,&g_val:%p\n",getpid(),getppid(),g_val,&g_val);29       sleep(2);30     }31   }32   return 0;33 }

        由上图可知,父子进程读取到的普通全局变量的地址相同,但是读取到的普通全局变量的值不同,因此我们可知,此处普通全局变量的地址一定不是物理地址,不然肯定不会出现这种情况,虽然上述父子进程读取到的普通全局变量的地址相同,但父子进程各自读取到的普通全局变量并不是 同一个 变量,所以,此处的普通全局变量的地址一定是虚拟地址、

        我们之前在 C/C++ 中所谓的地址,都不是物理地址,都是虚拟地址;虚拟地址在 Linux 操作系统下又被称为:线性地址、逻辑地址;这三个概念在 Linux 操作系统下是一样的概念,但是不在Linux 操作系统下,是三个完全不一样的概念,这和 Linux 操作系统本身的空间布局有关、

        本节所讲的进程地址空间,本质上就是虚拟地址空间,因此本小结也可被称为:感知虚拟地址空间的存在、

        操作系统不让我们直接看到或访问物理内存的原因:不安全;内存(物理内存,不存在虚拟内存的概念,物理地址对应物理内存)是一个硬件,不能阻拦我们进行访问,只能被动的进行读取和写入;由野指针或越界等原因造成程序崩溃的情况并不是由内存(物理内存)造成的,而是由操作系统在程序与内存(物理内存)之间添加的软件层造成的、

        每一个进程在启动时,操作系统都会给其创建一个进程地址空间(虚拟地址空间),操作系统会对这若干个进程地址空间进行管理(先描述再组织);所谓的进程地址空间,其实本质上就是内核(操作系统)的一个数据结构,也存在于内存(物理内存)中,在 Linux 操作系统中,也是一个描述进程地址空间的 struct 结构体( struct mm_struct )、        

        所谓的进程地址空间其实就是操作系统通过软件的方式,给进程提供一个软件视角,使得每一个进程都认为其独占操作系统的所有资源(物理内存)、

        前面我们知道进程是具有独立性的,体现在相关的内核数据结构是独立的和进程的代码和数据是独立的;类比于一位海王同时撩三个女的(广撒网,钓大鱼),并对每一个女的说我只中意你,且画大饼说以后对你怎么怎么好……,从而使得每个女的都天真的认为我是不可替代的那个人,这个例子中,海王充当的就是操作系统OS,三个女的就是三个进程,海王画的大饼就是进程地址空间,海王画大饼的原因在于为了维护这三个女(三个进程)的独立性,互补干扰,若有交集则必然乱套、

        所谓的进程就是:进程等于加载到内存(物理内存)中的可执行程序(代码及数据)加上该进程对应的内核数据结构(该进程对应的描述该进程所使用的 struct 结构体(task_struct),其也存在于内存(物理内存)之中)的组合、

        在 Linux 内核中,每个进程都有 task_struct 结构体,该结构体中有个指针指向一个结构mm_struct (进程地址空间),我们假设磁盘上的一个可执行程序被加载到物理内存,之后,操作系统会给每一个进程构建一个页表结构(映射表),我们需要将虚拟地址空间(进程地址空间)和物理内存之间建立映射关系,这种映射关系是通过页表(映射表)的结构完成的,如下图:

// Linux 内核中的进程地址空间、
struct mm_struct
{struct vm_area_struct* mmap;  // list of VMAs......
}strcut vm_area_struct
{struct mm_struct* vm_mm;unsigned long vm_start;unsigned long vm_end;......
}

磁盘中的可执行程序里是有地址的,因为,在链接阶段就是把多个目标文件和链接库通过地址链接起来,从而生成可执行程序的,因此可执行程序里面是由地址的,在该可执行程序中,包括代码区,已初始化全局数据区,未初始化全局数据区等等,这些区域在划分时,采用的不是在物理内存中的地址,采用的是相对地址(相对于整个可执行程序最开始的地址),当该可执行程序被加载到物理内存中(0x00000000 - 0xFFFFFFFF)时,

而堆区和栈区是等该可执行程序被加载到物理内存中才会存在的,

也是有区域的,在 Linux 命令行中输入:readelf -S a.out 即可查看、


http://www.ppmy.cn/news/44536.html

相关文章

网络抓包分析【IP,ICMP,ARP】以及 IP数据报,MAC帧,ICMP报和ARP报的数据报格式

网络抓包分析&#xff0c;IP数据报&#xff0c;MAC帧&#xff0c;ICMP报&#xff0c;ARP报格式以及不同网络通信的过程。网络抓包工具 wireshark以太网v2MAC帧IP数据报格式ICMP报文格式ARP协议及ARP报文格式抓包分析IP数据报抓包分析icmp数据报的抓包分析ARP数据报的抓包分析网…

网站实现HTTPS的详细流程介绍

网站实现HTTPS的详细流程介绍 对于网站来说&#xff0c;采用HTTPS不仅可以提升网站安全性&#xff0c;还可以帮助优化搜索引擎排名&#xff0c;具体如何实现HTTPS呢&#xff1f;以下就详细介绍一下实现HTTPS的流程。 1、申请SSL证书 在实施HTTPS之前&#xff0c;需要先确认一…

Linux操作基础(文件系统和日志分析)

文章目录一、inode与block1.1inode和block概述1.2 inode包含文件的元信息1.3 linux文件系统的三个时间戳1.4 inode的号码1.5 inode的大小1.6 inode号的特点1.7软连接与硬链接二 、文件恢复2.1 xfsdump恢复2.2 opic恢复方式三 、日志文件3.1 日志文件的分类3.2 日志的格式3.3 常…

能聊天、会学习,远不是GPT的终局

自然语言处理&#xff08;NLP&#xff09;技术的发展和运用&#xff0c;使得计算机性能增长速度一举跃过摩尔定律瓶颈&#xff0c;将AI拱入属于它的高光时代。而象征技术融合的ChatGPT一夜爆红&#xff0c;仿佛给整个商业社会带来了一次“技术革命”。 微软、谷歌、百度、华为…

2023最全的自动化测试入门基础知识(建议收藏)

1)首先&#xff0c;什么是自动化测试&#xff1f; 自动化测试是把以人为驱动的测试行为转化为机器执行的一种过程。通常&#xff0c;在设计了测试用例并通过评审之后&#xff0c;由测试人员根据测试用例中描述的过程一步步执行测试&#xff0c;得到实际结果与期望结果的比较。…

【接口测试】从0不到1的心路历程

我是一名做了三年测试的tester&#xff0c;2020年以功能测试工程师的身份入职北京一家医疗培训公司&#xff0c;入职后为了提高测试效率&#xff0c;接触到接口测试&#xff0c;以下是从零到现在 (还有很大完善的空间&#xff0c;所以不能算是1) 的一些心路历程。 做接口测试的…

精通 TensorFlow 1.x:11~15

原文&#xff1a;Mastering TensorFlow 1.x 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 深度学习 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 不要担心自己的形象&#xff0c;只关心如何实现目标。—…

矩阵求逆_高斯消元法

高斯消元法流程 首先必须要判断矩阵是不是一个方阵&#xff0c;其方法是对于一个矩阵AnnA_{n \times n}Ann​&#xff0c;先构造一个增广矩阵W[A∣E]W[A \mid E]W[A∣E]&#xff0c;其中EEE是一个nnn \times nnn的单位矩阵&#xff0c;这样WWW就成了一个n2nn \times 2nn2n的矩…