接上文,用户态写完,本章写内核态内存空间。
3.2内核态内存
大家会发现用户态空间不管32还是64位,这种内存分布是相差不大的。是因为使用虚拟内存的系统,会让应用程序感到和别的程序是相互独立的,互不干扰,使得进程觉得自己用了这3G的全部内存。但内核态就不一样了,它是所有进程共享的,所有进程的内核态都是长一个鬼样子的。
注意内核态的内存地址也虚拟内存地址,不要以为来这就变成实际的物理地址了。
3.2.1 32位系统
在Linux中,系统使用/arch/x86/include/asm/page_32_types.h文件中的__PAGE_OFFSET来决定内核态的起始地址。在32位系统中定为0xC000 0000,作者没改过,大家可以试试这个可以改不。
1.在这1G空间中,最前边的896MB是直接映射区。
这块内存是和0到896M的物理地址直接映射。
虚拟内存的3G到3G+896MB的地址在减去3G,就是物理内存。这个映射关系是固定不变的。
可以通过cat /proc/iomem命令去查看,需要在su模式下才可以。分布如下图:
很明显,前1M是被系统给用了,而且还有保留区。
在1M后,就是内核的代码段、数据段、BSS段。这些都是我们说的装系统的那个系统,被装进电脑时加载到了内存所占据的内存地址。大家不要以为系统很NB,其实他也是一个程序。只不过是我们普通程序员写的程序是跑在系统上,而系统是直接跑在硬件环境上的。
直接映射区也分了两块。
1.1直接映射区的最前面0-16M被称为DMA映射区。
由于硬件问题,ISA总线的DMA控制器只能寻址前16M的地址,最前面16M只能给基于ISA的设备通过DMA使用,直接映射到内核的地址空间。所以就叫了DMA映射区。学名:ZONE_DMA。
1.2直接映射区的其他内存被称为NORMAL映射区。
这个就好理解了,就是可以正常使用的。学名:ZONE_NORMAL。
直接映射区中的两块是由物理内存映射进虚拟内存的,内核对虚拟内存怎么规划,也就是对物理内存怎么规划。
2. 896MB之前的内存都被分走了,896MB到1G的这快比较高的地址被称为高端内存高端内存。学名:ZONE_HIGHMEM。
因为896MB之前都是和物理内存实际映射的,所以高端内存就是剩下全部的物理内存共同映射的。
2.1 高端内存最前面8M被称为8M空洞。
这个空洞范围是在/arch/x86/include/asm/pgtable_32_areas.h中定义的,范围是high_memory到VMALLOC_START.
2.2 8M空洞后面的被称为vmalloc动态映射区。
范围是从VMALLOC_START到VMALLOC_END,使用动态映射的方式将物理内存映射到高端内存。
这个区域是内核使用vmalloc进行内存分配的,在虚拟内存上是连续的,但物理内存不一定连续。这导致了在映射的性能开销上要比直接映射要大。这部分会在硬件机制中会讲。
2.3 vmalloc动态映射区的后面的被称为可持久映射区。
范围是PKMAP_BASE到FIXADDR_START,这个区域可以和高端物理内存建立长期的映射关系。
使用kmap函数可以将物理内存映射到可持久映射区。
在可持久内核映射区,可通过调用函数kmap()在物理页框与内核虚拟页之间建立长期映射。这个空间通常为4MB,最多能映射1024个页框,数量较为稀少,所以为了加强页框的周转,应及时调用函数kunmap()将不再使用的物理页框释放。
2.4 可持久映射区的后面是固定映射区。
范围是FIXADDR_START到FIXADDR_TOP。定义在/arch/x86/include/asm/fixmap.h中。
这块区域的虚拟内存可以自由的映射到高端物理内存上,但虚拟内存的地址是固定的,而物理内存地址是可以改变的。
是为了在内核启动的时候,有的模块比内存模块起的快时,也有可以使用的虚拟内存。让这些模块可以将自己的物理地址映射到这些固定分配的虚拟地址上。
这块空间具有如下特点:
(1)每个 CPU 占用一块空间
(2)在每个 CPU 占用的那块空间中,又分为多个小空间,每个小空间大小是 1 个 page,每个小空间用于一个目的,这些目的定义在 kmap_types.h 中的 km_type 中。
2.5 固定映射区的后面是临时映射区。
范围是FIXADDR_TOP到4G。一般来讲FIXADDR_TOP是0xFFFF F000.临时映射区大小一般是4MB。
2.4和2.5这两块区域其实是一个整体,也叫固定映射区和保留区。该区主要应用在多处理器系统中,因为在这个区域所获得的内存空间没有所保护,故所获得的内存必须及时使用;否则一旦有新的请求,该页框上的内容就会被覆盖,所以这个区域叫做临时映射区。
如图:
3.2.1 64位系统
32位系统只有1G的内核空间,所以说要精细化操作,就分出来的很多内存区域来管理。但64位操作系统有128TB的内核态空间,就可以不用那么精细了。
在64位的内核态中取消了高端内存的那种复杂映射,它可以随便访问物理内存。但其余各段的作用和32位的差不多。
1.内核态开始就一段8TB大小的空洞。
主打一个富裕,挥霍。
2.空洞后边是64TB大小的直接映射区。
这个地址减去PAGE_OFFSET就是物理地址。
PAGE_OFFSET是在/arch/x86/include/asm/pgtable_64_types.h中定义,大小是8TB。
3.直接映射后边是1TB大小的空洞。
那是相当富裕。
4.空洞后边是32T大小的vmalloc映射区。
5.vmalloc映射区后接一个1T大小的空洞。
6.空洞后有一个1T大小的虚拟内存映射区。
从VMEMMAP_START开始,定义在/arch/x86/include/asm/pgtable_64_types.h。
里面存放了物理内存页的描述符,来描述物理内存页。
7.虚拟内存映射区后面有一个512M大小的代码段。
这个段是来存放内核代码段、全局变量段、BSS段等。
这个段减去__START_KERNEL_map就是物理内存的地址。
__START_KERNEL_map也是在/arch/x86/include/asm/pgtable_64_types.h定义。