Linux内存管理(x86-32位系统)

linux内存的管理主要分为两部分,地址管理和存储设备管理。下面针对这两部分介绍一下我对内存管理的理解。

硬件地址的基本概念

  • DRAM域地址:是DRAM控制器所能访问的地址空间集合。
  • PCI总线域地址:是PCI设备所能直接访问的地址空间集合。
  • 存储器域地址:是CPU所能访问的地址空间集合。

结合下图对上面概念进行解释:
技术分享

CPU访问DRAM域或PCI总线域地址空间时,都需要进行地址转换(将存储器域地址转换为相应域的地址)。例如:CPU访问DRAM域时,需要进行存储器域地址空间到DRAM域地址空间的转换(由DRAM控制器完成);CPU访问PCI总线域时,需要进行存储器域地址空间到PCI总线域地址空间的转换(由HOST主桥完成)。

在x86处理器系统中,会将DRAM域和PCI总线域映射到存储器域空间中,并且其大多数DRAM域中的地址与存储器域中的地址一一对应而且相等,而存储器域的PCI地址与PCI总线域的地址也一一对应而且相等。它们在存储器域空间的映射彼此独立,互不冲突,映射关系由BIOS提供(e820地址映射表)。

PCI设备访问DRAM域地址空间时,首先要经过HOST主桥将PCI总线域地址转换为存储器域地址,然后再由DRAM控制器将存储器域地址转换为DRAM域地址。

软件地址的基本概念

  • 逻辑地址,是一个32位长的地址。所有进程地址都使用逻辑地址。

  • 线性地址(也称为虚拟地址),是一个32位长地址,可以用来表示高达4G的地址,值的范围从0x00000000到0xffffffff。它是通过分段单元的硬件电路将逻辑地址转换为线性地址,即将逻辑地址加上一个段起始地址就得到了线性地址。在linux中,所有的段都从0x00000000开始,所以在linux下逻辑地址等于线性地址,即进程地址也是线性地址。后面我们就不再讨论逻辑地址,而都使用线性地址代表进程所使用的地址。

  • 物理地址,是通过分页单元的硬件电路将线性地址转换为物理地址。该地址就是上面所说的存储器域地址,是CPU所能访问的地址空间的集合

下面用图对上面概念进行解释:
技术分享

在X86系统中,所有进程都使用的是逻辑地址,它要通过分段单元硬件电路转换为线性地址,再通过分页单元硬件电路转换为物理地址(即存储器域地址)。

分段单元的逻辑如下图所示(将逻辑地址转换为线性地址)
技术分享

分页单元的逻辑如下图所示(将线性地址转换为物理地址)
技术分享

地址管理

linux将线性地址空间分为user和kernel两个空间

  • USER线性地址空间,指的是可被应用层访问的线性地址空间。其大小(32位处理器) = 4G - KERNEL线性地址空间大小。

  • KERNEL线性地址空间,指的是可被内核使用的线性地址空间。其大小 = LOWMEM线性地址空间大小 + VMALLOC线性地址空间大小。

    • LOWMEM线性地址空间,它占用了KERNEL线性地址空间中的一部分。其大小 = KERNEL线性地址空间大小 - VMALLOC线性地址空间大小 。
    • LOWMEM功能,这段地址空间在启动时已做好地址映射,其映射的物理内存是连续的,并与物理内存地址一一对应。
    • VMALLOC线性地址空间,它占用了KERNEL线性地址空间中的另一部分。其大小 = (KERNEL线性地址空间大小 - 物理内存大小) > VMALLOC_RESERVE ?KERNEL线性地址空间 - 物理内存大小 : VMALLOC_RESERVE
    • VMALLOC功能,用于在内核中分配线性地址连续,但物理地址不需连续的大内存区。这段地址空间初始化时未做任何地址映射。

linux内核线性地址空间变量的含义

  • PAGE_OFFSET = __PAGE_OFFSET = CONFIG_PAGE_OFFSET:这个值是用于划分USER线性地址和KERNEL线性地址的分界点。

  • __VMALLOC_RESERVE:表示为VMALLOC线性地址空间最小应保留的空间大小,默认是128M。这个值可在系统启动时通过内核参数vmalloc来改变。

  • VMALLOC_END:VMALLOC线性地址空间的结束地址。其值 = 最大线性地址 – 其它ROM内存空间大小

  • VMALLOC_START:VMALLOC线性地址空间的起始地址。其值 = VMALLOC_END - VMALLOC线性大小(最小为VMALLOC_RESERVE)

  • VMALLOC_OFFSET:固定大小为8M,用于再LOWMEM线性地址空间和VMALLOC线性地址空间之间建立一个隔离带,防止它们互相影响。

  • MAXMEM:其值为(VMALLOC_END - PAGE_OFFSET - __VMALLOC_RESERVE),表示内核能够直接映射的最大RAM容量,这个值也是相对固定的(与物理内存实际的大小没有关系),因为VMALLOC_END、PAGE_OFFSET、 __VMALLOC_RESERVE是相对固定的。

  • MAXMEM_PFN:其值为PFN_DOWN(MAXMEM),表示内核能够直接管理的内存页个数。

  • MAX_ARCH_PFN:表示CPU最大可访问的内存页数。对应32位CPU,如果其开启了PAE,则MAX_ARCH_PFN=(1ULL?(36-PAGE_SHIFT))。如果未开启PAE,则MAX_ARCH_PFN=(1ULL?(32-PAGE_SHIFT))

  • max_pfn:是从bios发现的最大的能管理的内存页个数, 与实际内存大小有关,但它的大小不能超过MAX_ARCH_PFN。
    当max_pfn <= MAXMEM_PFN时说明实际的物理内存比内核能够线性映射的内存数小,此时就不需要Highmem来管理了。此时就算开启了HIGHMEM,ZONE_HIGHMEM区能够管理的内存大小也是0。
    当max_pfn > MAXMEM_PFN时说明实际的物理内存比内核能够线性映射的内存数大,这样多余的那部分物理内存就不能进行线性映射了,必须通过页表映射(即vmalloc来管理), 该物理内存应该由ZONE_HIGHMEM区来管理。而如果没有开启HIGHMEM选项,则这部分内存就丢失了,不再被管理。

  • max_low_pfn:低端内存(相对于highmem)的最大页数,这个就是ZONE_NORMAL区能管理的最大页数。
    如果实际物理内存比内核能够线性映射的内存数小,max_low_pfn = max_pfn,即所有的内存都归低端内存区管理。
    如果实际物理内存比内核能够线性映射的内存数大,max_low_pfn = MAXMEM_PFN,即低端内存区能够管理的最大值就是内核能够线性映射的最大值。

  • highmem_pages:高端内存的最大页数,这个是就是ZONE_HIGHMEM区管理的最大页数。其值为(max_fn – max_low_pfn)。

存储设备管理(即可使用物理内存空间的管理)

linux内核将物理内存空间进行功能区划分(划分时要参考地址管理中线性地址的分配),并通过page结构进行管理

  • ZONE_DMA物理内存空间,在x86体系结构上它是0~16M的内存范围。由page结构进行管理,这个区包含的页能被老设备执行DMA操作。

  • ZONE_NORMAL物理内存空间,在x86体系上它是16M~KERNEL->LOWMEM线性地址对应的内存范围。由page结构进行管理。
    (内核在启动时,就已经将上面这两个区的内存空间映射到KERNEL线性地址空间中了。所以内核不需做任何操作,就可以直接访问这些物理内存。但为了保证所访问内存页没有被其它功能使用,因此在访问内存之前,要使用get_free_page()等函数获取空闲页,并将该页标记为使用。返回的物理页不需要做任何地址映射,仅仅是将物理地址加上内核空间偏移量就获得了其线性地址)

  • ZONE_HIGHMEM物理内存空间,其大小 = 实际物理内存大小 – KERNEL->LOWMEM线性地址空间大小。由page结构进行管理,用于管理物理内存大小超过KERNEL->LOWMEM线性地址空间大小以外的物理内存。这段内存空间没有被映射到内核线性地址空间,在使用时,要通过alloc_page()分配一个page页,然后再将该页映射到线性地址空间,这样才能通过线性地址访问。内核(通常用vmalloc()函数)将HIGHMEM物理内存页映射到VMALLOC线性地址空间,因此内核可使用的HIGHMEM物理内存大小与VMALLOC线性地址空间大小有关。另外,HIGHMEM物理内存页通常被映射到USER线性地址空间。

  • ZONE_NORMAL 与ZONE_HIGHMEM主要的区别是ZONE_NORMAL中的物理内存不需要做页表映射直接用就行了,其线性地址的最大值是MAXMEM。而ZONE_HIGHMEM中的物理内存需要做页表映射才能使用,其线性地址的范围是VMALLOC_START~VMALLOC_END。所以不能做线性映射的内存都归ZONE_HIGHMEM来管理了。如果开启的HIGHMEM选项,则ZONE_HIGHMEM的大小就是剩下的物理内存,如果没开起,则这部分物理内存就无法管理了。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。