招聘网站开发文档,旺道网站优化,wordpress图片缝隙,外贸公司没网站 怎么做业务标题#xff1a;[Linux] Linux初识进程地址空间 个人主页水墨不写bug #xff08;图片来源于AI#xff09; 目录
一、什么是进程地址空间
二、为什么父子进程相同地址的变量的值不同
三、初识虚拟地址、页表 一、什么是进程地址空间 其实#xff0c;在很久之前#xf… 标题[Linux] Linux初识进程地址空间 个人主页水墨不写bug 图片来源于AI 目录
一、什么是进程地址空间
二、为什么父子进程相同地址的变量的值不同
三、初识虚拟地址、页表 一、什么是进程地址空间 其实在很久之前也就是去年的时候我在《C语言动态内存规划》可转跳的这篇文章一文中就提到了内存分布不同的变量类型会存储在不同的内存区域。 但是这个内存真的是真正的内存吗一个进程都有一个这样的内存区域吗这样的内存区域是如何组织的这是本文想要回答的问题。 我们在动态内存规划中讲解过C/C语言的不同变量存在于不同的区域 但是实际上我们对这个图片并没有非常只管的认识我们只知道不同数据类型存储在不同的区域我们甚至不知道这个整体的结构到底存储在哪里接下来一切都要从一个现象说起。 我们在Linux下可以使用系统调用fork创建一个子进程 子进程的会继承父进程的代码和数据所以我们可以通过一个if条件判断把父进程和子进程将要执行的代码分开
#include stdio.h
#include unistd.h
#include stdlib.hint g_val 0;int main()
{pid_t id fork();if(id 0)//返回值小于0表示创建子进程失败打印错误信息后返回{perror(fork);return 1;}else if(id 0)//child子进程只能执行这里的代码{ printf(child[%d]: %d : %p\n, getpid(), g_val, g_val);}else//parent父进程只能执行这里的代码{ printf(parent[%d]: %d : %p\n, getpid(), g_val, g_val);}sleep(100);return 0;
} 上述的代码就通过if判断把子进程和父进程将要执行的代码分离开这样就能单独编写父子进程需要执行的任务了。 我们运行上面的代码得到结果 我们发现父子进程的全局变量g_val的值是相同的地址也是相同的这符合我们的预期因为子进程会按照父进程为模板父子都没有对这个变量做出任何修改。 但是如果我们把这个代码稍微修改一下在子进程中故意修改全局的g_val变量
#include stdio.h
#include unistd.h
#include stdlib.hint g_val 0;int main()
{pid_t id fork();if(id 0){perror(fork);return 0;}else if(id 0)//child,子进程肯定先跑完也就是子进程先修改完成之后父进程再读取{ g_val100;printf(child[%d]: %d : %p\n, getpid(), g_val, g_val);}else//parent{ sleep(3);printf(parent[%d]: %d : %p\n, getpid(), g_val, g_val);}sleep(100);return 0;
} 这个时候再运行程序得到结果 这个结果发生了在语言层面上根本无法回答的问题 同一个地址处的变量的值不相同 二、为什么父子进程相同地址的变量的值不同 其实我们以前在C/C中打印出来并看到的地址全都是虚拟地址并不是真正的地址。 我们可以推测 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量。但地址值是一样的说明该地址绝对不是物理地址 我们其实访问的都是虚拟地址对于物理地址用户一律是看不到的是由操作系统统一管理的。于是操作系统负责讲把虚拟地址转化为物理地址。 具体如何操作呢这就需要结合进程地址空间同时需要引入一个新的概念页表。 三、初识虚拟地址、页表 页表浅层次的理解就是一个能把虚拟地址映射到物理地址的表。 操作系统通过PCB也就是Linux的task_struct来组织管理进程进程地址空间本质是PCB内部的一个结构体简单一点理解就是PCB内部含有维护本进程虚拟地址空间的信息 在进程正常运行的情况下PCB是链接在CPU的运行队列当中的 这样我们就对进程地址空间有了全新的直观的认识了。CPU会根据《Linux的O1调度算法来调度进程》可转跳的这篇文章这是之前的故事了这里不再赘述。 言归正传页表是一个把虚拟地址映射到物理地址的结构 我们创建的全局变量g_val就存储在数据段g_val自然会通过页表映射到一段物理地址 当我们fork创建子进程子进程会有和父进程一样的代码和数据 但是由于进程间具有独立性所以注定子进程改变全局变量g_val不能影响到父进程这就好比你写的代码遇到空指针挂了你的VS不会跟着挂一样。 于是解决这一问题的方法写时拷贝就会发生。 当子进程想要该表父进程的g_val时会发生写时拷贝在物理地址空间上重新开辟一块空间复制拷贝一份g_val专门给子进程使用这在保证进程的独立性的同时也在最大程度上节约了空间资源。 到这里你也许就会明白为什么相同的地址的值不同了因为父子进程页表的内容是相同的。不同的是页表的映射关系不同了。 完~
未经作者同意禁止转载