在之前使用C语言中,我们知道使用malloc函数可以开辟一块堆空间,然后这块堆空间如果不用的话是需要释放的,然后一般的变量是放在栈区上面的,还有一些静态区,代码段是存放静态变量和代码的,这些空间可以说是内存,但他又不是真正意义上的物理内存,我们可以做一个实验,我们可以使用fork函数生成一个子进程,然后子进程继承父进程的一个变量,子进程修改这个变量的内容,然后打印这个变量的值和他的地址,结果我们会惊讶的发现,他们显示的地址竟然是一样的,但是变量去不一样,如图:
由图我们可以推断出,他们用的地址肯定不是相同的地址,因为他们一样的地址中的值却不一样,因此,他们所使用的空间并不是同一块空间,他们使用的地址并不是真正的存放于物理内存芯片上的物理地址,而是虚拟地址,这个地址是软件意义上的地址,而且每一个进程,这个地址是独一份的,与PCB进程控制块相似,他们这个地址的编码的起始和结束都存放于每一个进程的PCB进程控制块中,而这个地址空间的大小是根据真实计算机的内存大小进行映射,形成一个虚拟的地址空间,然后将这个地址空间进行分割,便就有了堆空间和栈空间,对于一般的32位计算机来讲,如果按字节编址,则这个地址空间的大小为4 GB.
其实这个虚拟的进程地址空间是通过页表来管理的,页表将物理内存与虚拟内存进行一一对应,便形成了一个虚拟的进程地址空间,因为被映射出来的虚表是虚拟的空间地址,因此为了方便可以进行线性编址,因此进程地址空间又被称为线性地址空间,每个进程都有自己独有的进程地址空间与自己独有的映射页表,所以整个物理内存的不同分段可以由多个页表进行映射。
进程地址空间的虚拟化映射有以下几个优点:
1.因为物理内存的访问由页表控制,因此并不会出现进程之间的内存越界访问,比如在C语言中,如对野指针控制针进行越界访问,并不会出现影响别的进程,或者说影直接影响操作系统的情况,页表首先就会阻拦这类似的违规访问,这也变相的维护了系统安全.
2.当一个父进程使用fork创建一个子进程后,父子进程共享部分代码,且子进程将父进程的地址空间与页表复制一份,但他们的页表还是使用的同一块物理地址,当子进程或父进程任意一方改变他们共有的变量时,操作系统会对修改他们共有变量的一方所在的页表与物理地址修改映射关系,在另一块空的物理空间上添加一个修改后的值,来使修改后的子进程不影响父进程原先的值,这样就实现了父子进程的独立性,这样的方法也被叫做写时拷贝.
3.还有最后一个优点就是可以让进程以统一的视角来看待进程对应的代码数据,各个区域方便编译器统一的对各个数据来进行编译,因为当一个进程被编译好放在磁盘中时,编译器会对里面所有的数据进行编址,此时也会有一个地址空间,这个地址空间被叫做逻辑地址空间,当一个可执行程序被加载到内存时,这个逻辑地址空间也会被加在内存中。此时,这个可执行程序的所有数据会有物理地址和逻辑地址两套地址,当CPU从可执行程序的,Main函数开始执行时,此时页表已经构建好了Main函数的虚拟地址与物理地址之间的映射,CPU直接使用main函数的虚拟地址开始执行主函数的指令,当调用到一个函数时,页表会通过这个函数的逻辑地址生成虚拟地址与物理地址的映射关系,然后CPU直接使用这个虚拟地址就可以调用到要使用的这个函数了,从始至终CPU都没有使用到物理内存的真实地址,这样就让编译器编译一个程序时,只需要考虑正在编译这一个程序时的地址空间分布而不需要考虑别的,这更加提高了代码的编译效率
标签:映射,空间,地址,内存,linux,进程,页表 From: https://www.cnblogs.com/qjwxlj/p/18227208