进程地址空间
进程地址空间的排布:堆栈相对而生
这里的地址空间不是内存!
为何这样说呢?可以看以下的程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
int g_val=0;
int main()
{
pid_t id =fork();
if(id < 0)
{perror("error!");}
else if(id == 0)
{
// child
while(true)
{
g_val = 300;
printf("child:g_val:%d, &g_val:%p, pid: %d\n", g_val, &g_val, getpid());
sleep(1);
}
}
else{
// father
while(true)
{
printf("father:g_val:%d, &g_val:%p, pid: %d\n", g_val, &g_val, getpid());
sleep(1);
}
}
return 0;
}
同一个变量,不会出现两个值,所以这里的地址是虚拟地址
#include <stdlib.h>
int un_val;
int val = 1;
int main(int argc, char* argv[], char* env[])
{
printf("text:%p\n", main); // 代码区
printf("un_val:%p\n", &un_val);
printf("val:%p\n", val);
int *p = (int*) malloc(sizeof(int));
int *p2 = (int*) malloc(sizeof(int));
printf("heap1:%p\n", p);
printf("heap2:%p\n", p2);
int s1 = 10;
int s2 = 20;
printf("stack1: %p", &s1);
printf("stack2: %p", &s2);
}
如果再在main函数里定义一个static变量,可以看到该变量的地址在全局区,所以static变量的本质就是将该变量开辟在全局区域
字面常量const char*
.char*
,放在代码区
什么是进程地址空间(概念级别)
如何理解区域划分
深入理解进程地址空间(代码级别)
看task_struct的源码,可以看到有一个
struct mm_struct *mm
指针,其中mm_struct
就是地址空间,里面有各个区域的划分所以地址空间就是一种内核数据结构
struct mm_struct {
...
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack;
unsigned long arg_start, arg_end, env_start, env_end;
...
}
为什么同一个变量会有两个地址?
为什么要有进程地址空间
-
凡是非法的访问或者映射,OS都能识别到
(因为地址空间和页表是OS创建并且维护的,意味着凡是想要使用地址空间和页表进行映射,一定要在OS的监管下进行访问)
,并且终止这个进程,也便保护了物理内存中的所有合法数据,包括各个进程,以及内核的相关的有效数据 -
因为有地址空间的存在,页表映射的存在,我们的物理内存中,可以对未来的数据进行任意位置的加载
物理内存的分配 就可以和 进程的管理做到没有关系,也就是内存管理模块 和 进程管理模块做到了解耦合
我们在c/c++里malloc,new出来的空间是虚拟空间, 如果new出来后不立马使用,就会导致空间的浪费
本质上,因为有地址空间的存在,所以上层在申请空间时,其实是在地址空间上申请的,物理内存可以甚至一个字节都不给你,
而当你真正的对物理内存进行访址时(有os自动完成,用户和进程0感知),
才执行内存的相关管理算法,帮你申请内存,构建页表映射关系,然后再让你进行内存的访问
这种策略叫做延迟分配,用来提高整机的效率,这样,几乎内存的有效使用接近100% -
物理内存在理论上可以任意位置加载,因此物理内存中几乎所有的数据和代码在内存中是乱序的,
但是,因为页表的存在,可以将地址空间上的虚拟地址和物理地址进行映射,所以在进程视角上的内存分布是有序的
所以①地址空间+页表的的存在,可以将内存分布有序化
我们知道,地址空间是os给进程画的大饼,结合第2条,进程要访问物理内存中的数据和代码,可能目前并没有在物理内存中,同样的,
也可以让不同的进程映射到不同的内存上,所以很容易实现进程的独立性
②进程的独立性可以通过地址空间+页表的方式实现
结论:**因为有地址空间的存在,在32位系统下,每一个进程都认为自己有4gb有序的内存,通过页表映射到不同的区域,实现进程的独立性,
每一个进程,不知道,也不需要知道其它进程的存在