linux 内存管理之基础篇

一.Linux内存管理的一些基本概念

内存空间:

            绝大多数的嵌入式系统的系统内存和I/O地址空间是统一编址的,内存和I/O地址空间共享0x00000000~0xFFFFFFFF共4GB地址空间范围,这4GB的地址空间范围包括以下几种存储空间:设备空间、内部高速SRAM空间、内部mini cache空间、低端中断向量空间、高端中断向量空间、RAM内存空间(系统的内存空间)、ROM空间。

内存页(PAGE):

            Linux一般以页为单位管理物理内存的,一般页的大小问4kb,对于页可以做到多小还是和处理器有关的。

内存区段(bank):

            一个BANK表示一块连续的内存空间,对应于处理器的ram片选管脚链接的ram的内存空间。对于RAM在系统的起始地址和大小可以通过处理器寄存器设置的,如果处理器所链接所有RAM芯片都设置为首尾地址相连的话,Linux就可以用一个BANK来表示这片内存空间。

内存节点(node):

            其实就是一个或者多个BANK组成的集合,对于上述的1个BANK的话,就是只有一个内存节点了。如果RAM的起始地址是固定的,不可设置的话,就有可能是内存空间不连续,这样就需要几个BANK了。如果对Linux配置了CONFIG_DISCONTIGMEM的话,即可以每个BANK可以对应不同的内存节点,或是多个BANK对应一个内存节点,不配置的话,全部都对应内存节点0。当多个BANK对应一个内存节点时,在上述的情况可能会有内存孔洞,这样在启动的时候就要产生页帧位码和struct page数据结构。这样会影响到系统在启动的时候获得最大连续物理内存,但是启动后就不会有任何影响,因为mem_init()会把这些页帧位码和struct page所占的空间释放掉。

内存页区(zone):

             每个内存节点可分为3个内存页区,即DAM页区、Normal页区和HighMem页区。每个页区的含义如下:

             DAM页区:可以进行DAM操作的RAM内存区域。

             Normal页区:不可以进行DAM操作的RAM内存区域。

             HighMem页区:属于高端内存的区域,高端内存是指系统中的物理内存容量太大,其中超过一定域值的RAM内存页区就是高端内存页区。

空闲内存区域(free area):是内存页区内连续2^order页空闲内存组成的内存区域。

二.linux内存管理的任务为以下几项:

1)规划整个系统的物理和虚拟存储空间布局。

2)为虚拟空间即线性地址空间建立页表(虚拟地址到物理地址的映射关系)。

3)设置不同存储空间的访问控制属性,保护系统存储空间不被非法访问。

4)内存分配和释放。

三.linux中的物理和虚拟存储空间布局:

linux运行在虚拟存储空间的,由于系统支持MMU,则系统负责把远远小于4GB的物理内存根据不同的需要映射到整个4GB的虚拟内存空间中。首先我们来先说说他们的分布。

Linux物理内存空间一般分布:

首先它分为几个node(内存节点,内存节点号最大不可以超过MAX_NUMNODES-1,节点号是从0号开始的),然后有的内存节点只是包括一个BANK(内存区段),有的包括几个BANK,有的几个BANK里面可能有孔洞,有的可能没有。在这里我们主要说明一下node0号。

node0号的起始地址就是系统RAM内存物理开始地址,接着就是一级映射符号表,接着就是编译出内核本身所占的空间,接着就是逆序的每个node的页帧位码表。

系统RAM物理内存的开始地址就是node0号的开始地址,结束位置要看具体的RAM物理内存有多大。

一级映射符号表一般都是16kb,一般用swapper_pg_dir变量记录它在物理内存空间的起始地址。

编译出内核本身它的起始地址由_stext记录,结束地址由_end记录。

node的页帧位码表一共有多少,是由有多少个node决定的,而且存放的顺序是逆序,就是说现实有最大的node号的页帧位码表开始存放的,一直到node0号的页帧位码表。如果不清楚这个表有什么用途的话,我建议你上网查找一下。每个页帧位码表的起始地址是由node_bootmem_data[i].nod_bootmem_map变量来存放的。这个变量的结构体是struct bootmem_data(bootmem_data_t)

这样,我们就把Linux物理内存空间分布说完了,现在接下来说说Linux系统虚拟内存空间一般分布。

       首先,内核空间和用户空间合起来就是Linux线性地址空间(Linux虚拟地址空间)。

       内核空间大小为1GB,地址范围是:0xC0000000~0xFFFFFFFF。处理器的最高级别的超级用户模式下的代码和数据都是放在这里。工作在内核态的进程可以共享这部分内存。对于内核空间,我们要把它展开,它分为:内核逻辑地址空间和高端线性地址空间。

       (1)内核逻辑地址空间,地址范围是:PAGE_OFFSET~high_memory。这个线性地址空间是系统物理内存映射区。这里说的 系统物理内存其实就是我们刚才上面说的系统ram物理内存。对于要映射多少,这个和实际的内存有关,有时是全部,有时可能只是部分。有人会问,为什么不全部映射呢?这样不是浪费了!对于这个问题,我会在接下来说明的。这里还要补充一下,其实你可以看出,系统ram物理内存的起始地址是从0x0开始的,而内核逻辑空间的起始地址是从PAGE_OFFSET开始的,再加上他们之间的映射关系式一一对应的,这样说大小也是要一样的。所以说对于MMU来说,转换时就是加上或是减去一个PAGE_OFFSET的偏移量就可以了。

       在说高端线性地址空间之前,我要先介绍两个概念:低端内存、高端内存。

       低端内存:其实就是说系统RAM物理内存被映射要内核逻辑地址空间那部分实际内存,这部分内存是在系统初始化的时候就映射好了,还建立了页表,是永久的。

       高端内存:比低端内存高出的那部分内存就是高端内存,这部分内存没有固定的一一映射的内核逻辑地址空间。在系统初始化的时候一不会负责映射,二不会为之建立页表。只有需要的时候,才会为分配的高端内存建立页表,这样才可以被使用。对于什么时候开始才是高端内存,其实是存在一个阀值的,接下来我们再说说这个概念。

       阀值:0xC0000000~0xFFFFFFFF这个空间除了要映射系统内存空间外,还要映射处理器外设寄存器空间等I/O空间。当系统内存空间远远比1GB小的时候,这样在其之上的high_memory~0xFFFFFFFF就有足够的空间来个I/O空间映射了。当系统内存高于1GB的时候,就需要设置一个阀值,比如x86设置为896MB,这样前896MB就会映射到内核逻辑地址空间,剩下的128MB就是用来映射高端内存和I/O空间。我们称这段位高端线性地址空间。

       (2)高端线性地址空间,地址范围是:high_memory~0xFFFFFFFF。关于这段地址空间的分布,我不打算细讲。

四.为虚拟存储空间建立页表:

    其实在MMU中,我们已经知道有一级页表和二级页表之分了。其实对于Linux来说,我们是使用两级页表来实现地址映射的。其实就是考查一张表。现在,我们来讨论一下,这些表是怎么建立的。

    一级页表存放在虚拟地址swapper_pg_dir处,这张表里的每一项对应4GB虚拟内存中1MB存储空间,由于每一项占4字节,一共需要4096项,这样一张表的大小就是16KB。这张表的基址存放在swapper_pg_dir,表里的每项的地址对应于swapper_pg_dir[i]每个元素单元里面存放的内容。对于一个虚拟地址是固定对应这里的某个元素,前提是它一定要属于这个段。这样一个数组指针其实是存放在struct mm_struct xxx_mm.pgd这个变量里。xxx_mm这个变量一般是用来描述内存的。

    二级页表是以两端(2MB)空间所需的二级页表容量大小为单位来创建和分配的,为什么要以这个单位来创建呢?我现在来仔细说一下。

    首先,在ARM中swapper_pg_dir[i]每个单元对应的数据结构是pgd_t,这是一个8字节的类型。换句话说,一个这样的元素就对应两个一级页表中的两个页表项,也就是说对应两张二级页表。而在Linux中,每张表有两份实现,一份是Linux定义的,一份是硬件定义的,两个版本页表的大小一样,所以这里要申请2*2*256*4=4KB大小空间。

    总的来说,我们现在知道一张一级和二级的表是需要多大的内存空间来存放的。我们现在来看看Linux是怎么实现的。

    Linux在启动开始的汇编代码部分会调用_create_page_tables()汇编函数为系统的内存头4段(4MB)建立一张映射表。这样这头4MB的系统内存就和内核逻辑地址空间一一对应上了。最前面的16KB、一级页表16KB(这个就是我们上面说的)和内核本身(1MB),这样还剩下大约2MB多的空间,接下来为每个内存节点的页帧位码表的建立,提供最大为1MB的空间,剩下来的1MB多的空间就是空闲下来的,有了这些空闲的空间,就可以供某些函数申请用。如果你想使用低端内存或是其他的一些存储空间的话。先要建立我们上面二级页表。要想建立一个二级页表,就必须先申请用来存放二级页表的空间。这就是为什么剩下这些空闲空间的意义所在啦。

五.设置存储空间的访问控制属性:

    看过MMU文章,就不会对这个有任何陌生的感觉了,在这里我不在细讲这是怎么回事。我这里只是大概说下Linux的ARM中对这16个域的访问控制是怎么设置的。域3~15没有访问权限,0~1为管理者访问权限,最后2是客户访问权限。对于0~2域,Linux有再次细分了一次,就是在Linux里面有4种域,它们的定义如下:

       #define DOMAIN_USER 0

       #define DOMAIN_KERNEL 1

       #define DOMAIN_TABLE 1

       #define DOMAIN_IO 2

很明显,后面的数字表示这四个域分别属于0~2域中的哪个域。这里有点特殊的是,0号域不是管理模式了,而是客户模式了。所以每次用的时候要把权限改变一下,一般都是调用函数modify_domain。DOMAIN_USER是用于用户空间的域,DOMAIN_KERNEL是用于内核空间的域,DOMAIN_TABLE是用于页表空间的域,DOMAIN_IO是用于I/O空间的域。

现在介绍完Linux特殊定义后的域,还知道他们的权限。这样我们来说说,在早前我们提到的4GB存储空间包括的那几种空间分别是属于哪些域的。

         设备空间:DOMAIN_IO

         内部高速SRAM空间:DOMAIN_KERNEL

         内部mini cache 空间;DOMAIN_KERNEL

         低端中断向量空间:DOMAIN_USER

         高端中断向量空间:DOMAIN_USER

         RAM内存空间:DOMAIN_KERNEL

         ROM空间:DOMAIN_KERNEL

 

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值