Kernel那些事儿之内存管理(10) --- 映射(上)

前面花了好多时间讲了内存管理中 node, zone, page frame, buddy system等。这些都是物理地址空间中的概念。然而,对于一个进程来说,它看到的却是完全不同的地址空间。

我们接下来就来看看这些地址空间,以及它们之间的映射。

1.内存地址

在内存管理中,会涉及到三种内存地址。

  1. 逻辑地址:程序的机器语言指令里,指定一条指令或是一个操作数的地址时,所使用的实际上都是逻辑地址。一个逻辑地址由两部分组成:段选择符和段偏移值。
  2. 线性地址:逻辑地址经过分段映射后,得到的就是线性地址。
  3. 物理地址:访问内存条上的内存单元时所使用的地址。CPU会把该地址送往地址总线来读/写某一内存单元。

逻辑地址到物理地址的转换是由MMU (Memory Management Unit)来完成的:分段单元把逻辑地址转换为线性地址,分页单元把线性地址转换为物理地址。

2.分段

系统中每个段都有一个对应的段描述符。段描述符中包含了该段的详细信息,比如:段的起始地址,段的长度,段的类型,段的权限等等。

系统中所有的段描述符都集中放在了两个table里:GDT (Global Descriptor Table) 或是 LDT (Local Descriptor Table)。这两个表的线性地址分别保存在寄存器 GDTR 和 LDTR 中。

一个逻辑地址由段选择符和段内偏移值组成。其中,段选择符格式如下:

TI:Table Indicator,指定了该段存在于GDT中(TI = 0)还是LDT中(TI = 1)。

index:指定了该段在GDT/LDT中的索引。

RPL:Requestor Privilege Level,与该段的权限相关。

系统提供了不同的段寄存器来存放段选择符:CS, SS, DS, ES, FS, GS. 其中,CS专门来放代码段的段选择符,DS专门来放数据段的段选择符,SS专门来放栈的段选择符。

其中,CS段寄存器中的RPL,代表了CPU当前的运行level。Linux只是用了两个level:0(内核态)和 3(用户态)。

逻辑地址经过分段单元转换成线性地址的过程,可以用下面的图来说明:

Linux简化了分段逻辑:所有运行在用户态的进程,都使用同一个代码/数据段,即用户态代码/数据段;同样的,所有运行在内核态的进程,都使用同一个代码/数据段,即内核态代码/数据段。

这四个段的段选择符由四个宏来定义:__USER_CS,__USER_DS,__KERNEL_CS,__KERNEL_DS。

而且,这四个段的起始地址都为0x0。这样带来一个好处:逻辑地址中的段偏移值总是和对应的线性地址相等。所以,逻辑地址空间到线性地址空间的映射,是非常简单的。

3.分页

既然Linux中所有的进程都使用相同的线性地址空间,那么将这些进程分隔开来的重任就要由分页来完成了。

分页机制非常简单,一张图就可以讲清楚了:

不同的进程,虽然使用相同的线性地址空间,但是会使用不同的页表,因此实际上使用的物理地址是不同的。

不过,线性地址空间到物理地址空间的映射,却并没有这么简单,是我们接下来的重点。

时间: 2024-10-10 14:44:14

Kernel那些事儿之内存管理(10) --- 映射(上)的相关文章

Kernel那些事儿之内存管理(11) --- 内核映射(上)

前面简单地介绍了三种不同的地址空间,接下来重点讲述线性地址空间到物理地址空间的映射. 我们先从32位系统开始. 在32位系统中,线性地址空间的大小为 2^32,即4GB.Kernel一般会按照 3:1 的比例,把线性地址空间分为两部分: 0~3GB 用户地址空间 3GB~4GB 内核地址空间. 用户地址空间的管理和映射是个大的topic.我们后面再详细讲述. 内核地址空间只有1GB大小,最多只能映射1GB的物理内存.那问题来了:1GB之外物理内存怎么办? Kernel 给出的解决办法是,把1GB

Kernel那些事儿之内存管理(13) --- 内核映射(下)

前面讲过,针对于内核地址空间中后面的128MB空间,Kernel提供了三种机制来映射物理内存.之前讲过了两种,即持久内核映射和临时内核映射.这两种机制的目的都是一样的:使Kernel能够访问到高端内存. 今天讲一下第三种机制:非连续内存分配,也就是vmalloc.这个机制同样可以使Kernel能够访问到高端内存,不过这不是该机制的主要目的.该机制的主要目的是:把物理上不连续的页面映射到连续的内核线性地址空间中. 非连续内存区域管理 既然是映射,肯定会涉及到三个元素:集合L,集合P,映射M. 集合

Kernel那些事儿之内存管理(1)

有人的地方就有江湖.要介绍内存管理这个江湖,首先还得从这里面的主要人物讲起. 在NUMA结构中,物理内存首先被分成若干nodes.每一个node进一步被分成若干zones.每一个zone又关联了一个描述page frames的数组,该数组包含了属于该zone的所有page frame的描述符. 不难看出,在这个江湖里主要有三位重要人物:nodes, zones 和 page frames.这三者的关系和地位大体可以用下图来描述(该图取自"Professional Linux Kernel Arc

Kernel那些事儿之内存管理(3) --- 久别重逢

上次我们讲到page frame是物理内存的基本组成单位.那Kernel就必须要有一套机制来管理空闲的page frames.这一点不难理解.每个县长必须要把本县可用的劳动力登记在册,这样哪天皇帝要征兵了,你才不至于手忙脚乱. 这个问题看似简单,实则不然.因为这里面有一个外碎片的问题. 在物理内存中,连续的物理内存页有时是很重要的.例如在DMA操作中,由于大部分DMA处理器都没有分页机制,它们会直接访问物理内存地址,因此DMA 所用的缓冲区在物理地址空间必须连续:再例如,使用连续的物理内存页,可

Kernel那些事儿之内存管理(8) --- Slab(中)

上篇讲了Slab中的数据结构,这篇该讲Slab中的操作了. 既然是内存管理,那操作无非就两点:allocate 和 free. 1. 申请一个object 在Slab中,申请一个object是通过函数 kmem_cache_alloc() 来完成的. 3618 void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags) 3619 { 3620     return __cache_alloc(cachep, flags, __bu

Kernel那些事儿之内存管理(12) --- 内核映射(中)

内核地址空间中后面这128MB的最后一部分,是固定映射 (fixed mappings). 固定映射是什么意思?为什么要有固定映射?Kernel源代码的注释里有一句话,可谓一语中的:The point is to have a constant address at compile time, but to set the physical address only in the boot process. 一个固定映射的线性地址是个常量,例如0xffffc000,且该常量在编译阶段就可以确定.

Kernel那些事儿之内存管理(7) --- Slab(上)

前面讲的buddy system算法,分配内存的最小单位是一个页面(例如 4K).这对于大的内存申请比较适用.可是实际生活中,Kernel经常需要分配小的内存空间,比如几十个字节,这个时候怎么办呢? 不同的人可能会想到不同的解决办法. 对于财大气粗的富人一族,办法很简单:申请一个页面,只用其中的几十字节,剩下的直接丢掉. 对于锱铢必较的穷人一族,就不敢这么挥霍了:申请一个页面,然后记录好页面内哪些内存区用了,哪些还没用,没用的部分可以用来满足其他的内存分配请求.总之务必做到物尽其用. 很不幸,K

Kernel那些事儿之内存管理(5) --- 衣带渐宽终不悔(上)

Kernel中负责分配一个连续内存页块的子系统一般被称为zoned page frame allocator.前面讲了函数 buffered_rmqueue() 是如何从指定zone的buddy system中分配一个连续内存页块的.这个函数貌似完成了内存页块分配相关的所有工作,然而实际上,这个函数只是zone allocator的冰山一角. 记得我刚上大学那会,拥有一个MP3还是一件能够令男生羡慕.令女生着迷的事情.于是我咬咬牙,花了近一个月的生活费买了一个漂亮的MP3,从此过上了非凡的生活.

Kernel那些事儿之内存管理(2) --- 百闻不如一见

上次介绍了物理内存管理中三位主要人物中的node 和zone.这两位是当官的,一个是县长,一个是里长,不敢不先介绍啊.接下来出场的就是我们的老百姓了 --- page frame. Page frame是物理内存的基本组成单位,在Kernel中由结构体 struct page 来描述. struct page {     unsigned long flags;          atomic_t _count;          union {         atomic_t _mapcou