潍坊装饰网站建设,自己的网站怎样做优化,江西建网站做优化,商标设计大全文章目录 背景进程地址空间分页和虚拟地址空间 写时拷贝 背景
研究背景#xff1a;我们在之前通过fork函数创建子进程的时候#xff0c;我们发现fork的返回值有两个#xff0c;且值不相同但地址确实相同的#xff0c;我们知道在物理空间上这种情况是不可能存在的#xff… 文章目录 背景进程地址空间分页和虚拟地址空间 写时拷贝 背景
研究背景我们在之前通过fork函数创建子进程的时候我们发现fork的返回值有两个且值不相同但地址确实相同的我们知道在物理空间上这种情况是不可能存在的同一个地址的变量怎么会有两个值呢 把原来的代码再拿来感受一下 上述代码返回值有两个因为分别为父子进程的返回值且父子进程共用同一份代码和数据这个我们在之前就了解过了没什么问题再来看下面的代码稍加改动 结果输出不同因为我们这里把子进程的g_val改为了100且在修改的过程中发生了写实拷贝新开辟了一块空间给子进程存储了这个数据这个我们似乎也可以理解那么又要怎么解释他们的地址相同呢通过上述例子我们可以得出以下结论 这个地址一定不是真正的地址物理地址在Linux下这种地址叫做虚拟地址我们在C/C语言中所看到的地址也全部都是虚拟地址物理地址用户一概看不到全部都由OS统一进行管理。 因此OS必须负责把虚拟地址转换为物理地址
进程地址空间
程序地址空间其实是一种不准确的表达准确的应该说成进程地址空间那么该如何理解 此时可能你还会有些疑问为什么要有虚拟地址直接使用物理地址不是更简单吗 我们在语言层面上经常会听到静态区常量区栈区堆区等概念现在我们先来对地址空间进行一个划分。
#includestdio.h
#includestdlib.h
int g_unval;//未初始化
int g_val 100;//初始化
int main(int argc,char *argv[],char *env[])
{printf(code addr: %p\n,main);//代码区起始地址const char* p1 hello world;//p1是指针变量(栈区)p指向字符常量h(字符常量区)printf(read only : %p\n,p1);printf(global val: %p\n,g_val);printf(global uninit val: %p\n,g_unval);char *p2 (char *)malloc(10);printf(heap addr: %p\n, p2);char *p3 (char *)malloc(10);printf(heap addr: %p\n, p3);printf(stack addr: %p\n,p1);//p1先定义先入栈printf(stack addr: %p\n,p2);printf(args addr %p\n,argv[0]);//命令行参数printf(args addr %p\n,argv[argc-1]);printf(env addr: %p\n,env[0]);//环境变量return 0;
} main函数开始地址空间由低向高增长且栈区地址由高向低增长堆区地址由低向高增长
1.进程地址空间不是内存 2.进程地址空间会在进程的整个生命周期内一直存在直到进程退出
分页和虚拟地址空间 我们来解释一下为什么需要虚拟地址空间为什么不能让进程直接访问物理内存呢 保护物理内存不受任何进程内地址的直接访问在虚拟地址到物理地址的转化过程中方便进行合法性检验。 比如野指针导致物理内存中的数据被修改即使操作系统让该段内存是可读的也会有风险比如密码可能会被别人读取到。 那么虚拟地址空间是如何解决进程直接访问物理内存可能会出现的问题呢 每一个进程都有它对应的task_struct地址空间页表页表中有虚拟地址和物理内存的映射关系有了页表的存在就有了虚拟地址到物理地址的对应转化关系这个转化过程由操作系统完成同时也可以帮助系统进行合法性检测。 我们在写代码时经常会出现指针越界那么指针越界就一定会出现错误吗 不一定 1.越界可能它还是在自己的合法区域比如他本来指向的就是栈区越界后他依然指向栈区编译器的检查机制就会认为它是合法的当你本来是指向数据区结果指针后来指向了字符常量区编译器就会根据mm_struct里面的structend区间来判断你有没有越界此时发现你越界了就会报错 2.页表也有一种权限管理当你对数据区进行映射时数据区是可以读写的相应的在页表映射关系就是可读可写的但是当你对代码区和字符常量区进行映射时因为这两个区域只是可读的相应的在也表中的映射关系中的权限就是可读的如果你对这个区域进行了写通过页表中的权限管理操作系统就会直接将这个进程杀掉。 所以进程地址空间的存在也使得可以通过start和end以及页表的管理权限来判断指针是否具有合法性 虚拟地址空间的存在使得所有的进程以统一的视角去看待内存然后OS会把虚拟地址空间中的虚拟地址映射到真实的物理地址上去 因为地址空间和页表的存在将进程管理模块和内存管理模块进行解耦合
写时拷贝
通常父子代码共享父子再不写入时数据也是共享的当任意一方试图写入便以写时拷贝的方式各自一份副本 子进程继承于父进程数据段只是可读的当有一方想要写入时OS会通过一系列的管理机制让它进行写实拷贝在页表中重新去映射对应的关系从而做到而不去修改原来的数据此处还有一些疑问OS是怎么知道哪些数据是可以进行写实拷贝的呢这个问题比较复杂以后再来说