买过域名之前就可以做网站了吗,网页制作基础知识答案,政工网站建设方案,网站建设一百万文章目录 程序地址空间虚拟地址和物理地址地址的转换地址空间是什么#xff1f; 程序地址空间
在C和C程序中#xff0c;一直有一个观点是#xff0c;程序中的各个变量等都会有一定的地址空间#xff0c;因此才会有诸如取地址#xff0c;通过地址访问等操作#xff0c;那… 文章目录 程序地址空间虚拟地址和物理地址地址的转换地址空间是什么 程序地址空间
在C和C程序中一直有一个观点是程序中的各个变量等都会有一定的地址空间因此才会有诸如取地址通过地址访问等操作那么在前面的学习中基本有下面的概念 这是学C语言的时候就已经知晓的内容那么现在抛出下面的几个疑问这些数据和所谓的地址是内存中的地址吗内存中的地址存储排列形式如此整齐吗不会造成内存浪费吗
下面做一个小实验
#include stdio.h
#include unistd.hint g_val 100;
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_val200;printf(child change g_val: 100-200\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;
}实验结果如下
child, Pid: 6781, Ppid: 6780, g_val: 100, g_val0x60105c
father, Pid: 6780, Ppid: 30413, g_val: 100, g_val0x60105c
child change g_val: 100-200
child, Pid: 6781, Ppid: 6780, g_val: 200, g_val0x60105c
father, Pid: 6780, Ppid: 30413, g_val: 100, g_val0x60105c这是一个很神奇的现象父进程和子进程的g_val选项的地址是一样的但是读取出来的值却不一样这是为什么呢
说明这里的地址并不是物理地址而是虚拟地址也叫做线性地址
虚拟地址和物理地址
下面就来研究的是虚拟地址和物理地址之间是如何进行转换的
地址的转换
由前面的内容知道进程是由进程的代码和数据以及内核数据结构组成的那么当一个进程生成的时候会创建其对应的PCB用来管理进程中的数据而在进程中的数据会根据具体的类型而放入不同的地址空间中例如栈区堆区代码区等等…
而实际上这个区域只是一个虚拟的地址由于一些原因(后续补充)存在一个叫做页表的映射关系将虚拟地址和物理地址进行一一映射具体的表现如下所示 上图即展示了页表的映射关系的具体含义对于前面图片中的内容只是一个虚拟地址打印出来的信息也并非实际的物理地址而真正的物理地址则是通过页表进行一个一一映射的关系通过这个一一映射就能够找到物理地址这个物理地址才是真正存储信息的地方
那对于子进程来说是如何解释的
由前面的理论基础可以得出这样的一个结论当使用fork创建子进程的时候为子进程创建自己的PCB对于代码和数据如果发生了变化就使用写时拷贝完成一份拷贝这样可以保证进程的互不干扰独立性因此对于上面的场景当对于创建子进程的时候本质上就是直接复制了一份上面图片中的内容并将这个task_struct变成子进程
下图中所示的页表是一部分实际上的页表还有其他的组成部分 而当子进程或者父进程要发生数据改变的时候就会发生写时拷贝具体的产生过程如下 这样就解释清楚了写时拷贝的含义写时拷贝是发生在物理内存中的拷贝过程整个过程是由操作系统来完成的保证了进程之间的独立性
地址空间是什么
简单来说地址空间就是它 每一个进程都会有一个这样的地址空间而对于地址空间是需要进行管理的那么如何对地址空间进行管理答案是先描述再组织因此如何对地址空间进行描述
地址空间最终一定是一个内核的数据结构对象简单来说就是一个内核的结构体正如task_struct一样在Linux内核中有一个名字叫做mm_struct而这个数据是如何进行管理的答案很明显也是在内核数据结构中进行的管理
在Linux内核源码中来查看这个结构体的存在性 转到关于它的定义观看它内部的定义实现方式 对于mm_struct来说它通过定义了各个区域的起止位置来进行管理数据
为什么要有地址空间呢
先说结论
让进程以统一的视角看待内存任意一个进程都可以通过地址空间和页表将杂乱无序的内存数据变成有序的空间也就是说这是一个变无序为有序的过程存在虚拟地址空间可以有效的进行进程访问内存的安全检查将进程管理和内存管理进行耦合通过页表可以让进程映射到不同的物理内存中从而实现进程的独立性
下面对于上面的结论进行一一的解释
1. 变无序为有序的过程
这个过程是很好理解的由于页表的存在因此具体的实际内存中的数据不必排放到一块而是可以进行不同位置的存储但是在管理的角度来看通过虚拟地址来进行管理是相当方便的每一个地方都被分门别类的具体一一列举了出来这样不仅便于管理同时也可以最大化的利用内存中的空间
2. 访问内存的安全检查
讲到这点就必须对页表进行进一步的补充说明了实际上页表中存储的不仅仅有虚拟地址和物理地址还有其他很多的信息例如这里的访问权限字段 那么首先是解释访问权限字段存在的意义可以有效避免进行修改保护进程的数据等功能例如下面的这个具体事例
#include stdio.h
//#include unistd.hint main()
{char* str hello linux;*str H;return 0;
}在gcc的编译器下这是可以通过编译的原因是这里的一个常量字符串的起始地址交给了一个字符指针str而对于str来说将它的指向内容改成H这个本身是可以的但是问题出现在str指向的内容实际上是一个字符常量区而这个区域内的数据是不可以被修改的因此如果要进行修改的话是不被允许的那么页表是如何进行保护的呢
在执行程序的时候会引发段错误这就是页表的功劳当使用虚拟地址进行映射到物理地址的过程中在进行页表的权限访问字段的时候会发现这个字段的访问权限是只读权限但是现在要进行写入很明显这是不被允许的行为因此就会终止这种行为因此页表中的权限访问字段就有这样的功能可以进行访问内存的安全检查
3. 将进程管理和内存管理进行耦合
在解释这个结论前还需要补充一下页表的内容页表中还存在一列它的意义是查看是否被分配和是否有内容 在实际中是采用一个0和1来表示是否有没有被分配和内容的在实际进程管理控制过程中虚拟地址首先会放到页表中而当需要和内存地址进行交互的时候就会通过这个分配和内容的内容表来进行判断到底内存有没有分配具体的物理地址给这部分内容如果没有就会进行分配等信息也是方便于进程的管理
这样做就把进程管理和内存管理这两个模块的耦合度大大降低两个模块控制系统互不干扰这样就实现了进程的独立性