买了《深入Linux内核架构》这本书准备了解一下linux内核机制。但是最开始看了十几页感觉看着很累,本来都准备弃了

过了段时间看见一个面经有linux内核的内容,于是就照着那个先把内存管理学习了下。静下心来看发现这本书还是不错,我跳过了很多细节部分,先对内核管理有个大致了解。

水印+冷热页+伙伴系统+slab缓存


通常用户和内核以3:1的比例划分虚拟地址

内存组织:

首先,内存划分为节点,每个节点关联到一个处理器,即pg_data_t

在把各个节点划分为内存域(zone),对内存进行进一步的细分。各个内存域关联到一个页帧(物理内存页)的数组。

内存域zone:

对zone结构可能有不同cpu同时访问,使用锁。因为内核对该结构访问比较频繁,经常获取它的两个自旋锁。

(如果对象已被加锁,线程不会陷入睡眠(进入等待队列),而是一直循环。 常用于多处理器服务器)

zone中涉及:

1.pages_min,pages_high,pages_low水印(unsigned long)

  • 如果空闲页多余high,状态理想
  • 如果低于low,内核开始将页换出硬盘
  • 如果低于min,那么页回收就会比较有压力

2.lowmem_reserve各个内存域无论如何都不能失败的关键性内存分配(unsigned long)

3.pageset数组,实现冷、热页帧(per_cpu_pageset)

4.free_area部分,实现伙伴系统(free_area)

伙伴系统:

把所有的空闲页框分组为11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512和1024个连续页框的页框块。

如果要申请一个256个页的块,那么会先在256对应的链表中查找,否则在512中进行查找,以此类推。(如果512分配了256,那么剩下未使用的会放到对应链表上面)

如果有连续的块,会进行合并。

只需要内存区第一个page实例,长度可以根据所在的页表推导


在系统运行后经常会产生内存碎片:



在linux中对页面做个归类:

  • 不可移动页面 unmoveable:在内存中位置必须固定,无法移动到其他地方,核心内核分配的大部分页面都属于这一类。
  • 可回收页面 reclaimable:不能直接移动,但是可以回收,因为还可以从某些源重建页面,比如映射文件的数据属于这种类别,kswapd会按照一定的规则,周期性的回收这类页面。
  • 可移动页面 movable:可以随意的移动。属于用户空间应用程序的页属于此类页面,它们是通过页表映射的,因此我们只需要更新页表项,并把数据复制到新位置就可以了,当然要注意,一个页面可能被多个进程共享,对应着多个页表项。

碎片处理:

1.依靠可移动性组织页:

我们可以根据页的可移动性进行分类,三类page放在不同的链表上,避免不同类型页面相互干扰。(即不可移动页不能位于可以移动内存中间)

2.虚拟可移动内存域:

手动打开,可以将内存划分为两个内存域,用于不同的匹配,需要手动指示大小。


slab:

用于比完整页面小得多的内存块。slab也用做一个缓存,用于存储经常分配释放的对象。

(每个缓存负责一个对象类型)

例:为管理进程关联的文件系统数据,内存要生成fs_struct实例,同样需要经常回收(进程结束时)。

slab不会将释放的内存块马上返回给伙伴系统(保存在一个内部列表中),如果要求为该类分配一个实例时,会使用最近释放的,因此其驻留在cpu高速缓存中的概率会大大提高。//先进后出

。。

slab主要结构:



kmem_cache是一个cache_chain的链表,描述了一个高速缓存,每个高速缓存包含了一个slabs的列表,这通常是一段连续的内存块。存在3种slab:slabs_full(完全分配的slab),slabs_partial(部分分配的slab),slabs_empty(空slab,或者没有对象被分配)。slab是slab分配器的最小单位,在实现上一个slab有一个货多个连续的物理页组成(通常只有一页)。单个slab可以在slab链表之间移动,例如如果一个半满slab被分配了对象后变满了,就要从slabs_partial中被删除,同时插入到slabs_full中去。

  • 首先要查看inode_cachepslabs_partial链表,如果slabs_partial非空,就从中选中一个slab,返回一个指向已分配但未使用的inode结构的指针。完事之后,如果这个slab满了,就把它从slabs_partial中删除,插入到slabs_full中去,结束;
  • 如果slabs_partial为空,也就是没有半满的slab,就会到slabs_empty中寻找。如果slabs_empty非空,就选中一个slab,返回一个指向已分配但未使用的inode结构的指针,然后将这个slab从slabs_empty中删除,插入到slabs_partial(或者slab_full)中去,结束;
  • 如果slabs_empty也为空,那么没办法,cache内存已经不足,只能新创建一个slab了。

页的分配:

分配alloc_page_node:

1.通常会想通过标志来判断页是否可以分配(水印什么的)。默认情况下只有在high以上的内存域才能分配页。

可以通过相应的设置放宽限制。但是会检测空闲页数量是否小于lowmem_reserve(必要内存)和最小值。

//空闲内存可以通过 缩减内核缓存和页面回收获得。写回or换出很少使用的页,kswapd守护进程发起。

2.如果交换进程唤醒后,会再次尝试寻找合适的内存块。如果失败:

  • 如果设置了相应的标志位 GPF_NOMEMALLOC,禁止使用紧急分配链表,会失败
  • 否则会在忽略水印的情况下进行分配

失败之后,分页机制会尝试使用try_to_free_pages(为了获取内存还需为函数额外分配内存TAT)

找出最近不十分活跃的页,将其写到交换区,腾出物理内存。

为了防止其递归调用,会设置MEMALLOC标识符,允许最后无视水印。

。。

内核如果可能影响VFS层并且没有设置相应的标志符,那么可以会判断杀死一个进程能否获得

相应的连续内存区。可以的话会通过out_of_memory(OOM killer)干掉进程。

。。

在找到合适的内存域之后,会按伙伴系统的方式从free_lists上面移除页。

如果只分配一页(0阶的情况),内核会进行优化,直接取自per-CPU缓存。

  • 检查per-CPU是否有指定迁移类型(可移动什么的)的页
  • 如果找不到,向缓存中添加合适迁移类型页,再取出一页(从伙伴系统)

如果特定的迁移上面没有内存可以用了,那么尝试从其他的类型中来分配。

不过会(大->小),选取一个大的整块内存给它。

最后,如果所有分配阶和迁移类型都无法满足,那么尝试从MIGRATE_RESERVE

在获取到分配的页帧之后,如果需要将其加入页缓存,通过radix_tree_insert将与其相关的page实例插入地址空间的基数树

释放页:

如果是单页则判断per-CPU缓存中的页数量过多,则会将一批内存页归还给伙伴系统。(惰性合并)

在将页归还给伙伴系统的时候会进行合并:

  • 计算出释放页当前阶的伙伴( page_index ^ (1 << order) , 10 ^ (1 << 0) = 1)
  • 判断这个页是否是空闲的
  • 如果是则将其临时从伙伴系统移除并清除标志位和private数据(分配阶)
  • 合并后重复判断


参考:

http://www.codeceo.com/article/linux-cold-hot-page.html

http://www.cnblogs.com/wangzahngjun/p/4977425.html

http://blog.csdn.net/gatieme/article/category/6393814

《深入Linux内核架构》

内存管理——linux内核学习的更多相关文章

  1. Linux内核学习笔记-2.进程管理

    原创文章,转载请注明:Linux内核学习笔记-2.进程管理) By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...

  2. Linux 内核学习的经典书籍及途径

    from:http://www.zhihu.com/question/19606660 知乎 Linux 内核学习的经典书籍及途径?修改 修改 写补充说明 举报   添加评论 分享 • 邀请回答   ...

  3. 关于Linux内核学习的误区以及相关书籍介绍

    http://www.hzlitai.com.cn/article/ARM9-article/system/1605.html 写给Linux内核新手-关于Linux内核学习的误区 先说句正经的:其实 ...

  4. Linux内核学习笔记-1.简介和入门

    原创文章,转载请注明:Linux内核学习笔记-1.简介和入门 By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...

  5. Linux内核学习趣谈

    本文原创是freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/9304991 从大二开始学习Linux内核,到现在已经 ...

  6. Linux 内核学习经验总结

    Linux 内核学习经验总结 学习内核,每个人都有自己的学习方法,仁者见仁智者见智.以下是我在学习过程中总结出来的东西,对自身来说,我认为比较有效率,拿出来跟大家交流一下. 内核学习,一偏之见:疏漏难 ...

  7. Linux内核分析——Linux内核学习总结

    马悦+原创作品转载请注明出处+<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 Linux内核学习总结 一 ...

  8. Linux内核学习总结(final)

    Linux内核学习总结 符钰婧 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ...

  9. 关于Linux内核学习的一点点总结

    关于Linux内核学习的一点点总结 关键词:Linux, 操作系统,内核 博客列表 由反汇编C程序来理解计算机是如何工作的 通过分析一个简化版时间片轮转多道程序内核代码来认识操作系统中的进程调度 通过 ...

随机推荐

  1. bug终结者 团队作业第二周

    bug终结者 团队作业第二周 我们小组选取游戏"开心消消乐",回答问题: 1. 此类软件是什么时候开始出现的, 这些软件是怎么说服你(陌生人)成为他们的用户的? 他们的目标都是盈利 ...

  2. 关于python中的operator.itemgetter()函数的用法

    1. operator.itemgetter(num)函数 表示对对象的第num维数据进行操作获取. >>>import operator >>>a = [1, 2 ...

  3. GNU/Hurd笔记整理

    patch 0 关于文件锁支持的解决方案,大部分是由Neal Walfield在2001年完成的.这些补丁由Marcus Brinkmann发表,随后被Michael Banck于2002年修改了部分 ...

  4. Project facet is Java version 1.7 is not spported

    在移植eclipse项目时,如果遇到 "Project facet Java version 1.7 is not supported." 项目中的jdk1.7不支持.说明项目是其 ...

  5. recompose mapProps

    mapProps介绍 mapProps函数接收一个函数参数,这个函数参数会返回一个对象用作为接下来的组件的props.组件接收到的props只能是通过mapProps函数参数返回的对象,其他的prop ...

  6. 第一次PTA作业

    题目6-1拆分实数整数及小数部分 1设计思路 (1) 第一步:阅读题目要求及所给部分. 第二步:根据题意补全相应函数. (2)流程图 无 2.实验代码 #include <stdio.h> ...

  7. IIS 配置 FTP 网站

    在 服务器管理器 的 Web服务器IIS 上安装 FTP 服务 在 IIS管理器 添加FTP网站 配置防火墙规则 说明:服务器环境是Windows Server 2008 R2,IIS7.5. 1. ...

  8. Python内置函数(33)——any

    英文文档: any(iterable) Return True if any element of the iterable is true. If the iterable is empty, re ...

  9. [52ABP实战课程系列]Docker&Ubuntu从入门到实战开课啦~

    任何的课程都逃不开理论的支持 久等了各位,在Asp.NET Core2.0 项目实战入门视频课程结束后,根据发起的投票信息.Docker 排在首位.按照结果,我们开始进行Docker视频课程的录制. ...

  10. android studio 何如修改报名

    1. 重命名办法,网上很多见 2. 对于需要重新修改包名的级别的 a. 修改package 和 gradle 的包名,对应一致. b. 修改R 所在包名,使用crtl+n修改R文件的路径 c. 手动首 ...