重新了解地址空间
在学习c/c++语言的时候,大家一定见过以下这张图
说的是程序会加载在如图的结构上,实际上,我们真的对他很了解吗,而在Linux进程控制这,就会有一个奇怪的现象
前提提要:简要介绍一下fork函数
进程=内核数据结构(PCB)+自己的代码以及数据
在Linux中,fork可以从当前进程创建一个新进程,创建的新进程为子进程,而当前当前进程就是子进程的父进程,而子进程会以父进程为模板,创建子进程
头文件在#include <unistd.h>
pid_t fork(void);
返回值:自进程中返回0,父进程返回子进程id,出错返回-1进程调用fork,当控制转移到内核中的fork代码后,内核做:
1.分配新的内存块和内核数据结构给子进程
2.将父进程部分数据结构内容拷贝至子进程
3.添加子进程到系统进程列表当中4.fork返回,开始调度器调度
这里先不深入讲解fork函数,主要是为接下来的行为做铺垫
请看以下代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;//全局变量int main()
{pid_t id = fork();//创建进程if(id < 0){//创建进程错误perror("fork");return 0;}else if(id == 0){ //子进程返回码为0 这里就是子进程printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);//getpid返回当前进程的pid}else{ //父进程printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);}sleep(1);return 0;
}
运行结果也如我们所料
parent[19376]: 0 : 0x601050
child[19377]: 0 : 0x601050
子进程和父进程的g_val的地址都是相同的,很正常.
奇怪的事情
如果我们对以上代码的两个进程的其中一个进行修改,会发生什么呢,大家不妨大胆猜测一下
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;//全局变量int main()
{pid_t id = fork();//创建进程if(id < 0){//创建进程错误perror("fork");return 0;}else if(id == 0){ //子进程返回码为0 这里就是子进程g_val+=10;//我们在子进程这修改g_val的值printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);//getpid返回当前进程的pid}else{ //父进程printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);}sleep(1);return 0;
}
在这里 我们修改了全局变量g_val的值,嗯,因为是全局的,g_val地址对应的值只有一个,所以父子进程都应该打印g_val=10 对吧? 我们来看看实际的结果
parent[19907]: 0 : 0x601050
child[19908]: 10 : 0x601050
欸 不是哥们,啊? 你这俩进程全局变量的对应地址不都是一样的吗,咋打印出的值还不一样啊,难道技术创新了,一个地址可以对应两个值? 当然不是的,接下来,我们就来讲讲为什么会出现这种奇怪的现象
物理地址所对应的值有且只能有一个,所以我们可以退出以下结论
1.变量内容不一样,所以父子进程输出的变量绝对不是同一个变量
2.但地址值是一样的,说明,该地址绝对不是物理地址!
3.在Linux地址下,这种地址叫做 虚拟地址
4.我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由操作系统OS统一管理
那么------------> 也就是说 操作系统必须负责将 虚拟地址 转化成 物理地址,
进程地址空间
所以之前说‘程序的地址空间’是不准确的,准确的应该说成进程地址空间,那该如何理解呢?
那么操作系统是如何将进程虚拟地址转换成物理地址的呢?
页表&虚拟进程地址空间
操作系统其实是利用一种页表的操作将进程虚拟地址空间映射到物理地址空间,而这个页表去映射的过程,其实就有点类似于哈希的映射的关系
上面的图就足矣说明问题,同一个变量,地址相同,其实是虚拟地址相同,内容不同其实是被页表映射到了不同的物理地址!!!
而操作系统为什么要这么做呢
这样做有两个好处
1.将物理内存从无序转换成有序,让进程以统一的视角,看待内存
2.将进程管理和内存管理进行耦合
地址空间+页表也就是保护内存安全的一个重要手段