4月14日

很多硬件的功能,物尽其用却未必好过软实现,Linux出于可移植性及其它原因,常常选择不去过分使用硬件特性。

比如 Linux只使用四个segment,分别是__USER_CS、__USER_DS、__KERNEL_CS、__KERNEL_DS,因为Paging可以完成segmentation的工作,而且可以完成的更好。而且这样简化了很多,统一了逻辑地址和线性地址。

而TSS存在每CPU一个的GDT中,虽然每个process的TSS不同,但Linux 2.6却不利用其中的hardware context switch(虽然低版本使用)以一个far jmp来实现任务转换,而用一系列的mov指令来实现。这样做的原因是:

1、可以检验ds和es的值,以防恶意的forge。
2、硬转换和软转换所用时间相近,而且硬转换是无法再优化的,软转换则可以。

4月15日

Paging也就是将linear地址转成物理地址的机制。

内存被视为一堆4k的小page frame(就像空的格子),在归OS管的Paging机制的苟延残喘下,仿佛地存放着多于page frame数目的page(数据)。要通过两层索引(directroy和table)来寻到page,再加offset寻到址。这两层索引中的entry包含一些标志表明该page在不在内存里,是否被改写过,最近是否访问过,以及读/写访问权限。

如果page entry里的Page Size标志和cr4的PSE标志设置了的话(Extended Paging),就是4M一片page frame,这样就只用directory一层索引了。

从奔腾pro开始,adress针脚非常神奇地从32增加到36,有了一个叫做PAE的机制,它启用(cr4的PAE标志设置)的时候就是2M一片page frame了。这样可以寻址64GB,远远超越了没启用前4GB的理论极限(实际极限1GB)。但这样的寻址非常别扭,因为物理地址虽然因此变成了36位,线性地址仍是32位,要想寻址超过4GB,要用cr3去指向不同的PDPT或在31-30bit指定PDPT中entry。不过,更郁闷的是,这并不能改变process的地址空间4GB的限制,仅仅是内核可以用这么多内存来运行更多的process。

在64位机器上,由于如果只用两层的话,索引条目会太多,严重消耗内存,所以只好再加层数,alpha、ia64、ppc64、sh64都是3层(虽然每层bit数不一),x86_64非常神奇地用了4层。

Paging换的是page,Cache换的是line。但是如何在Cache中确定某个内存地址在不在呢?或者说,某内存地址附近的数据,放在Cache中什么位置好呢?不能一对一映射过来(direct mapping),这样会导致巨大的Cache;也不能随意放(fully associative)然后在旁边标记(tag)说是什么地址附近的,这样会导致每次找Cache都是线性查找。一个浪费空间一个浪费时间,因此有一种折衷叫做N-Way Set Associative,有点像Hash。首先把Cache分成很多个N line的集合,然后弄个hash函数把一个地址唯一地映射到某个集合里,之后至于放在这N line中的哪一line就无所谓了。找的时候,先一瞬间找到集合,然后对N line进行线性查找。

读的时候,自然有cache hit和cache miss。对于写操作,cache hit的话,可能有两种不同的处理方法:write-through(Cache和RAM都写)和wirte-back(line换出时写RAM)。Linux清空PCD (Page Cache Disable)和PWT(Page Write-Through),永远启用cache并使用write-back策略。

哈哈,TLB(Translation Lookaside Buffers )解决了我心中的一大疑问:每次寻址(将linear翻译成physical),都要非常艰辛地查directroy和table,访问多次RAM(你以为这些东西不是放在RAM里啊?!),岂不累死。幸好,我们有TLB,这样最近翻译的成果就可以缓存在里面,这样就省得每次翻译啦。

4月17日

Linux用了四层索引来做Paging。这样既可以通过隐藏掉中间两层来做无PAE的32位paging,又可以隐藏掉pud来支持有PAE的3位paging,还可以支持64位的paging。

pte_t     Page Table

pmd_t   Page Middle Directory

pud_t    Page Upper Directory

pgd_t    Page Global Directory

每个进程的内存空间中0到PAGE_OFFSET(0xc0000000,即3G)-1是用户空间,PAGE_OFFSET到0xffffffff(4G)则是内核空间(只有内核态才能寻址)。

启动的时候,Linux问BIOS内存格局如何,保留第1个MB(machine_specific_memory_setup()),然后把自己放在第2个MB开始的地方(从_text到_etext是内核代码,从_etext到_edata是初始化了的内核数据)。

在这个过程中:

Linux首先建立初始(provisional)页表(startup_32()),使RAM前8M(两页)可以用两种方式寻址,用来存放最小的自己(text、data、初始页表、128k的堆空间)。

初始pgd放在swapper_pg_dir中。所有项为0,但0、1与0x300、0x301分别完成线性地址的前8M和3G+8M到物理地址前8M的映射。

接着,Linux建立最终页表。

线性地址最高的128M保留给Fix-Mapped Linear Addresses和Noncontiguous Memory Allocation用,所以,最终页表只需要把PAGE_OFFSET后面的896M映射到物理地址的前896M。剩余RAM由Dynamic Remapping来完成。然后用zap_low_mapping()把原先那个初始页表清掉。

paging_init()会执行:

pagetable_init() //一个循环,初始化了swapper_pg_dir

cr3 <- swapper_pg_dir

cr4 |= PAE

__flush_tlb_all()

Linux利用CPU有限的指令和行为模式,实现了一系列操纵tlb的函数,应用于不同的情境。

值得一记的是Lazy TLB模式,在多CPU系统中,它可以避免无意义的TLB刷新。

[轉]Linux 2.6内核笔记【内存管理】的更多相关文章

  1. Linux内核笔记--内存管理之用户态进程内存分配

    内核版本:linux-2.6.11 Linux在加载一个可执行程序的时候做了种种复杂的工作,内存分配是其中非常重要的一环,作为一个linux程序员必然会想要知道这个过程到底是怎么样的,内核源码会告诉你 ...

  2. Linux内核笔记——内存管理之slab分配器

    内核版本:linux-2.6.11 内存区和内存对象 伙伴系统是linux用于满足对不同大小块内存分配和释放请求的解决方案,它为slab分配器提供页框分配请求的实现. 如果我们需要请求具有连续物理地址 ...

  3. Linux内核笔记——内存管理之块内存分配

    内核版本:linux-2.6.11 伙伴系统 伙伴系统是linux用于满足对不同大小块物理内存分配和释放请求的解决方案. 内存管理区 linux将物理内存分成三个内存管理区,分别为ZONE_DMA Z ...

  4. 24小时学通Linux内核之内存管理方式

    昨天分析的进程的代码让自己还在头昏目眩,脑子中这几天都是关于Linux内核的,对于自己出现的一些问题我会继续改正,希望和大家好好分享,共同进步.今天将会讲诉Linux如何追踪和管理用户空间进程的可用内 ...

  5. Linux内核之内存管理

    Linux内核之内存管理 Linux利用的是分段+分页单元把逻辑地址转换为物理地址; RAM的某些部分永久地分配给内核, 并用来存放内核代码以及静态内核数据结构; RAM的其余部分称动态内存(dyna ...

  6. [转]linux内核分析笔记----内存管理

    转自:http://blog.csdn.net/Baiduluckyboy/article/details/9667933 内存管理,不用多说,言简意赅.在内核里分配内存还真不是件容易的事情,根本上是 ...

  7. Linux内核之内存管理完全剖析

    linux虚拟内存管理功能 ? 大地址空间:? 进程保护:? 内存映射:? 公平的物理内存分配:? 共享虚拟内存.实现结构剖析   (1)内存映射模块(mmap):负责把磁盘文件的逻辑地址映射到虚拟地 ...

  8. 深入理解Linux内核-内存管理

    内核如果给自己分配动态内存 动态内存:RAM的某些部分被永久打分配给内核,用来存放内核代码以及静态内核数据结构:剩余的部分被称为动态内存 连续物理内存区管理: 页框管理:1.页大小的选择,通常情况下主 ...

  9. Linux 内核开发 - 内存管理

    1.1什么是内存管理 内存管理是对计算机内存进行分配和使用的技术.内存管理主要存在于多任务的操作系统中,因为内存资源极其有限.须要在不同的任务之间共享内存,内存管理的存在就是要高效.高速的非配内存,并 ...

随机推荐

  1. C++中逻辑操作符的重载分析

    1,逻辑运算符的原生语义: 1,操作数只有两种值(true 和 false): 1,C 语言中只有非 0 和 0: 2,逻辑表达式不用完全计算就能确定最终值: 1,短路规则: 3,最终结果只能是 tr ...

  2. Java面试宝典(7)混合2

    数据库 & XML & 流行的框架与新技术 & 软件工程与设计模式 & J2EE & EBJ & webservice & 其他 pageSiz ...

  3. python模块打补丁

    先自定义两个模块,然后,我们调用模块时,用打补丁方式,改写mod_1.py模块.为mod_2.py内容:其实这就相当于,在不改动mod_1.py模块的前提下,打上补丁. 写这个主要是gevent协程的 ...

  4. 5分钟搞定android混淆(转)

    转自:https://www.jianshu.com/p/f3455ecaa56e 前言 混淆是上线前挺重要的一个环节.android使用的ProGuard,可以起到压缩,混淆,预检,优化的作用.但是 ...

  5. 解决mac pro 软件损坏

    1,打开终端 2,输入 sudo spctl --master-disable 3,打开系统偏好设置——>安全与隐私——>勾选任何来源

  6. reduce 好东西

    reduce()方法可以搞定的东西,for循环,或者forEach方法有时候也可以搞定,那为啥要用reduce()?这个问题,之前我也想过,要说原因还真找不到,唯一能找到的是:通往成功的道路有很多,但 ...

  7. idea maven打jar包

    双击clean install 会在根目录targer生成文件(注意删除test和替换yml文件)

  8. org.apache.http.client.HttpClient使用方法

    一.org.apache.commons.httpclient和org.apache.http.client区别(转)   官网说明: http://hc.apache.org/httpclient- ...

  9. python的logging,将log保存到文件

    import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(line ...

  10. python--闭包函数、装饰器

    先来点补充. x= def foo(): print(x) x= foo() 结果: x= def foo(): global x x= print(x) foo() print(x) 结果: x= ...