做网站模板链接放哪里,如何建设电子商务网站,北京 网站建设|小程序|软件开发|app开发公司,好看的网站ui前言#xff1a;在讲完环境变量后#xff0c;相信大家对Linux有更进一步的认识#xff0c;而Linux进程概念到这也快接近尾声了#xff0c;现在我们了解Linux进程中的地址空间#xff01; 本篇主要内容#xff1a; 了解程序地址空间 理解进程地址空间 探究页表和虚拟地址空…前言在讲完环境变量后相信大家对Linux有更进一步的认识而Linux进程概念到这也快接近尾声了现在我们了解Linux进程中的地址空间 本篇主要内容 了解程序地址空间 理解进程地址空间 探究页表和虚拟地址空间 进程地址空间 1. 程序地址空间2. 进程地址空间3. 什么是地址空间3. 地址空间的管理4. 页表5. 为什么要存在地址空间6. 总结拓展 1. 程序地址空间
我们在学习C语言的时候大家都了解过这样的空间布局图 那么到底是不是这样排布的呢我们来验证一下 1 #includestdio.h2 #includestdlib.h3 4 int un_gval;5 int init_gval 100; 6 7 int main(int argc, char *argv[], char *env[])8 { 9 printf(code addr: %p\n, main); 10 const char *str Hello, Linux!; 11 printf(read only char addr: %p\n, str); 12 printf(init global value addr: %p\n, init_gval);13 printf(uninit global value addr: %p\n, un_gval);14 15 char *heap1(char*)malloc(100);16 char *heap2(char*)malloc(100);17 char *heap3(char*)malloc(100);18 char *heap4(char*)malloc(100);19 20 int a 100; 21 22 printf(heap1 addr: %p\n, heap1);23 printf(heap2 addr: %p\n, heap2);24 printf(heap3 addr: %p\n, heap3);25 printf(heap4 addr: %p\n, heap4); 26 27 printf(stack addr: %p\n, str); 28 printf(stack addr: %p\n, heap1); 29 printf(stack addr: %p\n, heap2); 30 printf(stack addr: %p\n, heap3);31 printf(stack addr: %p\n, heap4); 32 printf(a addr: %p\n,a); 33 return 0;34 }栈区中的数组和结构体
int num[10] ......a[0] a[9]struct s
{int a; ......s.aint b; ......s.bint c; ......s.c
}注意栈区是整体向下增长局部想上使用的就是地址最低处,依次往上放后面的元素 但是如果我们将代码更改还能运行过去嘛
char *str Hello, Linux!;
*str S;显然我们是不能更改的一更改就就运行不了了
注意其实是因为字符常量区与代码区很接近而编译器在编译时字符常量区就是被编译到代码区的代码又不可被写入所以字符常量区也不可被修改 综上 栈区是整体向下增长局部想上使用的就是地址最低处,依次往上放后面的元素常量区的字符串不允许修改 但是这都是我们之前了解的知识现在我们来重新了解地址我们先来看这段代码 1 #includestdio.h2 #includestdlib.h3 #includesys/types.h4 #includeunistd.h5 6 int g_val 200;7 8 int main()9 {10 pid_t id fork();11 if(id 0)12 {13 // 子进程14 int cnt 5;15 while(1)16 {17 printf(child, pid: %d, ppid: %d, g_val: %d, g_val: %p\n, getpid(), getppid(), g_val, g_val);18 sleep(1);19 if(cnt 0)20 {21 g_val 100;22 printf(child change g_val: 200 - 100\n);23 }24 cnt--;25 }26 27 }28 else{29 // 父进程30 while(1)31 {32 printf(father, pid: %d, ppid: %d, g_val: %d, g_val: %p\n, getpid(), getppid(), g_val, g_val);33 sleep(1); 34 }35 }36 return 0;37 } 我们发现在开始时输出出来的变量值和地址是一模一样的 因为我们之前讲过子进程按照父进程为模版父子并没有对变量进行进行任何修改
但是在达到一定条件之后父子进程输出地址是一致的但是变量内容不一样 但是相同的地址为什么会有不同的值? 所以我们能得出结论我们之前看到的地址绝对不是物理地址我们平时用到的地址其实都是虚拟地址/线性地址而虚拟地址就是进程地址空间的内容 2. 进程地址空间
我们现在来深入的了解一下为什么相同的的地址为什么会有不同的值
首先引入一个概念每一个进程运行之后都会有一个进程地址空间的存在在系统层面都要有自己的页表映射结构
因此当一个进程先修改后它就不再指向原来那块物理空间而是拥有一个新的物理空间而页表左边的虚拟空间没有发生改变所以相同的的地址为什么会有不同的值是因为映射的物理空间不同 3. 什么是地址空间
在讲什么是地址空间之前我们先来讲一个故事来方便理解 一个拥有10亿美元身家的富豪他有4个私生子每个人都不知道彼此的存在但是富豪对每个孩子都说认真做好现在的事在未来可以继承自己的10个亿家产。 但是在得到10个亿之前他的几个孩子在经济上遇到了问题前三个都要找富豪要10w美金来解决麻烦富豪觉得合情合理也就给了但是它的第四个孩子直接找他要10个亿富豪当然不能给他然后讲明原因后给了他20w美金。因此他的所有孩子都可以得到10亿之内的经济资助但是绝对拿不到10个亿。 在这个故事中 操作系统富豪内存10亿美金进程私生子虚拟地址空间继承10亿的大饼 虚拟地址空间并不是真实的地址 3. 地址空间的管理
富豪给每一个私生子都画了饼他要把每个私生子都管理起来也就是要把所有大饼管理起来。 因此地址空间也要被OS管理起来!!每一个进程都要有地址空间系统中一定要对地址空间做管理!! 而操作系统管理地址空间一定是“先描述在组织”地址空间最终一定是一个内核的数据结构对象 就是一个内核结构体 而我们观察进程地址空间发现里面是一堆的地址划分。 在Linux中这个描述虚拟地址空间的东西叫做:
struct mm _struct
{long code_start;long code_end;long data_start;long data_end;long heap_start;long heap end; //brklong stack _start;long stack _end;......
}而该结构体的大小会被初始化成4gb线性编程范围从全0到全F然后把线性范围拆分成细小的范围这就是地址空间 4. 页表
在上面我们了解到了页表页表的映射关系中左侧表示虚拟地址右侧表示物理地址但是除了这两个其实在页表的映射关系中还存在一个标记字段——访问权限字段 讲到这里我们再回到字符常量区那里。
char *str Hello, Linux!;
*str S;此时我们就可以解释通字符常量区为什么不能修改 字符常量区在经过页表映射时访问权限字段只设置成只读的所以在写入时页表直接将我们拦住不让我们访问所以字符常量区不能修改代码区也是如此 所以页表可以进行安全评估有效的进行进程访问内存的安全检查 在除去上面提到的东西以外页表还可以通过二进制衡量能存中有没有内容是否分配地址 当我们有个虚拟地址要被访问了但是它并没有被分配空间更不会有内容那该则么办呢 其实在这个时候操作系统会将你的这个访问暂停然后进行一下操作 操作系统会将你的可执行程序重新开辟空间把对应可执行程序需要执行的这个虚拟地址对应的代码加载到内存里把对应的虚拟地址填充到页表把标志位改为1代表已经分配地址且内容已经填充将暂停的代码继续访问 操作过程也称为缺页中断
而我们操作系统在进行这些工作时是在进行内存管理 而进程管理和内存管理因为有了地址空间的存在 实现了在操作系统层面上的模块的解耦 5. 为什么要存在地址空间
到了这里我想大家也都了解得差不多了为什么要存在地址空间原因有很多
一、 让无序便有序 让进程以统一的视角看待内存在页表层映射时会将不同的数据类型进行划分使得映射到物理内存后是比较有序的一种状态!所以任意一个进程可以通过地址空间页表可以将乱序的内存数据变成有序分门别类的规划好! 二、存在虚拟地址空间可以有效的进行进程访问内存的安全检查
三、将进程管理和内存管理进行解耦
四、保证进程的独立性 通过页表让进程虽然虚拟地址一样但是映射到不同的物理内存处从而实现进程的独立性 6. 总结拓展
拓展 在mm_struct中还会存在一个struct vm_area_struct的结构 它能划分出一个start一个end。如果我们还想继续划分就会有多个struct vm_area_struct的结构然后他们会构成一个线性划分的链表结构。
struct vm_area_struct
{struct mm_struct * vm_mm;unsigned long vm_start;unsigned long vm_end;......
}到这里我们的进程地址空间也接近尾声了地址空间让进程管理和内存管理互不干涉起到了很大作用。结束进程地址空间我们的Linux进程概念到这里也结束了后面我将带大家走进进程控制。
谢谢大家支持本篇到这里就结束了