页表的理解
我们在之前一直都提到页表,知道它的作用是将虚拟地址映射到物理地址,但是它具体怎么映射的,它的结构是什么样的,并没有提及过。
char* str = "hello world";
*str = 'H';
上诉代码,会在运行时报错,原因是str指向的地址在字符常量区,字符常量区的内容是不允许用户去修改的。
代码在运行起来以后,操作系统是怎么知道用户在修改字符常量区的呢?
如上图所示的页表示意图,页表中不仅右虚拟地址和物理地址的映射关系,还有是否命中,RWX权限,U/K权限等等内容。
- U/K权限:U表示用户(user),K表示内核(kernal)。
- RWX权限:当前身份(用户或者内核)对当前地址的读,写执行权限。
- 上面代码在对srt指向的地址写内容时,先会经过页表映射到物理地址。
- 但是在页表中发现这是一个写操作,并且该地址是不允许被写的,此时MMU就发送信号,导致程序报错。
虚拟地址,物理地址以及属性所在的一行,称为条目。
仍然是这张图,需要将这张图分解进行讲解。
物理内存空间划分:
以32位系统为例,它的物理内存理论上有4GB大小,但是这4GB又被分成了多块小空间。
物理内存中会又很多个页框,并且这些页框也需要操作系统管理起来,采用的方式同样是先描述,再组织。
通过一个结构体来描述页框
struct_Page
{
//内存属性--4KB
}
代码形式如上所示,每一个页框都会有这样一个结构体对象,将多个结构体对象放在一个数组中:
struct_Page mem[];
可执行文件:
我们写好的代码会经过编译器的处理形成二进制可执行文件放在磁盘中,在运行的时候加载到内存中。
编译器在处理源文件生成的二进制可执行文件,同样是以4KB为单位的,这4KB的数据块被叫做页桢。
这一切都是设计好的,所以可执行程序在加载到内存中的时候是以4KB为单位的,正好一个页帧来填充一个页框。当页框被填充了以后,就会创建对应的struct_Page结构体对象,并且放在数组中,让操作系统来管理。
页目录和页表项
再回到页表,我们知道,每个进程对应的虚拟地址空间大小都是4GB的,也就是有232个地址,如果每个虚拟地址在页表中都对应着一个物理地址:
那么页表就会有232行,每一行都是一个条目,每个条目中不仅有物理地址,虚拟地址,还有其他属性,假设一个条目的大小是10B,那么光页表就有10*232=40GB,已经超过了物理内存的大小,所以页表肯定不是这样的。
实际上,页表是由页目录和页表项组成的。
在32位机器上,地址的大小是4G字节,也就是有32个比特位:
随便写了一个地址,如上图所示,一个32个比特位。
将32个比特位分为10个比特位,10个比特位,12个比特位,共3组。
之前的的页表 = 页目录 + 页表项,如上图所示。
- 32个比特位的高10位,作为页目录的下标,如上图所示的0000 0000,通过这个下标0可以访问到页目录中的第一个条目。<