赞
踩
进程地址空间是用来描述操作系统中的进程所占的空间,因为进程的独立性,所以通过让每个进程都看到完整的地址空间。其本质是虚拟地址空间,通过虚拟地址与物理地址的映射来分配空间。
这段空间中自下而上,地址是增长的,栈是向地址减小方向增长(栈是先使用高地址),而堆是向地址增长方向增长(堆是先使用低地址),堆栈之间的共享区,主要用来加载动态库。
1、进程地址空间不是内存
2、进程地址空间,会在进程的整个生命周期内一直存在,直到进程退出
这也就解释了全局变量为什么会一直存在,原因是未初始化数据,初始化数据,这些区域是一直存在的,不同于栈区需要维护栈帧,堆区需要向系统申请,静态区和常量区的变量,常量和函数定义好了以后就直接存储在相应区域。
进程地址空间究竟是什么?
我们通过一个代码来看一个现象,我们定义了一个全局变量,fork创建一个子进程,让父进程和子进程完成自己的任务,在子进程中定义count来计数,当子进程的打印任务进行到第五次时,让子进程将这个全局变量改成100:
- #include<stdio.h>
- #include<unistd.h>
- int g_val = 0;
- int main()
- {
- printf("begin.....%d\n",g_val);
- pid_t id = fork();
- if(id==0)
- {
- //child
- int count = 0;
- while(1)
- {
- printf("child: pid: %d,ppid: %d, g_val:%d, &g_val: %p\n",getpid(),getppid(),g_val,&g_val);
- sleep(1);
- count++;
- if(count == 5)
- {
- g_val = 100;
- }
-
- }
-
- }
- else if(id>0)
- {
- //father
- while(1)
- {
- printf("father: pod: %d,ppid: %d, g_val:%d, &g_val: %p\n",getpid(),getppid(),g_val,&g_val);
- sleep(1);
- }
-
- }
- else
- {
- //todo
- }
- return 0;
- }
代码共享,所以看到前五次打印的g_val的地址都是一样的,这我们不意外,等到了第六次时,我们发现父进程g_val依然是0,子进程的g_val变成了100,因为我们将它改了,这也不意外,因为前面说了,父子进程之间代码共享,而数据是各自私有一份的(写时拷贝),但是令人奇怪的是地址竟然是一样的!
如果是物理地址绝对不可能出现这种情况,所以它应该是虚拟地址,应该也是通过映射的方式来实现的,进程地址空间中的地址应该是一些虚拟地址。
进程地址空间本质是进程看待内存的方式,抽象出来的一个概念,内核:struct mm_struct,这样的每个进程,都认为自己独占系统内存资源,实际上是操作系统通过对物理地址分段,分配给不同的进程,但是对于进程而言,看到整个资源全在自己手上,体现了进程的独立性,要实现这种形式,我们就需要在进程和内存硬件之间加一层来实现,这一层就是“页表”
什么是页表呢?
通过上述,我们可以知道要想实现,那就需要建立虚拟地址到物理地址的映射,页表就是这样一张实现虚拟地址到物理地址的映射表,每个进程都有一张,这样进程就在可以都看到同一份资源的情况下随意使用地址,即使虚拟地址相同,但是其映射的物理地址不同。
操作系统是软硬件资源的管理者,我们不能越过操作系统去直接访问硬件,这是为了避免风险。
需要详细了解可以看一下下边的文章:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。