在我们学习c语言的时候我们都接触过下面这张空间布局图。
可是我们对他并不理解!
#include
#include
int a=1, b;
int main(int argc, char* argv[], char* env[])
{
printf("code addr:%p\n", main);
printf("uninit global addr:%p\n", &b);
printf("init global addr:%p\n", &a);
char* heap_men = (char*)malloc(10);
printf("heap addr:%p\n", heap_men);
printf("stack addr:%p\n", &heap_men);
int j=0;
for( j=0; j<argc; ++j)
{
printf("argv[%d] addr:%p\n", j, argv[j]);
}
for(j=0; env[j]; ++j)
{
printf("env[%d] addr:%p\n", j, env[j]);
}
return 0;
}
有程序可知空间布局图,的确如此分布;共享区不好演示,此处就不演示了。
我们了解了以上内容。我们来看看下面一段代码。
#include
#include
#include
int g_val = 0;
int main()
{
pid_t id = fork();
if(id < 0){
perror("fork");
return 0;
}
else if(id == 0){ //child,子进程肯定先跑完,也就是子进程先修改,完成之后,父进程再读取
g_val=100;
printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
}else{ //parent
sleep(3);
printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
}
sleep(1);
return 0;
}
//与环境相关,观察现象即可
child[15090]: 100 : 0x601058
parent[15089]: 0 : 0x601058
我们发现,父子进程,输出地址是一致的,但是变量内容不一样!能得出如下结论:
提醒:
3. 写时拷贝我们这里粗讲,以后文章会详细介绍;现在我们只需了解就可以了。
2. 写时拷贝:我们知道子进程是父进程的一份拷贝;由此父子进程的虚拟地址也是相同的。刚开始的时候两者虚拟地址映射同一块物理内存,当子进程的变量值发生改变的时候,就会立即开辟一块新的空间给子进程。此时两者虚拟地址虽然相同,但映射到实际的物理空间内存就不同了。
函数调用实际是通过虚拟地址调用的。
可执行程序其实编译的时候,内部已经有地址。
地址空间不止是OS内部要遵守,其实编译器也要遵守。
每一个函数和变量都有地址。
我们使用Linux的时候,我们写的test.c文件会通过
gcc -o mytest test.c这条指令生成mytest可执行文件.exe。
通过编译器;
此时mytest里面同样有内存,而且是虚拟地址;CPU调用的也是虚拟地址。CPU读到指令内部,使用的地址是虚拟地址,再通过映射找到物理地址。
.
.
.
感觉有所收获的话,友友们给小丁一个赞