网站定制化开发介绍,引用网站信息怎么做备注,dnf制裁做任务网站,前端项目Linux程序的地址空间 #x1f4df;作者主页#xff1a;慢热的陕西人 #x1f334;专栏链接#xff1a;Linux #x1f4e3;欢迎各位大佬#x1f44d;点赞#x1f525;关注#x1f693;收藏#xff0c;#x1f349;留言 本博客主要内容深刻理解了什么程序或者进程的地址…Linux程序的地址空间 作者主页慢热的陕西人 专栏链接Linux 欢迎各位大佬点赞关注收藏留言 本博客主要内容深刻理解了什么程序或者进程的地址空间以及它存在的意义和操作系统内部是如何实现进程地址空间的 文章目录 Linux程序的地址空间1.一个测试代码运行的结果1.1可以得到的结论 2.引入地址空间2.1小故事2.2 代码区数据区堆区等等这些区域该如何理解 3. 地址空间是什么为什么怎么办3.1为什么要有地址空间3.2 malloc的本质3.3 重新理解地址空间 1.一个测试代码 我们先运行一个测试代码 #includestdio.h
#includeunistd.h
#includeassert.hint g_val 100; //全局变量int main()
{pid_t id fork();assert(id 0);if(id 0)
{//childwhile(1){//此处反斜杠是为了让代码可以换行printf(我是子进程我的PID是:%d,我的PPID是:%d,g_val:%d, g_val:%p\n,\getpid(), getppid(), g_val, g_val); sleep(1);g_val;}
}
else
{while(1){//父进程printf(我是父进程我的PID是:%d,我的PPID是:%d,g_val:%d, g_val:%p\n,\getpid(), getppid(), g_val, g_val);sleep(1);}
}
return 0;
} 运行的结果 1.1可以得到的结论
我们发现竟然指向相同地址的变量却出现了不同值的情况这其实是因为发生了写时拷贝并且我们佐证了子进程可以对全局变量进行修改并且不影响父进程全局变量的值从而证明了进程具有独立性。
同时我们也可以看出这里的地址不是物理地址因为假设是物理地址我们不可能在同一块物理地址上取到不同的值所以我们认为这是一个虚拟地址or线性地址。
2.引入地址空间
2.1小故事 我们用一个大富翁和私生子的故事来引入地址空间的概念 大富翁有十亿美金但是他有四个私生子(A , B, C , D); A做生意的 | B卖化妆品 | C哈弗读书 | D高中辍学的混混 并且ABCD并不知道彼此的存在并且他们都坚信自己最终能够继承大富翁的遗产 某一天大富翁对着A说你好好做你的生意将来等我去世的时候我的十亿家产全都是你的。 对B说你的化妆品卖的挺好你听老爹的话等老爹到时候西去的时候我的家产都是你的。 同样对C和D都是一样的画饼。 所以现在ABCD每个人脑子里都有一张饼就是老爹的十亿。 … 有一天A来找到大富翁告诉他说自己需要五万美金做生意需要买一块手表会见自己和合作伙伴但是自己的生意目前周转不开大富翁一想才五万美金大手一挥直接给A了。 C联系大富翁并给他说自己在学校念书没有生活费了想问老爹要1000美金的生活费。老爹一听小钱大手一挥又给了。以上两个是进程向操作系统申请较小的内存空间操作系统当然允许 有一天D找到大富翁说:我在社会上遇到一点事需要一些钱摆平一下你给我5000万美金吧。老爹一听说一边去我这么多钱也不能这么花。D骂骂咧咧的走了。(进程向操作系统申请很大的内存空间操作系统拒绝了) …这是ABCD也都会认为自己最终如果想要那十亿美金**(饼)**他们都还是会获得的。 但其实如果ABCD每个人向大富翁索要几千几万大富翁都会给予。但是如果向大富翁索要很多几千万几亿的情况下大富翁是完全可以直接拒绝的。 这里我们引入第一个概念 进程的地址空间就是大富翁给每个私生子画的饼。 那么最后揭晓十亿美金相当于内存大富翁相当于操作系统 那么我们想想大富翁有没有必要将自己的“饼”管理起来呢 答案是有必要的先描述在组织因为如果大富翁的儿子多了并且他画的饼多了之后他也会忘记的。 2.2 代码区数据区堆区等等这些区域该如何理解 我们用一个小故事引入 时间来到小学时代有一对同桌小花和小胖两个人**共用一张大桌子。(内存空间)**然而小胖不爱干净男孩子很爱运动所以身上老有一股汗味。小花就不满意了二话不说先把小胖揍了一顿告诉他:回家之后好好洗一洗回去告诉你爸爸妈妈我们上幼儿园要讲卫生。第二点你从今天开始不能再欺负我了要不然我再揍你一顿。 小花告诉小胖我们以中间这根线为界谁都不许越界不能把你的东西放到我这边来也不能把你的胳膊放到我这边来。 小花画这条线的本质叫做区域划分如下这样 struct area
{int start;int end;
}
struct area xiaohua_area {1, 50};
struct area xiaopang_area {50, 100};地址空间本质就是线性结构的 不管是32位还是64位机器我们并不需要记录地址空间是多少GB的原因是不管是多少位的机器在操作系统刚刚开始加载的时候就确定好了。 那么有了之前的铺垫我们就可以地址空间是怎么在mm_struct中划分的; struct mm_struct ---- 4GB
{long code_start; //代码区的起始long code_end; //代码区的结束long init_start;long init_end;....long brk_start; //堆区的起始long brk_end; //堆区的结束long stack_start; //栈区的起始long stack_end; //栈区的结束
}那么如果限定了区域那么区域之间的数据是什么 例如[1000,2000],1000和2000被称作我们的地址那么他们之间的区域就被称作虚拟地址or线性地址 那么我们故事在继续… 小花和小胖画好了三八线之后一段时间两个人都很受规矩但是小胖却很调皮小胖总是将自己的胳膊放到三八线左侧或者将自己的书包和都扔到小花的桌子上这强烈的引起了小花的不满小花就将小胖揍了一顿然后更改了三八线的位置… 那么对于小花来说叫做区域扩大。对于小胖来说叫做区域缩小 体现在mm_struct中呢就是 xiaohua_area.end 75;
xiaopang_area.start 75;那么对应到我们的地址空间上对于我们的栈区堆区等的区域扩大和缩小也是一样的都是更改我们的start和end. 3. 地址空间是什么为什么怎么办 数据和代码真正是存储在内存中。 是什么 虚拟地址空间是一种在操作系统内部为进程创建出来的具体的数据结构对象让进程以同意的视角来查看内存。 虚拟地址会经过也变转化为物理地址我们平时使用的地址都是虚拟地址当我们有了虚拟地址我们就可以通过页表找到物理地址中对应的位置从而将物理地址中存储的内容放到我们的CPU中。 接下来我们用一幅图来解释最开始的父进程和子进程查看相同地址的内存却看到不同值的问题。 首先操作系统为每一个进程维护一张页表用来存储虚拟地址和物理地址的映射关系。 最开始的时候父子进程的虚拟地址都是指向最开始的g_val的位置但是当子进程尝试修改g_val的时候这时候操作系统会在物理地址的另一个位置拷贝一份g_val的值然后修改并且将这个物理地址存储到子进程页表刚刚指向原物理地址的位置。 并且这里也是体现了进程的独立性。 同时我们这里也可以解释最开始的之前的代码fork()函数貌似有两个返回值的问题 首先fork在返回的时候父子进程都有了return两次id是不是也是pid_t类型定义的变量呢那么返回的本质就是写入父子进程谁先写入就发生了写时拷贝。造成了一个变量有两个值的情况。 #includestdio.h
#includeunistd.h
#includesys/types.h
int main()
{ pid_t ret fork();if(ret 0){ //子进程printf(我是子进程我的pid是%d我的父进程是%d,getpid(),getppid()); sleep(1); }else if(ret 0){ //父进程while(1){ printf(我是父进程我的pid是%d我的父进程是%dgetpid(),getppid());sleep(2); } } return 0;
} 3.1为什么要有地址空间 如果没有地址空间我们的进程的pcb只能直接指向物理内存中的该进程相关的数据和代码。 这样就会产生很多不安全的问题以及效率问题。 ①防止地址随意访问保护物理内存和其他进程 ②将进程管理和内存管理解耦合 ③可以让进程以统一的视角看待自己的代码和数据 3.2 malloc的本质 当我们向操作系统申请内存操作系统立马给你还是需要的时候再给你呢 那么毫无疑问肯定是我们需要的时候才会给我们。 ①操作一同不会允许任何的浪费和不高效 ②申请内存 ! 立马使用 ③在你申请成功之后和你使用之前有很小一段的时间窗口----这个空间没有被正常使用但是别人也用不了—闲置状态。 那么malloc的时候申请空间我们操作系统只会再地址空间上对应的堆区进行申请空间那么这里就是我们之前提到的操作系统会为我们的堆区进行区域扩大也就是去操作我们的mm_struct中的brk_end和brk_start,然后去页表中存储但是不会再物理地址上去帮我们申请。相当于做了一半的映射这时候直到当我们的进程需要用到这片地址的时候操作系统才会帮我们去完成页表和物理地址的映射。 当操作系统检测到我们需要内存的时候才给我们申请或者检测到我们的代码和数据不在内存当我们需要的时候给我们换入这样的操作叫做缺页中断。 总结一句就是malloc在申请空间的时候我们的操作系统不会立马分配空间而是在我们需要使用的时候才给我们分配这就是malloc的本质。 并且正是因为有了地址空间的存在我们的进程也不关心它的数据和代码存储在物理地址的任意位置。 **进程管理**操作系统管理进程和地址空间的过程叫做进程管理。 内存管理: 操作系统管理页表和物理内存之间的映射的过程叫做内存管理。 并且操作系统将进程和物理内存之间分成进程管理和内存管理的过程叫做解耦合。 3.3 重新理解地址空间 首先我们想一想我们的程序在被编译的时候没有被加载到内存请问我们的程序内部有没有地址呢 答案肯定是有的源代码被编译的时候就是按照虚拟地址空间的方式进行对代码和数据早就已经编好了对应的编址。 我们不能认为虚拟地址这样的策略只会影响操作系统还要让我们的编译器也要遵守这样的规则。(Linux系统中的可执行文件的格式是ELF格式) 进程的代码和数据需要一直在内存中吗 答案是不一定的就算是我们要运行一个进程我们操作系统也不会一次将这个进程所对应的代码和数据加载到内存中。 操作系统一般是边加载边执行这也是地址空间的意义。 到这本篇博客的内容就到此结束了。 如果觉得本篇博客内容对你有所帮助的话可以点赞收藏顺便关注一下 如果文章内容有错误欢迎在评论区指正