文档章节

Linux内核学习之四-内存管理

黄亿华
 黄亿华
发布于 2014/01/28 17:05
字数 1815
阅读 1045
收藏 22

一、虚拟存储器

内存管理最基础的概念,恐怕是虚拟存储器(Virtual Memory,简称VM)了,它是计算机系统(注意我没写操作系统,因为其中还有部分硬件功能)在物理存储之上的一套机制。它将物理地址(Physical Address,简称PA)转换为虚拟地址(Virtual Address,简称VA),来访问存储。正是因为有了虚拟存储器,才有了后面的内存转换、页表等机制。

看到这里我就有疑问了,有了物理地址,程序已经能定位到内存块并且使用了,为什么需要有虚拟地址?

实际上,如果只有一个程序在内存中运行,没有虚拟存储问题也不大,比如DOS(又提到它了!),而且据说DOS确实是没有虚拟存储机制的。但是当有了多进程之后,问题就出现了:多个进程共同使用一整个物理内存,既不安全也不方便,比如A用了0xb7001008,结果B没法知道,然后也用了它,岂不是乱套了?

为了解决这个问题,就有了虚拟存储机制。对于每个进程来说,它的虚拟地址空间总是一样的,但是实际使用的物理内存是分开的,而且它也不知道到底是在使用虚拟地址,还是物理地址,反正用就对了!这样子既简化了程序开发,又增加了安全性,真是非常巧妙的设计!

总结一下,虚拟存储的最大作用就是隔离与抽象

二、地址转换的实现

为了了解地址的转换,我们必须引入“页”(page)的概念。其实页就是一块连续的内存,这也是操作系统利用内存的最小单位。更直观一点的说,在Linux里,它是这么实现的:

struct page{
    //保存状态
    unsigned long flags;
    //引用计数
    atomic_t _count;
    atomic_t _mapcount;
    unsigned long private;
    struct address_space *mapping;
    pgoff_t index;
    struct list_head lru;
    //指向虚拟地址
    void *virtual;
}

page结构体保存对应页的引用数、虚拟地址等信息。因为这个结构本身也是消耗内存的,所以一页的大小太小,那么还要存page结构,就有很多内存浪费了,很不划算。如果页大小太大,经常会出现一个页装不满的情况,也是我们不愿看到的。在32位CPU里,一页是4KB。

有了页的知识,地址转换就可以进行了。在看这部分之前,不妨先想想,如果让我们实现一套地址转换,会怎么做?

似乎这没有什么难度啊?可以分两部分,你看我伪代码都写好了:

  1. 保存虚拟地址到物理地址的映射关系

     page_table={virtual_address:physical_address}
    
  2. 在程序中使用指针访问物理地址的时候,对其进行转换

     physical_address=page_table[virtual_address]	
    

是不是非常简单?

实际上目前的计算机系统做的方式也差不多。但是区别是,因为这两个操作非常频繁,光靠软件实现性能未必有那么好,所以这两部分有了一些硬件上的优化。

把VA转换为PA的事情,由CPU里一个专门的部件来完成,它叫做“内存管理单元”(Memory Management Unit,简称MMU)。

保存地址映射关系的部件,叫做页表(Page Table),它是保存在内存中的,由操作系统维护。但是访问一次内存还要查一次内存,这事感觉不太科学,所以MMU还会维护一份用过的页表索引,这就是传说中的TLB(Translation Lookaside Buffer,也叫转换备用缓冲区,我们学校的孙钟秀院士在他的教材中将其翻译为“快表”)。

所以最后的流程是:

  1. 操作系统新建进程时,为进程分配内存空间,并更新页表;
  2. 该进程的指令到CPU之前,其中的虚拟地址,会触发MMU转换流程;
  3. MMU先到TLB中找页表,找不到再去物理内存中找页表,最后转换为物理地址,递给CPU执行。

这其实也是一个操作系统反过来影响CPU设计的案例,这也说明,其实硬件跟系统分界并不是死的,比如有些CPU的指令集也会包括一些高等的操作,理论上越底层越快,所以到底放在哪一层,主要取决于这个机制的价值和通用性。

至此地址转换算是差不多了,操作系统和MMU握了个手,合作愉快!

PS:在有些嵌入式CPU上没有MMU,就确实是软件来完成地址转换。不过现在带有MMU的CPU越来越多了!

三、Linux中的内存管理

Linux中内存分配相关的代码在kernal/page_alloc.c中,其中核心的函数是struct page * __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, struct zonelist *zonelist, nodemask_t *nodemask)。我对能看懂的代码做了注释,大部分都没看懂,等全部都研究一遍之后再来细细研究吧…

Linux内存管理中还有很多细节,罗列几个,做个备忘。

  1. 分区(zone)

    Linux将内存分为几个区:ZONE_DMA、ZONE_NORMAL和ZONE_HIGHEN。DMA(Direct Memory Access)是一种IO直接操作内存的技术,有些硬件只能用特定地址的内存来进行DMA,对这种内存需要标记一下。

  2. NUMA

    NUMA(Non-Uniform Memory Access Architecture)是相对于UMA(Uniform Memory Access Architecture)来说的。UMA就是指多处理器共享一片内存,而NUMA则反其道行之,将CPU绑定到一些内存中,从而加快速度。据说在多于八核的处理器中效果明显。

  3. slab

    slab这部分LKD讲的并不好,有些绕弯(也可能是翻译不好吧),然后我搜到很多资料,大致连描述都照样复制,我也不知道是不是我智商太低,弄不懂,还是作者只是做了个摘抄,反正对于这些技术文章只能呵呵了。

    其实slab解决了什么问题呢?我们知道在内核里有些数据结构是很常用的,例如inode,这些数据结构会频繁初始化和销毁。但是初始化数据结构是有开销的啊,更好的办法是把它存下来,然后下次创建的时候,直接拿一个现成的,改改内容,就可以用了!slab又译作“板坯”,这样子是不是好理解一点呢?

    在实现上,slab会为一类对象开辟一段空间,存储多个这样的对象,然后创建和销毁,其实只是在这片空间里指针移动一下的事情了!我们其实可以叫它“对象池”或者“结构池”吧!slab的代码实现在LKD中有非常详细的描述,不再赘述了。

参考资料:

© 著作权归作者所有

黄亿华

黄亿华

粉丝 2443
博文 131
码字总数 116344
作品 7
程序员
私信 提问
加载中

评论(1)

码农与厨子
码农与厨子
79
对操作系统的五点感受--接口/进程/内存/磁盘管理/系统架构

之一:管理和使用--对内和对外的接口 总的说来吧,任何事情都有两套机 构,一套是为使用而设置的,另一套是为管理而设置的,比如一个网站,普通的页面是为了让用户访问的,而一般还要有一系列...

晨曦之光
2012/04/10
150
0
迅为4412开发板Linux驱动教程之内核开发基础

视频教程:http://v.youku.com/v_show/id_XMTMwNjAwMDc0OA==.html 主要内容 • Linux体系结构 • Linux内核结构 • Linux内核源码目录结构 Linux体系结构 从上图可知,Linux体系结构由用户空...

topeet
2015/08/10
303
0
Linux 内核剖析

简介: Linux® 内核是一个庞大而复杂的操作系统的核心,不过尽管庞大,但是却采用子系统和分层的概念很好地进行了组织。在本文中,您将探索 Linux 内核的总体结构,并学习一些主要的子系统和...

晨曦之光
2012/03/02
203
0
最近的学习---一句话收获(备查用)(2)

1.在内核中分配大内存问题。 首先要明白内核没有义务为你分配连续的物理内存,因此你需要的内核中连续的物理内存就是苛刻的要求,既然在计算机系统抽象给用户一个连续的美丽的一维的虚拟内存...

晨曦之光
2012/04/10
68
0
Linux与Android的关系

大家都知道Android是基于Linux内核的操作系统,也曾经和Linux基金会因为内核问题产生过分歧,本文将开始对Android的内核进行剖析,主要介绍Android和Linux之间的关系,后续还会讲到Android系...

开心303
2011/08/03
375
0

没有更多内容

加载失败,请刷新页面

加载更多

golang-字符串-地址分析

demo package mainimport "fmt"func main() {str := "map.baidu.com"fmt.Println(&str, str)str = str[0:5]fmt.Println(&str, str)str = "abc"fmt.Println(&s......

李琼涛
今天
4
0
Spring Boot WebFlux 增删改查完整实战 demo

03:WebFlux Web CRUD 实践 前言 上一篇基于功能性端点去创建一个简单服务,实现了 Hello 。这一篇用 Spring Boot WebFlux 的注解控制层技术创建一个 CRUD WebFlux 应用,让开发更方便。这里...

泥瓦匠BYSocket
今天
6
0
从0开始学FreeRTOS-(列表与列表项)-3

FreeRTOS列表&列表项的源码解读 第一次看列表与列表项的时候,感觉很像是链表,虽然我自己的链表也不太会,但是就是感觉很像。 在FreeRTOS中,列表与列表项使用得非常多,是FreeRTOS的一个数...

杰杰1号
今天
8
0
Java反射

Java 反射 反射是框架设计的灵魂(使用的前提条件:必须先得到代表的字节码的 Class,Class 类 用于表示.class 文件(字节码)) 一、反射的概述 定义:JAVA 反射机制是在运行状态中,对于任...

zzz1122334
今天
5
0
聊聊nacos的LocalConfigInfoProcessor

序 本文主要研究一下nacos的LocalConfigInfoProcessor LocalConfigInfoProcessor nacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/impl/LocalConfigInfoProcessor.java p......

go4it
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部