首页 > 代码库 > Linux空间划分 & MMU

Linux空间划分 & MMU

Linux内核地址空间划分
    通常32位Linux内核地址空间划分0~3G为用户空间,3~4G为内核空间。注意这里是32位内核地址空间划分,64位内核地址空间划分是不同的。32位的Linux系统中从0x00000000到0xFFFFFFFF整个4GB虚拟存储空间。
    内核空间:内核空间表示运行在处理器最高级别的超级用户模式(supervisor mode)下的代码或数据,内核空间占用从0xC0000000到0xFFFFFFFF的1GB线性地址空间,内核线性地址空间由所有进程共享,但只有运行在内核态的进程才能访问,用户进程可以通过系统调用切换到内核态访问内核空间,进程运行在内核态时所产生的地址都属于内核空间。
    用户空间:用户空间占用从0x00000000到0xBFFFFFFF共3GB的线性地址空间,每个进程都有一个独立的3GB用户空间,所以用户空间由每个进程独有,但是内核线程没有用户空间,因为它不产生用户空间地址。另外子进程共享(继承)父进程的用户空间只是使用与父进程相同的用户线性地址到物理内存地址的映射关系,而不是共享父进程用户空间。运行在用户态和内核态的进程都可以访问用户空间。
    这样一个用户程序可以独立使用自己的0~3G的空间,同时可以通过系统调用等使用内核空间的1G空间,这样每个应用程序就相当于拥有4G的空间可以使用。
    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数据可能不在内存中。
    内核空间一般又会经过一次划分,比如划分为ZONE_DMA、ZONE_NORMAL、ZONE_HIGHMEM三块,上面的分析已经可以得出ZONE_DMA和ZONE_NORMAL可以使用简单的映射关系映射到物理内存,因为这部分空间对于内核对于每个应用程序来说都是一致的,然而用户空间因为要实现每个应用程序独立的0~3G的空间,映射相对来说比较麻烦一些(这种麻烦换来的是用户编写应用程序的极大方便)。ZONE_NORMAL的映射一般是直接采用一个偏移量实现的,比如实际的内存地址开始的地方是0x0,那么只需要加上一个偏移0xc0000000(3G)就可以了。但是如果这1G的空间都这样映射的话,内核空间只能够访问到实际物理内存的1G的空间,所以就有了高端内存来解决这个问题。高端内存一般是896~1024M这一段的128M的空间,当内核想访问高于896MB物理地址内存时,从0xF8000000 ~ 0xFFFFFFFF地址空间范围内找一段相应大小空闲的逻辑地址空间,借用一会。借用这段逻辑地址空间,建立映射到想访问的那段物理内存(即填充内核PTE页面表),临时用一会,用完后归还。这样别人也可以借用这段地址空间访问其他物理内存,实现了使用有限的地址空间,访问所有所有物理内存。
    对于s3c2440的用户空间地址的映射,采用的是页表的方式,需要注意的是页表是一个动态的概念,也就是说这个页表是会随着程序的运行而可能被修改的。MMU提供两个最基本的功能就是:
>>> 负责虚拟地址和物理地址之间的转换工作
    这个功能就是上面提到的每个应用程序都拥有自己独立的0~3G的用户空间,每个应用程序在这0~3G的空间里看到的都是自己的数据。上面说到的页表,就是MMU工作的时候实现虚拟地址和物理地址之间转换需要用到的。

>>> 提供硬件机制的内存访问授权
    在ARM处理器中,MMU将整个存储空间分成最多16个域,记作D0~D15,每个域对应一定的存储区域,该区域具有相同的访问控制属性。在ARM处理器中,MMU中的每个域的访问权限分别由CP15的C3寄存器中的两位来设定,C3寄存器刚好可以设置16个域的访问权限。    

linux的swap分区最好设置成内存容量的两倍:系统在满载的时候,就会有一部分程序占用的内存会被写入交换分区,当这时你的系统突然down掉的话,可以保证你的swap里有足够大的地方放下当时内存里的东西来作为后面查询使用。

Linux空间划分 & MMU