免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 10062 | 回复: 17
打印 上一主题 下一主题

[内存管理] 内存管理(X86-32位系统) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-05-28 11:53 |只看该作者 |倒序浏览
本帖最后由 pywj777 于 2012-05-28 11:58 编辑

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

评分

参与人数 1可用积分 +10 收起 理由
Godbach + 10 感谢分享!

查看全部评分

论坛徽章:
0
2 [报告]
发表于 2012-05-28 12:25 |只看该作者
硬件地址的基本概念
  • 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域地址。

待续...

论坛徽章:
0
3 [报告]
发表于 2012-05-28 14:16 |只看该作者
软件地址的基本概念
  • 逻辑地址:是一个32位长的地址。所有进程地址都使用逻辑地址。
  • 线性地址(也称为虚拟地址):是一个32位长地址,可以用来表示高达4G的地址,值的范围从0x00000000到0xffffffff。它是通过分段单元的硬件电路将逻辑地址转换为线性地址,即将逻辑地址加上一个段起始地址就得到了线性地址。在linux中,所有的段都从0x00000000开始,所以在linux下逻辑地址等于线性地址,即进程地址也是线性地址。后面我们就不再讨论逻辑地址,而都使用线性地址代表进程所使用的地址。
  • 物理地址:是通过分页单元的硬件电路将线性地址转换为物理地址。该地址就是上面所说的存储器域地址,是CPU所能访问的地址空间的集合


下面用图对上面概念进行解释:


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

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


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


对于X86 CPU中的分段与分页网上有很多资料,我这就不再介绍了。

待续...

论坛徽章:
0
4 [报告]
发表于 2012-05-28 15:11 |只看该作者
本帖最后由 pywj777 于 2012-05-28 15:12 编辑

前面介绍过,linux内存管理分为地址管理和存储设备管理。存储设备是由page结构进行管理的,每个page结构管理4K内存。地址管理主要是地址映射(线性地址向存储器域地址映射),是由分页单元的逻辑电路完成的。硬件地址映射(存储器域到DRAM域和PCI总线域地址映射)已由BIOS完成,并通过e820存储器域映射表提供给系统使用。

下面看一个包含4G DRAM设备的BIOS-e820图表
BIOS-provided physical RAM map:
BIOS-e820: 0000000000000000 - 000000000009ec00 (usable)
BIOS-e820: 000000000009ec00 - 00000000000a0000 (reserved)
BIOS-e820: 00000000000e0000 - 0000000000100000 (reserved)
BIOS-e820: 0000000000100000 - 0000000020000000 (usable)     (512M)
BIOS-e820: 0000000020000000 - 0000000020200000 (reserved)
BIOS-e820: 0000000020200000 - 0000000040000000 (usable)     (512M)
BIOS-e820: 0000000040000000 - 0000000040200000 (reserved)
BIOS-e820: 0000000040200000 - 00000000bad59000 (usable)      (2048M)
BIOS-e820: 00000000bad59000 - 00000000bada6000 (ACPI NVS)
BIOS-e820: 00000000bada6000 - 00000000badae000 (ACPI data)
BIOS-e820: 00000000badae000 - 00000000badc1000 (reserved)
BIOS-e820: 00000000badc1000 - 00000000badc2000 (ACPI NVS)
BIOS-e820: 00000000badc2000 - 00000000badd3000 (reserved)
BIOS-e820: 00000000badd3000 - 00000000badd6000 (ACPI NVS)
BIOS-e820: 00000000badd6000 - 00000000badf6000 (reserved)
BIOS-e820: 00000000badf6000 - 00000000badf8000 (usable)
BIOS-e820: 00000000badf8000 - 00000000bae09000 (reserved)
BIOS-e820: 00000000bae09000 - 00000000bae16000 (ACPI NVS)
BIOS-e820: 00000000bae16000 - 00000000bae3b000 (reserved)
BIOS-e820: 00000000bae3b000 - 00000000bae7e000 (ACPI NVS)
BIOS-e820: 00000000bae7e000 - 00000000bb000000 (usable)
BIOS-e820: 00000000bb800000 - 00000000bfa00000 (reserved)
BIOS-e820: 00000000fed1c000 - 00000000fed40000 (reserved)
BIOS-e820: 00000000ff000000 - 0000000100000000 (reserved)
BIOS-e820: 0000000100000000 - 000000013fe00000 (usable)    (1024M)

从存储设备管理的角度看,内核根据这个图表获得可用物理内存大小(usable部分),并建立一个page数组(每个page项管理4K内存)对所有可用内存进行管理。同时根据Memory split(用户/内存线性地址空间的分配比例,我的系统是1G/3G user/kernel split)将page数组划分为不同的管理区(DMA、NORMAL、HIGHMEM),并为DMA、NORMAL区的内存建立好页目录/页表的映射关系(管理区的概念后面会介绍)。

从地址管理的角度看,0~3G存储器域地址大部分用于映射DRAM域(也有部分被保留做其它用途,在page数组中对保留区管理的page结构会标识为不可用),3G~4G被用作其它用途,4G~5G存储器域地址被用于映射DRAM域(我们的设备使用了4G DRAM)。我们可以得到以下几点:
  • 前3G存储器域地址都被用于映射DRAM域,是为了内核线性地址空间能够多映射一些DRAM空间。因为内核线性地址空间通过页目录/页表映射为存储器域地址,而被映射的这部分存储器域地址映射的DRAM域越多,则意味着内核线性地址空间映射的DRAM空间越多。
  • 由于最高1G的DRAM地址被4G~5G的存储器域地址所映射,所以为了能够访问最高1G的DRAM空间,我们必须打开PAE选项,该选项会使CPU的寻址空间增加到36位,即CPU可访问64G存储器域地址。
  • 由于我们为内核分配的3G线性地址空间,而这其中还有512M(通过内核启动命令vmalloc=512M设置的)被用于VMALLOC,所以内核只有2.5G线性地址用于映射存储器域地址,也就是说只有2.5G内存被DMA、NORMAL区管理,其余1.5G内存被HIGHMEM区管理(这个区的内存还未进行页目录/页表映射)。


待续...

论坛徽章:
0
5 [报告]
发表于 2012-05-28 15:28 |只看该作者
本帖最后由 pywj777 于 2012-05-28 15:39 编辑

地址管理

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)。


待续...

论坛徽章:
0
6 [报告]
发表于 2012-05-28 15:48 |只看该作者
本帖最后由 pywj777 于 2012-05-28 15:52 编辑

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

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的大小就是剩下的物理内存,如果没开起,则这部分物理内存就无法管理了。


待续...

论坛徽章:
0
7 [报告]
发表于 2012-05-28 15:52 |只看该作者
本帖最后由 pywj777 于 2012-05-28 17:27 编辑

下面通过几个实例来看一下在2G物理内存平台上,进行不同的线性地址空间划分对物理内存管理区的影响。

1G/3G user/kernel splitit   (__VMALLOC_RESERVE = 128M)
0MB HIGHMEM available.     
2047MB LOWMEM available.  
  mapped low ram: 0 - 7ffd0000
  low ram: 0 - 7ffd0000
  node 0 low ram: 00000000 - 7ffd0000
  node 0 bootmap 00008000 - 00017ffc
Memory: 2071924k/2096960k available (2801k kernel code, 24644k reserved, 1065k data, 328k init, 0k highmem)
virtual kernel memory layout:
    fixmap  : 0xfff17000 - 0xfffff000   ( 928 kB)
    pkmap   : 0xff800000 - 0xffc00000   (4096 kB)
    vmalloc : 0xc07d0000 - 0xff7fe000   (1008 MB)   
   lowmem  : 0x40000000 - 0xbffd0000   (2047 MB)  
    .init : 0x413c7000 - 0x41419000   ( 328 kB)
      .data : 0x412bc701 - 0x413c6e64   (1065 kB)
      .text : 0x41000000 - 0x412bc701   (2801 kB)
物理内存管理区的划分:HIGHMEM物理内存大小为0M,LOWMEM物理内存是2047M。
线性地址空间的划分:内核为VMALLOC预留的线性地址空间大小是1008M(vmalloc),内核直接映射的线性地址空间大小是2047M(lowmem)。
      

1G/3G user/kernel splitit   (__VMALLOC_RESERVE = 128M, vmalloc=1512M)
527MB HIGHMEM available.
1519MB LOWMEM available.
  mapped low ram: 0 - 5effe000
  low ram: 0 - 5effe000
  node 0 low ram: 00000000 - 5effe000
  node 0 bootmap 00008000 - 00013e00
Memory: 2071912k/2096960k available (2801k kernel code, 24656k reserved, 1065k data, 328k init, 540488k highmem)
virtual kernel memory layout:
    fixmap  : 0xfff17000 - 0xfffff000   ( 928 kB)
    pkmap   : 0xff800000 - 0xffc00000   (4096 kB)
    vmalloc : 0x9f7fe000 - 0xff7fe000   (1536 MB)
    lowmem  : 0x40000000 - 0x9effe000   (1519 MB)
      .init : 0x413c7000 - 0x41419000   ( 328 kB)
      .data : 0x412bc701 - 0x413c6e64   (1065 kB)
      .text : 0x41000000 - 0x412bc701   (2801 kB)
物理内存管理区的划分:HIGHMEM物理内存大小为527M,LOWMEM物理内存是1519M。
线性地址空间的划分:内核为VMALLOC预留的线性地址空间大小是1536M(vmalloc),内核直接映射的线性地址空间大小是1519M(lowmem)。

      
2G/2G user/kernel split   (__VMALLOC_RESERVE = 128M)
135MB HIGHMEM available.
1911MB LOWMEM available.
  mapped low ram: 0 - 777fe000
  low ram: 0 - 777fe000
  node 0 low ram: 00000000 - 777fe000
  node 0 bootmap 00008000 - 00016f00
Memory: 2071924k/2096960k available (2801k kernel code, 24644k reserved, 1065k data, 328k init, 139080k highmem)
virtual kernel memory layout:
    fixmap  : 0xfff17000 - 0xfffff000   ( 928 kB)
    pkmap   : 0xff800000 - 0xffc00000   (4096 kB)
    vmalloc : 0xf7ffe000 - 0xff7fe000   ( 120 MB)
    lowmem  : 0x80000000 - 0xf77fe000   (1911 MB)
      .init : 0x813c7000 - 0x81419000   ( 328 kB)
      .data : 0x812bc6b1 - 0x813c6e64   (1065 kB)
      .text : 0x81000000 - 0x812bc6b1   (2801 kB)
物理内存管理区的划分:HIGHMEM物理内存大小为135M,LOWMEM物理内存是1911M。
线性地址空间的划分:内核为VMALLOC预留的线性地址空间大小是120M(vmalloc),内核直接映射的线性地址空间大小是1911M(lowmem)。


3G/1G user/kernel split   (__VMALLOC_RESERVE = 128M)
1159MB HIGHMEM available.
887MB LOWMEM available.
  mapped low ram: 0 - 377fe000
  low ram: 0 - 377fe000
  node 0 low ram: 00000000 - 377fe000
  node 0 bootmap 00008000 - 0000ef00
Memory: 2072684k/2096960k available (2801k kernel code, 23884k reserved, 1065k data, 328k init, 1187656k highmem)
virtual kernel memory layout:
    fixmap  : 0xfff17000 - 0xfffff000   ( 928 kB)
    pkmap   : 0xff800000 - 0xffc00000   (4096 kB)
    vmalloc : 0xf7ffe000 - 0xff7fe000   ( 120 MB)
    lowmem  : 0xc0000000 - 0xf77fe000   ( 887 MB)
      .init : 0xc13c7000 - 0xc1419000   ( 328 kB)
      .data : 0xc12bc701 - 0xc13c6e64   (1065 kB)
      .text : 0xc1000000 - 0xc12bc701   (2801 kB)
物理内存管理区的划分:HIGHMEM物理内存大小为1159M,LOWMEM物理内存是887M。
线性地址空间的划分:内核为VMALLOC预留的线性地址空间大小是120M(vmalloc),内核直接映射的线性地址空间大小是887M(lowmem)。
     

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
8 [报告]
发表于 2012-05-28 15:55 |只看该作者
本帖最后由 Godbach 于 2012-05-28 15:56 编辑

回复 1# pywj777
感谢 LZ 分享。

之前回帖的两位网友,为了保持作者原帖的连续性,先把你们的回复贴删除了,请理解,谢谢。

   

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
9 [报告]
发表于 2012-05-28 18:07 |只看该作者
回复 1# pywj777
多谢 LZ 的分享。
请问 LZ,是否研究过 64 位上的相关内存管理,比如内核地址空间和 32 位的区别等等。


   

论坛徽章:
0
10 [报告]
发表于 2012-05-28 19:08 |只看该作者
本帖最后由 pywj777 于 2012-05-28 19:09 编辑

回复 13# Godbach

64位的还未研究过


   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP