解析Linux系统下的高端内存

固安黄闷鸡米饭

固安黄闷鸡米饭

2016-03-31 15:15

今天图老师小编给大家介绍下解析Linux系统下的高端内存,平时喜欢解析Linux系统下的高端内存的朋友赶紧收藏起来吧!记得点赞哦~

解析Linux系统下的高端内存

   Linux内核地址空间划分

(本文来源于图老师网站,更多请访问https://m.tulaoshi.com/fuwuqi/)

  通常32位Linux内核虚拟地址空间划分0~3G为用户空间,3~4G为内核空间(注意,内核可以使用的线性地址只有1G)。注意这里是32位内核地址空间划分,64位内核地址空间划分是不同的。

  通常32位Linux内核虚拟地址空间划分0~3G为用户空间,3~4G为内核空间(注意,内核可以使用的线性地址只有1G)。注意这里是32位内核地址空间划分,64位内核地址空间划分是不同的。

  Linux内核高端内存的由来

  当内核模块代码或线程访问内存时,代码中的内存地址都为逻辑地址,而对应到真正的物理内存地址,需要地址一对一的映射,如逻辑地址0xc0000003对应的物理地址为0×3,0xc0000004对应的物理地址为0×4, ,逻辑地址与物理地址对应的关系为

  物理地址 = 逻辑地址 – 0xC0000000:这是内核地址空间的地址转换关系,注意内核的虚拟地址在高端,但是ta映射的物理内存地址在低端。

  实际上,内核直接映射空间也达不到 1G, 还得留点线性空间给内核动态映射空间 呢。

  因此,Linux 规定内核直接映射空间 最多映射 896M 物理内存。

  对于高端内存,可以通过 alloc_page() 或者其它函数获得对应的 page,但是要想访问实际物理内存,还得把 page 转为线性地址才行(为什么?想想 MMU 是如何访问物理内存的),也就是说,我们需要为高端内存对应的 page 找一个线性空间,这个过程称为高端内存映射。

  假 设按照上述简单的地址映射关系,那么内核逻辑地址空间访问为0xc0000000 ~ 0xffffffff,那么对应的物理内存范围就为0×0 ~ 0×40000000,即只能访问1G物理内存。若机器中安装8G物理内存,那么内核就只能访问前1G物理内存,后面7G物理内存将会无法访问,因为内核 的地址空间已经全部映射到物理内存地址范围0×0 ~ 0×40000000。即使安装了8G物理内存,那么物理地址为0×40000001的内存,内核该怎么去访问呢?代码中必须要有内存逻辑地址 的,0xc0000000 ~ 0xffffffff的地址空间已经被用完了,所以无法访问物理地址0×40000000以后的内存。

  显 然不能将内核地址空间0xc0000000 ~ 0xfffffff全部用来简单的地址映射。因此x86架构中将内核地址空间划分三部分:ZONE_DMA、ZONE_NORMAL和 ZONE_HIGHMEM。ZONE_HIGHMEM即为高端内存,这就是内存高端内存概念的由来。

  在x86结构中,三种类型的区域(从3G开始计算)如下:

(本文来源于图老师网站,更多请访问https://m.tulaoshi.com/fuwuqi/)

  ZONE_DMA 内存开始的16MB

  ZONE_NORMAL 16MB~896MB

  ZONE_HIGHMEM 896MB ~ 结束(1G)

  高端内存是指物理地址大于 896M 的内存。对于这样的内存,无法在内核直接映射空间进行映射。

  为什么?

  因为内核直接映射空间最多只能从 3G 到 4G,只能直接映射 1G 物理内存,对于大于 1G 的物理内存,无能为力。

  高端内存映射有三种方式:

  1、映射到内核动态映射空间

  这种方式很简单,因为通过 vmalloc() ,在内核动态映射空间申请内存的时候,就可能从高端内存获得页面(参看 vmalloc 的实现),因此说高端内存有可能映射到内核动态映射空间 中。

  2、永久内核映射

  如果是通过 alloc_page() 获得了高端内存对应的 page,如何给它找个线性空间?

  内核专门为此留出一块线性空间,从 PKMAP_BASE 到 FIXADDR_START ,用于映射高端内存。在 2.4 内核上,这个地址范围是 4G-8M 到 4G-4M 之间。这个空间起叫内核永久映射空间或者永久内核映射空间

  这个空间和其它空间使用同样的页目录表,对于内核来说,就是 swapper_pg_dir,对普通进程来说,通过 CR3 寄存器指向。

  通常情况下,这个空间是 4M 大小,因此仅仅需要一个页表即可,内核通过来 pkmap_page_table 寻找这个页表。

  通过 kmap(), 可以把一个 page 映射到这个空间来

  由于这个空间是 4M 大小,最多能同时映射 1024 个 page。因此,对于不使用的的 page,应该及时从这个空间释放掉(也除映射关就是解系),通过 kunmap() ,可以把一个 page 对应的线性地址从这个空间释放出来。

  3、临时映射

  内核在 FIXADDR_START 到 FIXADDR_TOP 之间保留了一些线性空间用于特殊需求。这个空间称为固定映射空间

  在这个空间中,有一部分用于高端内存的临时映射。

  这块空间具有如下特点:

  1、 每个 CPU 占用一块空间

  2、 在每个 CPU 占用的那块空间中,又分为多个小空间,每个小空间大小是 1 个 page,每个小空间用于一个目的,这些目的定义在 kmap_types.h 中的 km_type 中。

  当要进行一次临时映射的时候,需要指定映射的目的,根据映射目的,可以找到对应的小空间,然后把这个空间的地址作为映射地址。这意味着一次临时映射会导致以前的映射被覆盖。

  通过 kmap_atomic() 可实现临时映射。

  下图简单简单表达如何对高端内存进行映射

  Linux内存线性地址空间大小为4GB,分为2个部分:用户空间部分(通常是3G)和内核空间部分(通常是1G)。在此我们主要关注内核地址空间部分。

  内核通过内核页全局目录来管理所有的物理内存,由于线性地址前3G空间为用户使用,内核页全局目录前768项(刚好3G)除0、1两项外全部为0,后256项(1G)用来管理所有的物理内存。内核页全局目录在编译时静态地定义为swapper_pg_dir数组,该数组从物理内存地址0x101000处开始存放。

  由图可见,内核线性地址空间部分从PAGE_OFFSET(通常定义为3G)开始,为了将内核装入内存,从PAGE_OFFSET开始8M线性地址用来映射内核所在的物理内存地址(也可以说是内核所在虚拟地址是从PAGE_OFFSET开始的);接下来是mem_map数组,mem_map的起始线性地址与体系结构相关,比如对于UMA结构,由于从PAGE_OFFSET开始16M线性地址空间对应的16M物理地址空间是DMA区,mem_map数组通常开始于PAGE_OFFSET+16M的线性地址;从PAGE_OFFSET开始到VMALLOC_START – VMALLOC_OFFSET的线性地址空间直接映射到物理内存空间(一一对应影射,物理地址==线性地址-PAGE_OFFSET),这段区域的大小和机器实际拥有的物理内存大小有关,这儿VMALLOC_OFFSET在X86上为8M,主要用来防止越界错误;在内存比较小的系统上,余下的线性地址空间(还要再减去空白区即VMALLOC_OFFSET)被vmalloc()函数用来把不连续的物理地址空间映射到连续的线性地址空间上,在内存比较大的系统上,vmalloc()使用从VMALLOC_START到VMALLOC_END(也即PKMAP_BASE减去2页的空白页大小PAGE_SIZE(解释VMALLOC_END))的线性地址空间,此时余下的线性地址空间(还要再减去2页的空白区即VMALLOC_OFFSET)又可以分成2部分:第一部分从PKMAP_BASE到FIXADDR_START用来由kmap()函数来建立永久映射高端内存;第二部分,从FIXADDR_START到FIXADDR_TOP,这是一个固定大小的临时映射线性地址空间,(引用:Fixed virtual addresses are needed for subsystems that need to know the virtual address at compile time such as the APIC),在X86体系结构上,FIXADDR_TOP被静态定义为0xFFFFE000,此时这个固定大小空间结束于整个线性地址空间最后4K前面,该固定大小空间大小是在编译时计算出来并存储在__FIXADDR_SIZE变量中。

  正是由于vmalloc()使用区、kmap()使用区及固定大小区(kmap_atomic()使用区)的存在才使ZONE_NORMAL区大小受到限制,由于内核在运行时需要这些函数,因此在线性地址空间中至少要VMALLOC_RESERVE大小的空间。VMALLOC_RESERVE的大小与体系结构相关,在X86上,VMALLOC_RESERVE定义为128M,这就是为什么ZONE_NORMAL大小通常是16M到896M的原因。

展开更多 50%)
分享

猜你喜欢

解析Linux系统下的高端内存

服务器
解析Linux系统下的高端内存

Linux操作系统下的多线程编程详细解析

Linux Linux命令 Linux安装 Linux编程 Linux桌面 Linux软件 Linux内核 Linux管理
Linux操作系统下的多线程编程详细解析

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

详解Linux系统内存寻址的分页机制

服务器
详解Linux系统内存寻址的分页机制

详解Linux系统下的hosts文件

服务器
详解Linux系统下的hosts文件

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

如何在双系统下卸载Linux系统

Linux Linux命令 Linux安装 Linux编程 Linux桌面 Linux软件 Linux内核 Linux管理
如何在双系统下卸载Linux系统

Linux下sed命令使用全解析

服务器
Linux下sed命令使用全解析

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

iPhone6S查找我的iphone怎么用?

iPhone6S查找我的iphone怎么用?

苹果iPhone6S云备份怎么关闭

苹果iPhone6S云备份怎么关闭
下拉加载更多内容 ↓