很早接触到了虚拟地址和虚拟地址空间,今天总结一下
首先怎么看虚拟地址空间呢
int global = 0;
int main()
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
return 0;
}
else if(id == 0){ //child
printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
}else{ //parent
printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
}
sleep(1);
return 0;
}
打印出结果结果地址是一样的
可以理解为子进程是以父进程为模板创造的,所以地址一样
那么对上面的代码进行改造
int global = 0;
int main()
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
return 0;
}
else if(id == 0){ //child
global = 1;
printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
}else{ //parent
printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
}
sleep(1);
return 0;
}
打印会发现,地址依然一样,但是值不一样了,我们都知道,变量对应的是一块内存空间,里面存着变量的值,怎么可能内存空间一样但是储存的变量的值不同呢
因为这个地址不是物理地址,而时虚拟地址
代码共享(只读) 数据独有
为啥要虚拟空间
简单理解一下
为了更好的利用内存空间
那么在操作系统中是这么描述和分配一个虚拟地址空间的呢
没错是一个结构体描述的
struct mm_struct
描述了一个完整的虚拟地址空间
{
ulong mem_size;//大小
ulong code_start;//代码段开始位置
ulong code_end;//代码段结束位置
}
程序运行时需要连续的地址空间,虚拟地址连续,但是映射到物理地址上不是连续的,虚拟地址和物理地址之间有页表
虚拟地址空间可称为内存描述符
那么在创建子进程时,发生了什么
啊啊啊回忆学过的东西真头疼
那么发生了什么呢
首先,子进程是通过复制父进程创建的,包括父进程的页表,这也就是说明,现在子进程和父进程是指向同一块物理内存空间的,当内存的数据,变量发生改变时,操作系统会为子进程重新分配内存并开辟页表
这就是写实拷贝技术
进程也具有独立性
所以虚拟空间有什么用
1.提高内存利用率
2.可以内存访问控制
3.保持进程独立性