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. 用MR生成HFile文件格式后,数据批量导入HBase

    环境hadoop cdh5.4.7 hbase1.0.0 测试数据: topsid  uid roler_num typ 10 111111 255 0 在Hbase 创建t2数据库: create ...

  2. vue - blog开发学习4

    1.新建页面的修改,集成富文本编辑 edit-post.vue(新建和修改都用该组件) <template> <div class="editor"> &l ...

  3. Autofac基本使用

    原文:Autofac基本使用 AutoFac是.net平台下的IOC容器产品,它可以管理类之间的复杂的依赖关系.在使用方面主要是register和resolve两类操作. 这篇文章用单元测试的形式列举 ...

  4. Sql Server Management Studio 18 打开闪退问题

    解决方案 找到MSSMS安装位置,例如我是安装到了D:\Program Files (x86)\Microsoft SQL Server Management Studio 18 将D:\Progra ...

  5. Ubuntu18.04+CUDA9.0+cuDNN7.1.3+openface安装总结

    目录 前言 编译工具CMake C++标准库安装 下载OpenFace代码 OpenCV安装 luarocks-Lua 包管理器,提供一个命令行的方式来管理 Lua 包依赖.安装第三方 Lua 包等功 ...

  6. 2019-10-31-WPF-等距布局

    title author date CreateTime categories WPF 等距布局 lindexi 2019-10-31 9:0:2 +0800 2018-2-21 17:3:4 +08 ...

  7. Codeforces 364E 分治

    题意:给你一个01矩阵,问此矩阵有多少个和恰好为k的子矩形. 思路:分治,对于当前矩形,用一条中线把矩形分成两半,分治之后计算跨过中线的矩形个数.更具体的来说(假设划了一条水平中线),我们枚举矩形左右 ...

  8. Table 'jiang.hibernate_sequence' doesn't exist

    spring+struts2+hibernate 运行报错 Table 'jiang.hibernate_sequence' doesn't exist 解决方法 一. 在hibernate.cfg. ...

  9. jsp中$使用不了

    导入了jstl <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>为啥 ...

  10. UNP学习 路由套接口

    一.概述 在路由套接口中支持三种类型的操作: 1.进程能通过写路由套接口想内核发消息.举例:路径就是这样增加和删除的. 2.进程能在路由套接口上从内核读消息. 3.进程可以用sysctl函数得到路由表 ...