买了《深入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. beta冲刺总结附(分工)-咸鱼

    冲刺链接 分工细则: 分配比例:前端:后台数据库+代码:服务器配置:测试=3:3:2:2 工作量权重比:   前端 后台 服务器 测试 翁陈华 0.9 0.1 0 0 黄紫仪 0.1 0.8 0 0 ...

  2. 【Alpha版本】冲刺阶段 - Day1 - 启航

    Alpha 阶段成员分工及任务量 成员 分工 任务量(小时) 袁逸灏 完成app用户车辆,子弹发射,背景移动,暂停界面,音乐界面,音乐查找,音乐播放 25 刘伟康 项目进度把控.分配任务.组织会议.整 ...

  3. Alpha冲刺No.3

    冲刺Day3 一.站立式会议 终于我们遇到了我们最艰难的时候,组员也反映每天做的事情越来越少,出现了问题越来越多. 人太少,时间太少,我们没有办法一个人花足够多的时间去钻研统一个问题,或许是所以组员的 ...

  4. Linux学习--进程创建

    进程创建 在Linux系统下,自己可以创建进程: 当进程执行时,它会被装载进虚拟内存,为程序变量分配空间,并把相关信息添到 task_struct里. 进程内存布局分为四个不同的段: • 文本段,包含 ...

  5. Flask 扩展 缓存

    如果同一个请求会被多次调用,每次调用都会消耗很多资源,并且每次返回的内容都相同,就该使用缓存了 自定义缓存装饰器 在使用Flask-Cache扩展实现缓存功能之前,我们先来自己写个视图缓存装饰器,方便 ...

  6. python之路--day13---函数--三元表达式,递归,匿名函数,内置函数-----练习

    1.文件内容如下,标题为:姓名,性别,年纪,薪资 egon male 18 3000 alex male 38 30000 wupeiqi female 28 20000 yuanhao female ...

  7. New UWP Community Toolkit - AdaptiveGridView

    概述 UWP Community Toolkit  中有一个自适应的 GridView 控件 - AdaptiveGridView,本篇我们结合代码详细讲解  AdaptiveGridView 的实现 ...

  8. pythoncharm 中解决启动server时出现 “django.core.exceptions.ImproperlyConfigured: Requested setting DEBUG, but settings are not configured”的错误

    背景介绍 最近,尝试着用pythoncharm 这个All-star IDE来搞一搞Django,于是乎,下载专业版,PJ等等一系列操作之后,终于得偿所愿.可以开工了. 错误 在园子里找了一篇初学者的 ...

  9. 算法题丨3Sum

    描述 Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all ...

  10. Web系统Login拦截器

    所需要导入的包类:import org.springframework.web.servlet.HandleInterceptor;(拦截器要继承该类) public class loginInter ...