前面的文章介绍了Linux中虚拟地址空间的管理,本文将讨论Linux系统对物理内存的管理。

NUMA

所谓物理内存,就是安装在机器上的,实打实的内存设备(不包括硬件cache),被CPU通过总线访问。在多核系统中,如果物理内存对所有CPU来说没有区别,每个CPU访问内存的方式也一样,则这种体系结构被称为Uniform Memory Access(UMA)。

如果物理内存是分布式的,由多个cell组成(比如每个核有自己的本地内存),那么CPU在访问靠近它的本地内存的时候就比较快,访问其他CPU的内存或者全局内存的时候就比较慢,这种体系结构被称为Non-Uniform Memory Access(NUMA)。

以上是硬件层面上的NUMA,而作为软件层面的Linux,则对NUMA的概念进行了抽象。即便硬件上是一整块连续内存的UMA,Linux也可将其划分为若干的node。同样,即便硬件上是物理内存不连续的NUMA,Linux也可将其视作UMA。

所以,在Linux系统中,你可以基于一个UMA的平台测试NUMA上的应用特性。从另一个角度,UMA就是只有一个node的特殊NUMA,所以两者可以统一用NUMA模型表示。

在NUMA系统中,当Linux内核收到内存分配的请求时,它会优先从发出请求的CPU本地或邻近的内存node中寻找空闲内存,这种方式被称作local allocation,local allocation能让接下来的内存访问相对底层的物理资源是local的。

每个node由一个或多个zone组成(我们可能经常在各种对虚拟内存和物理内存的描述中迷失,但以后你见到zone,就知道指的是物理内存),每个zone又由若干page frames组成(一般page frame都是指物理页面)。

Page Frame

虽然内存访问的最小单位是byte或者word,但MMU是以page为单位来查找页表的,page也就成了Linux中内存管理的重要单位。包括换出(swap out)、回收(relcaim)、映射等操作,都是以page为粒度的。

因此,描述page frame的struct page自然成为了内核中一个使用频率极高,非常重要的结构体,来看下它是怎样构成的(为了讲解需要并非最新内核代码):

struct page {unsigned long flags;atomic_t count;  atomic_t _mapcount; struct list_head lru;struct address_space *mapping;unsigned long index;         ...
} 

  • flags表示page frame的状态或者属性,包括和内存回收相关的PG_active, PG_dirty, PG_writeback, PG_reserved, PG_locked, PG_highmem等。其实flags是身兼多职的,它还有其他用途,这将在下文中介绍到。
  • count表示引用计数。当count值为0时,该page frame可被free掉;如果不为0,说明该page正在被某个进程或者内核使用,调用page_count()可获得count值。
  • _mapcount表示该page frame被映射的个数,也就是多少个page table entry中含有这个page frame的PFN。
  • lru是"least recently used"的缩写,根据page frame的活跃程度(使用频率),一个可回收的page frame要么挂在active_list双向链表上,要么挂在inactive_list双向链表上,以作为页面回收的选择依据,lru中包含的就是指向所在链表中前后节点的指针(参考这篇文章)。
  • 如果一个page是属于某个文件的(也就是在page cache中),则mapping指向文件inode对应的address_space(这个结构体虽然叫address_space,但并不是进程地址空间里的那个address space),index表示该page在文件内的offset(以page size为单位)。

有了文件的inode和index,当这个page的内容需要和外部disk/flash上对应的部分同步时,才可以找到具体的文件位置。如果一个page是anonymous的,则mapping指向表示swap cache的swapper_space,此时index就是swapper_space内的offset。

事实上,现在最新Linux版本的struct page实现中大量用到了union,也就是同一个元素在不同的场景下有不同的意义。这是因为每个page frame都需要一个struct page来描述,一个page frame占4KB,一个struct page占32字节,那所有的struct page需要消耗的内存占了整个系统内存的32/4096,不到1%的样子,说小也小,但一个拥有4GB物理内存的系统,光这一项的开销最大就可达30多MB。

如果能在struct page里省下4个字节,那就能省下4多MB的内存空间,所以这个结构体的设计必须非常考究,不能因为多一种场景的需要就在struct page中增加一个元素,而是应该尽量采取复用的方式。

需要注意的是,struct page描述和管理的是这4KB的物理内存,它并不关注这段内存中的数据变化。

Zone

因为硬件的限制,内核不能对所有的page frames采用同样的处理方法,因此它将属性相同的page frames归到一个zone中。对zone的划分与硬件相关,对不同的处理器架构是可能不一样的。

比如在i386中,一些使用DMA的设备只能访问0~16MB的物理空间,因此将0~16MB划分为了ZONE_DMA。ZONE_HIGHMEM则是适用于要访问的物理地址空间大于虚拟地址空间,不能建立直接映射的场景。除开这两个特殊的zone,物理内存中剩余的部分就是ZONE_NORMAL了。

在其他一些处理器架构中,ZONE_DMA可能是不需要的,

ZONE_HIGHMEM也可能没有。比如在64位的x64中,因为内核虚拟地址空间足够大,不再需要ZONE_HIGH映射,但为了区分使用32位地址的DMA应用和使用64位地址的DMA应用,64位系统中设置了ZONE_DMA32和ZONE_DMA。

所以,同样的ZONE_DMA,对于32位系统和64位系统表达的意义是不同的,ZONE_DMA32则只对64位系统有意义,对32位系统就等同于ZONE_DMA,没有单独存在的意义。

此外,还有防止内存碎片化的ZONE_MOVABLE和支持设备热插拔的ZONE_DEVICE。可通过“cat /proc/zoneinfo |grep Node”命令查看系统中包含的zones的种类。

Zone虽然是用于管理物理内存的,但zone与zone之间并没有任何的物理分割,它只是Linux为了便于管理进行的一种逻辑意义上的划分。Zone在Linux中用struct zone表示(以下为了讲解需要,调整了结构体中元素的顺序):

struct zone {spinlock_t         lock;unsigned long      spanned_pages;unsigned long      present_pages; unsigned long      nr_reserved_highatomic;    atomic_long_t      managed_pages;struct free_area   free_area[MAX_ORDER];unsigned long      _watermark[NR_WMARK];long               lowmem_reserve[MAX_NR_ZONES];atomic_long_t      vm_stat[NR_VM_ZONE_STAT_ITEMS];unsigned long      zone_start_pfn;struct pglist_data *zone_pgdat;struct page        *zone_mem_map;...
} 

  • lock是用来防止并行访问struct zone的spin lock,它只能保护struct zone这个结构体哈,可不能保护整个zone里的所有pages。
  • spanned_pages是这个zone含有的总的page frames数目。在某些体系结构(比如Sparc)中,zone中可能存在没有物理页面的"holes",spanned_pages减去这些holes里的absent pages就是present_pages

nr_reserved_highatomic是为某些场景预留的内存(参考本系列的第三篇文章),managed_pages是由buddy内存分配系统管理的page frames数目,其实也就是present_pages减去reserved pages。

  • free_area由free list空闲链表构成,表示zone中还有多少空余可供分配的page frames。_watermark有min(mininum), low, high三种,可作为启动内存回收的判断标准(详情请参考这篇文章)。

lowmem_reserve是给更高位的zones预留的内存(更详细的介绍请参考这篇文章)。vm_stat作为zone的内存使用情况的统计信息,是“/proc/zoneinfo”的数据来源。

  • zone_start_pfn是zone的起始物理页面号,zone_start_pfn+spanned_pages就是该zone的结束物理页面号。zone_pgdat是指向这个zone所属的node的。zone_mem_map指向由struct page构成的mem_map数组。

因为内核对zone的访问是很频繁的,为了更好的利用硬件cache来提高访问速度,struct zone中还有一些填充位,用于帮助结构体元素的cache line对齐。这和struct page对内存精打细算的使用形成了鲜明的对比,因为zone的种类很有限,一个系统中一共也不会有多少个zones,struct zone这个结构体的体积大点也没有什么关系。

关于前面提到的node的介绍,请看下文分解。

原创文章,转载请注明出处。

linux mmu的实现的讲解_Linux中的物理内存管理 [一]相关推荐

  1. linux mmu的实现的讲解_Linux中的段

    Intel 微处理器的段机制是从8086 开始提出的, 那时引入的段机制解决了从CPU 内部 16 位地址到20 位实地址的转换.为了保持这种兼容性,386 仍然使用段机制,但比以前复杂. 因此,Li ...

  2. linux mmu的实现的讲解_MMU解读

    转:https://blog.csdn.net/yueqian_scut/article/details/24816757 mmu页表也是放在内存中,mmu里有一个寄存器存放页表首地址,从而找到页表( ...

  3. linux java 获取路径怎么写_linux中java获取路径的实例代码

    linux中java获取路径怎么写? 在Unix/Linux中,路径的分隔采用正斜"/",比如"cd /home/java". 在java的代码开发中 \ 是代 ...

  4. linux怎么退出vim编辑模式_Linux中的vim编辑器使用

    Vim 介绍 Vim 是全屏幕纯文本编辑器,是vi编辑器的升级版. vim 不仅仅兼容vi所有的命令,而且高亮显示,vi只能运行在 UNIX 和 Linux 中,而 vim 可以跨平台运行在Windo ...

  5. linux下数据库mysql命令大全_linux中mysql数据库操作命令大全有哪些

    Linux中经常会对数据库进行操作,除了在软件应用中操作,还能使用命令实现,具体有哪些命令呢?下面由学习啦小编为大家整理了linux重启mysql数据库命令的相关知识,希望对大家有帮助! linux中 ...

  6. linux子线程运行的函数_Linux中线程使用详解

    4. 线程的属性 前面还说到过线程创建的时候是有属性的,这个属性由一个线程属性对象来描述.线程属性对象由pthread_attr_init()接口初始化,并由pthread_attr_destory( ...

  7. linux忘记mysql登录用户密码_linux中忘记mysql用户root密码解决方案

    1.vim /etc/my.cnf [mysqld] skip-grant-tables ##追加此行,跳过权限表, 2.重启mysql systemctl restart mysqld 3.mysq ...

  8. linux java 获取路径怎么写_linux中java获取路径怎么写?

    linux中java获取路径怎么写? 在Unix/Linux中,路径的分隔采用正斜"/",比如"cd /home/java". 在java的代码开发中 是代表转 ...

  9. linux rsa登录改密码登录_LINUX中RSA认证登录SSH(不需要输入密码登录)2种方法

    方法一, 有的时候经常需要登录ssh,每次都需要输入密码,会比较繁琐.所以设置了一下使用RSA公钥认证的方式登录Linux. 首先需要在服务器端设置/etc/ssh/sshd_config # vim ...

最新文章

  1. 【matlab】第三章数组和数组的运算
  2. android 自定义折线图
  3. UNPIVOT的详细说明
  4. 同一个硬盘装多个linux系统,在一个硬盘上安装两个Linux操作系统方法
  5. 数学建模债券投资组合_1998年全国大学生数学建模竞赛题目A题投资的收益和风险.PDF...
  6. [蓝桥杯]算法提高 道路和航路(spfa+deque+快读优化)
  7. ansible 修改文件变量_基于ansible的批量配置生成
  8. IOS – OpenGL ES 调节图像对比度 GPUImageContrastFilter
  9. 嵌入式linux 试卷,嵌入式Linux模拟试卷
  10. 数据的四大特征_大数据
  11. 小白白红队初成长(5)win权限维持
  12. 电信跨域跨系统业务实践
  13. 计算机二级office知识大纲,2017计算机二级考试MS office 考试内容大纲
  14. NLTK的安装/对象/词库/分词/词性标注/分块
  15. 优化js脚本设计,防止浏览器假死
  16. 用easyx图形库做一个简单的c++小游戏---迷宫游戏
  17. Hides for Mac(隐藏程序Mac老板键)
  18. 没读过一本励志鸡汤文,说什么在北上广打拼的人
  19. 【行业篇】一、汽车软硬架构 - EEA/SOA
  20. 华为防火墙ftp_华为防火墙如何配置ftp服务器映射到外网

热门文章

  1. python 语言教程(4)列表常用方法
  2. C++ Primer 5th笔记(1)chapter 1
  3. 设计模式--工厂方法(Factory Method)模式
  4. shell中三种引号的区别
  5. HTTP中response响应数据获取
  6. python网络编程—TCP协议(一)
  7. django中序列化、反序列化过程
  8. swoole mysql 协程_关于协程的优点以及swoole 协程的用法
  9. agilebpm脑图_干货基于SpringBoot2开发的Activiti引擎流程管理项目脚手架
  10. (52)系统调用阶段测试——基于 SSDT HOOK 的 FindWindowA 监视器