程序地址空间
代码验证
注: C/C++程序地址空间根本不是内存
代码验证
#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){
//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;
}
子进程与父进程地址没有发生变化,如果C/C++打印出来的是物理内存地址,这种情况不可能存在。所以这里我们使用的地址绝对不是物理地址,而是虚拟地址。
进程地址空间
地址空间本质:内核中的一种数据类型
每个进程都认为自己有一个地址空间,都认为自己独占物理内存。
每个进程都认为地址空间的划分是按照4GB空间划分的。
进程地址空间:先描述再组织,在内核中是一个数据结构类型,具体进程的地址空间变量。
虚拟地址:地址空间上进行区域划分时,对应的线性位置。(0x00000000~0xFFFFFFFF)
如何使用struct结构体进行各种局域划分,各种区域划分如何和物理内存进行关联?
页表 + MMU
页表:虚拟地址+物理地址
将虚拟地址转换成物理地址(映射) ---> 通过中间软件层(OS) 防止出现越界操作
例如:
字符串不可被修改的根本原因
str为栈上定义的指针变量而"Hello world"是在字符常量区(不能进行修改)。
本质:操作系统给的权限只有读权限,页表具有权限管理。
我们申请1000个字节,我们立马能使用1000个字节吗?
不一定,可能会存在暂时不会全部使用或者暂时不使用。
在操作系统角度,如果空间立马给你,是不是会有一部分空间本来可以供其他进程使用而被闲置?
解决办法:基于缺页中断进行物理内存申请
为什么要地址空间?
1.通过添加一层软件层,完成有效的堆进程操作内存进行风险管理。本质目的是为了保护内存以及各个进程的数据安全。
2.将内存申请和内存使用的概念在时间上划分清楚,通过虚拟地址空间,来屏蔽底层申请内存的过程,达到进程读写内存和OS进行内存管理操作,进行软件上面的分离。
3.站在CPU和应用层的角度,进程可以统一看作统一使用4GB空间,而且每个空间区域的相对位置是比较确定的。
4.程序的代码和数据可以加载到物理内存的任意位置。(页表可以通过映射,虚拟位置连续但是物理位置可以不连续,简化了数据的管理。)
总结:
一个进程要找到对应的代码,
(1)先定位到虚拟内存
(2)在虚拟内存中找到代码的虚拟地址
(3)根据页表映射,找到对应的物理地址
(4)在物理地址中找到对应的代码
所以
进程 = 进程控制块(task_struct) + 虚拟内存(mm_struct) + 页表(MMU) + 物理内存中的代码和数据。
补充知识:
写时拷贝,拷贝的是物理空间。
父子进程一般的代码是共享的。所以,所有的只读数据可以只有一份。
列如
char *str = "Hello world."; char *p = "hello world.";
这两个指针打印出来的值一样吗?
打印的值一样,操作系统维护一份的成本最低。
两个指针指向了同一地址,通过页表映射到同一物理地址。
代码验证