Question #
由于疾病、睡眠不足等原因导致身体虚弱时,为什么人会变得易怒易躁?
Answer #
位于大脑边缘系统的“小脑扁桃体”负责产生愤怒等情绪。身体虚弱时,前额皮质功能下降,无法控制对遵循本能的大脑边缘系统。
给出一个物理地址并不能准确地定位到内存空间,内存空间只是映射物理地址空间中的一个子集,物理地址空间中可能有空洞,有 ROM,有内存,有显存,有 I/O 寄存器,所以获取内存有多大没用,关键是要获取哪些物理地址空间是可以读写的内存。
由于 PC 机上电后由 BIOS 执行硬件初始化,中断向量表是 BIOS 设置的,所以执行中断自然执行 BIOS 服务。这个中断服务是 int 15h,但是它需要一些参数,就是在执行 int 15h 之前,对特定寄存器设置一些值,代码如下。
_getmemmap:
xor ebx,ebx ;ebx设为0
mov edi,E80MAP_ADR ;edi设为存放输出结果的1MB内的物理内存地址
loop:
mov eax,0e820h ;eax必须为0e820h
mov ecx,20 ;输出结果数据项的大小为20字节:8字节内存基地址,8字节内存长度,4字节内存类型
mov edx,0534d4150h ;edx必须为0534d4150h
int 15h ;执行中断
jc error ;如果flags寄存器的C位置1,则表示出错
add edi,20;更新下一次输出结果的地址
cmp ebx,0 ;如ebx为0,则表示循环迭代结束
jne loop ;还有结果项,继续迭代
ret
error:;出错处理
上面的代码是在迭代中执行中断,每次中断都输出一个 20 字节大小数据项,最后会形成一个该数据项(结构体)的数组,可以用 C 语言结构表示,如下。
#define RAM_USABLE 1 //可用内存
#define RAM_RESERV 2 //保留内存不可使用
#define RAM_ACPIREC 3 //ACPI表相关的
#define RAM_ACPINVS 4 //ACPI NVS空间
#define RAM_AREACON 5 //包含坏内存
typedef struct s_e820{
u64_t saddr; /* 内存开始地址 */
u64_t lsize; /* 内存大小 */
u32_t type; /* 内存类型 */
}e820map_t;
x86机器上如何开启Cache?
目前 Cache 已经成为了现代计算机的标配,但是 x86 CPU 上默认是关闭 Cache 的,需要在 CPU 初始化时将其开启。
在 x86 CPU 上开启 Cache 非常简单,只需要将 CR0 寄存器中 CD、NW 位同时清 0 即可。CD=1 时表示 Cache 关闭,NW=1 时 CPU 不维护内存数据一致性。所以 CD=0、NW=0 的组合才是开启 Cache 的正确方法。
开启 Cache 只需要用四行汇编代码,代码如下:
mov eax, cr0
;开启 CACHE
btr eax,29 ;CR0.NW=0
btr eax,30 ;CR0.CD=0
mov cr0, eax
MESI 协议定义了 4 种基本状态:M、E、S、I,即修改(Modified)、独占(Exclusive)、共享(Shared)和无效(Invalid)。下面我结合示意图,给你解释一下这四种状态。
1.M 修改(Modified):当前 Cache 的内容有效,数据已经被修改而且与内存中的数据不一致,数据只在当前 Cache 里存在。比如说,内存里面 X=5,而 CPU 核心 1 的 Cache 中 X=2,Cache 与内存不一致,CPU
核心 2 中没有 X。

最后还要说一下 Cache 硬件,它会监控所有 CPU 上 Cache 的操作,根据相应的操作使得 Cache 里的数据行在上面这些状态之间切换。Cache 硬件通过这些状态的变化,就能安全地控制各 Cache 间、各 Cache 与内存之间的数据一致性了。
...长模式下的4KB分页
该分页方式下,64 位虚拟地址被分为 6 个位段,分别是:保留位段,顶级页目录索引、页目录指针索引、页目录索引、页表索引、页内偏移,顶级页目录、页目录指针、页目录、页表各占有 4KB 大小,其中各有 512 个条目,每个条目 8 字节 64 位大小,如下图所示。
上面图中 CR3 已经变成 64 位的 CPU 的寄存器,它指向一个顶级页目录,里面的顶级页目项指向页目录指针,依次类推。需要注意的是,虚拟地址 48 到 63 这 16 位是根据第 47 位来决定的,47 位为 1,它们就为 1,反之为 0,这是因为 x86 CPU 并没有实现全 64 位的地址总线,而是只实现了 48 位,但是 CPU 的寄存器却是 64 位的。这种最高有效位填充的方式,即使后面扩展 CPU 的地址总线也不会有任何影响,下面我们去看看当前分页模式下的 CR3、顶级页目录项、页目录指针项、页目录项、页表项的格式,我画了一张图帮你理解。
由上图可知,长模式下的 4KB 分页下,由一个顶层目录、二级中间层目录和一层页表组成了 64 位地址翻译过程。顶级页目录项指向页目录指针页,页目录指针项指向页目录页,页目录项指向页表页,页表项指向一个 4KB 大小的物理页,各级页目录项中和页表项中依然存在各种属性位,这在图中已经说明。其中的 XD 位,可以控制代码页面是否能够运行。
32 位虚拟地址被分为三个位段:页目录索引、页表索引、页内偏移,只有一级页目录,其中包含 1024 个条目 ,每个条目指向一个页表,每个页表中有 1024
个条目。其中一个条目就指向一个物理页,每个物理页 4KB。这正好是 4GB 地址空间。
上图中 CR3 就是 CPU 的一个 32 位的寄存器,MMU 就是根据这个寄存器找到页目录的。
CR3寄存器数据格式
页目录表项格式
页表项格式
页目录项、页表项都是 4 字节 32 位,1024 个项正好是 4KB(一个页),因此它们的地址始终是 4KB 对齐的,所以低 12 位才可以另作它用,形成了页面的相关属性,如是否存在、是否可读可写、是用户页还是内核页、是否已写入、是否已访问等。
如何开启 MMU?
要使用分页模式就必先开启 MMU,但是开启 MMU 的前提是 CPU 进入保护模式或者长模式,开启 CPU 这两种模式的方法,我们在前面第五节课已经讲过了,下面我们就来开启 MMU,步骤如下:
mov eax, PAGE_TLB_BADR ;页表物理地址
mov cr3, eax
;开启 保护模式和分页模式
mov eax, cr0
bts eax, 0 ;CR0.PE =1
bts eax, 31 ;CR0.P = 1
mov cr0, eax
切换到长模式必须要开启分页,如何开启?
切换到长模式必须要开启分页,想想看,长模式下已经不对段基址和段长度进行检查了,那么内存地址空间就得不到保护了。而长模式下内存地址空间的保护交给了 MMU,MMU 依赖页表对地址进行转换,页表有特定的格式存放在内存中,其地址由 CPU 的 CR3 寄存器指向,这在后面讲 MMU 的那节课会专门讲。
mov eax, cr4
bts eax, 5 ;CR4.PAE = 1
mov cr4, eax ;开启 PAE
mov eax, PAGE_TLB_BADR ;页表物理地址
mov cr3, eax
长模式中断门描述符格式是怎样的?
保护模式下为了实现对中断进行权限检查,实现了中断门描述符,在中断门描述符中存放了对应的段选择子和其段内偏移,还有 DPL 权限,如果权限检查通过,则用对应的段选择子和其段内偏移装载 CS:EIP 寄存器。
如果你还记得中断门描述符,就会发现其中的段内偏移只有 32 位,但是长模式支持 64 位内存寻址,所以要对中断门描述符进行修改和扩展,下面我们就来看看长模式下的中断门描述符的格式,如下图所示。
结合上图,我们可以看出长模式下中断门描述符的格式变化。
首先为了支持 64 位寻址中断门描述符在原有基础上增加 8 字节,用于存放目标段偏移的高 32 位值。其次,目标代码段选择子对应的代码段描述符必须是 64 位的代码段。最后其中的 IST 是 64 位 TSS 中的 IST 指针,因为我们不使用这个特性,所以不作详细介绍。
长模式也同样在内存中有一个中断门描述符表,只不过表中的条目(如上图所示)是 16 字节大小,最多支持 256 个中断源,对中断的响应和相关权限的检查和保护模式一样,这里不再赘述。