当前位置:首页 >> 农林牧渔 >>

四讲:内存管理


Linux操作系统分析与实践 Linux操作系统分析与实践
第四讲:内存管理 第四讲:
《Linux操作系统分析与实践》课程建设小组 Linux操作系统分析与实践》 操作系统分析与实践 北京大学 二零零八年春季
*致谢:感谢Intel对本课程项目的资助 致谢:感谢Intel对本课程项目的资助 Intel

本讲主要内容

>? ? ? ? 基础知识 Linux中的分段 中的分段 Linux中的分页 中的分页 物理内存的管理
– – – 基于区的伙伴系统的设计 Slab分配器 分配器 非连续存储器区的管理

基础知识: 基础知识:基本概念
? 存储器地址
– 逻辑地址 逻辑地址(logical address)
? 机器语言指令中用来指定一个操作数或一条指令 的地址, 的地址,通常我们写程序中引用的符号地址是逻 辑地址

– 线性地址 线性地址(linear address/virtual address)
? 标识 标识0-4GB范围的地址 范围的地址

– 物理地址 物理地址(physical address)
? 送到地址线上的地址,用于对物理存储单元寻址 送到地址线上的地址,

基础知识:基本概念( 基础知识:基本概念(续)
? 三种地址之间的关系
– 通过硬件的分段和分页进行转换 – 逻辑地址->线性地址->物理地址 逻辑地址->线性地址->物理地址 ->线性地址->

基础知识: 基础知识:硬件分段
? 为支持保护模式,段改由段描述符来描述 为支持保护模式,
– – – – – 段基址 段限长 类型 访问该段所需的最小特权级 …

? 段描述符存放在 段描述符存放在GDT或LDT中(全局 局部描 或 中 全局/局部描 述符表) 述符表)中 ? GDT/LDT的基址存放在寄存器 的基址存放在寄存器GDTR/LDTR 的基址存放在寄存器 中

基础知识:硬件分段( 基础知识:硬件分段(续)
? 段寄存器
– – – – CS:代码段寄存器 : DS:数据段寄存器 : SS:堆栈段寄存器 : ES、FS、GS… 、 、

? 每个段寄存器存放段选择符
– GDT/LDT的索引 的索引 – TI位:指定描述符是 还是LDT 位 指定描述符是GDT还是 还是 – RPL:CPU的当前特权级 : 的当前特权级

基础知识:硬件分段( 基础知识:硬件分段(续)
? 逻辑地址到线性地址的转换

基础知识: 基础知识:硬件分页
? 页 vs. 页框
– 页:数据块,可以在主存也可以在磁盘 数据块, – 页框:固定长度的RAM块 页框:固定长度的 块

? 分页
– Intel CPU处理 处理4KB的页 的页(why 4KB?) 处理 的页 – 通过设置CR3的PG位开启分页 通过设置 的 位开启分页

? 数据结构
– 页目录
? 当前使用的页目录的物理地址在CR3中 当前使用的页目录的物理地址 物理地址在 中

– 页表

基础知识:硬件分页( 基础知识:硬件分页(续)
? 硬件的分页过程
– 发生在物理地址送地址线之前 – MMU中进行 中进行

基础知识:硬件分页( 基础知识:硬件分页(续)
? TLB(Translation Lookaside Buffer)
– 硬件,用来加速页表查找 硬件, – 关键的一点:如果操作系统更改了页表内容, 关键的一点:如果操作系统更改了页表内容, 它必须相应的刷新TLB以使 以使CPU不误用过时 它必须相应的刷新 以使 不误用过时 的表项 – CR3发生改变时,硬件自动更新 发生改变时, 发生改变时 硬件自动更新TLB中所有 中所有 的表项

基础知识: 基础知识:硬件保护机制
? 段描述符
– type标志:读/写/执行 标志: 写 执行 标志

? 页表项
– User/Supervisor标志 标志 – Read/Write标志 标志

? 硬件产生 硬件产生page fault

基础知识: 基础知识:扩展分页
? 允许页框为 允许页框为4MB(why?)

基础知识:物理地址扩展分页( 基础知识:物理地址扩展分页(PAE) )
? 内核不能直接对 以上的 内核不能直接对1G以上的 以上的RAM进行寻址 进行寻址 ? 市场需求刺激下,Intel进行了修补: 进行了修补: 市场需求刺激下, 进行了修补
– 在Pentiume Pro体系结构,地址线 位,但 体系结构, 体系结构 地址线36位 仍必须把32位线性地址转换为 位线性地址转换为36位 仍必须把 位线性地址转换为 位 – 办法:页表项长度改为 位(因为 位已不 办法:页表项长度改为64位 因为32位已不 够用),再增加一级页表,该页表4个表项 ),再增加一级页表 个表项, 够用),再增加一级页表,该页表 个表项, 基址存在CR3中 基址存在 中

基础知识: 基础知识:小结
? 如何面对不同的体系结构? 如何面对不同的体系结构?
– 原理类似,实现略有不同 原理类似,

? 分段
– 能不能不要分段? 能不能不要分段?

? 分页
– 硬件提供保护 – 性能的考虑:TLB 性能的考虑:

? 软件应该如何设计? 软件应该如何设计?

Linux中的分段 中的分段
? 内核代码段 内核代码段__KERNEL_CS:范围 0-4G; : ; 可读、执行; 可读、执行;DPL=0 ? 内核数据段 内核数据段__KERNEL_DS:范围 0-4G; : ; 可读、 可读、写;DPL=0 ? 用户代码段 用户代码段__USER_CS:范围 0-4G; : ; 可读、执行; 可读、执行;DPL=3 ? 内核代码段 内核代码段__USER_DS:范围 0-4G; : ; 可读、 可读、写;DPL=3

Linux中的分段(续) 中的分段( 中的分段
? TSS(任务状态段 :存储进程的硬件上下 任务状态段): 任务状态段 进程切换时使用,每个CPU有一个 文,进程切换时使用,每个 有一个 ? default_ldt:理论上每个进程都可以同时 : 使用很多段, 使用很多段,这些段可以存储在自己的 ldt段中,但实际 段中, 极少利用x86的这 段中 但实际linux极少利用 的这 极少利用 些功能, 些功能,多数情况下所有进程共享这个 段,它只包含一个空描述符

Linux中的分段:小结 中的分段: 中的分段
? 由于历史原因,IA32体系结构仍然强制 由于历史原因, 体系结构仍然强制 使用硬件分段 ? 段式映射基地址总是 ,逻辑地址与虚拟 段式映射基地址总是0, 地址总是一致的

Linux中的分页 中的分页
? 与体系结构无关的三级页表模型
– pgd,页目录 , – pmd,页中级目录 , – pte,页表项 ,

? 为什么使用三级页表
– 设计目标决定:可移植性 设计目标决定: – 硬件特性决定:PAE 硬件特性决定:

Linux中的分页 续) 中的分页(续 中的分页

Linux中的分页 续) 中的分页(续 中的分页
? 进程页表
– 各进程拥有自己的 用户空间 各进程拥有自己的3G用户空间 – 内核占用最高的1G作为系统空间,系统空 内核占用最高的 作为系统空间, 作为系统空间 间由所有进程共享

Linux中的分页 续) 中的分页(续 中的分页
? 1G的内核空间 的内核空间
– 内核空间==物理内存空间 内核空间== ==物理内存空间 – 1G的后 的后128MB用作实现非连续内存分配和 的后 用作实现非连续内存分配和 固定映射的线性地址

Linux中的分页 续) 中的分页(续 中的分页
? 问题: 问题:
– 为什么系统空间要由所有进程共享? 为什么系统空间要由所有进程共享? – 系统空间和进程空间中的线性地址可能映射 到同一物理地址么? 到同一物理地址么?

物理内存的管理
? Linux2.4的内存管理 的内存管理
– 实现了基于区的伙伴系统 – 为了改进伙伴系统的效率,实现了 为了改进伙伴系统的效率,实现了slab分配 分配 器 – 非连续存储器区的管理

? 思考:为什么Linux的内存管理系统会如 思考:为什么 的内存管理系统会如 此复杂? 此复杂?

基于区的伙伴系统: 基于区的伙伴系统:基本概念
? 受体系结构的制约,不同的存储器页有 受体系结构的制约, 不同的使用方式, 不同的使用方式,根据内存的不同使用 类型划分为内存区(Memory Zone) 类型划分为内存区 ? Linux中的内存区(Memory Zone) 中的内存区( 中的内存区
– ZONE_DMA:低于 低于16MB的存储器页 低于 的存储器页 – ZONE_NORMAL:16MB-896MB的存储器 的存储器 页 – ZONE_HIGHMEM:高于 高于896MB的存储器页 高于 的存储器页

基于区的伙伴系统:基本概念 续 基于区的伙伴系统:基本概念(续)
? 非一致存储器访问(Non-Uniform Memory 非一致存储器访问( Access,NUMA)模型 , )
– 计算机内存不是一种均匀的资源,给定CPU对不同 计算机内存不是一种均匀的资源,给定 对不同 存储器单元的访问时间可能不一样

? Linux将物理内存划分成多个节点(node) Linux将物理内存划分成多个节点(node) 将物理内存划分成多个节点
– 给定 给定CPU对一个单独节点内的存储器单元的访问时 对一个单独节点内的存储器单元的访问时 间是相同的

? X86体系结构使用一致存储器访问(UMA)模 体系结构使用一致存储器访问( 体系结构使用一致存储器访问 ) 因此并不真正需要NUMA的支持 型,因此并不真正需要 的支持

基于区的伙伴系统: 基于区的伙伴系统:基本数据结构
? 页框
– 页描述符 页描述符page – 页描述符数组mem_map 页描述符数组

? 内存区
– 区描述符zone_struct 区描述符
? zone_mem_map:指定对应的 指定对应的mem_map中的第一个元素 指定对应的 中的第一个元素 ? Size:元素个数 :

? 节点
– 节点描述符 节点描述符pg_data_t – 链表,首元素为 链表,首元素为pgdat_list – 80x86下,只有一个节点,其描述符存在 下 只有一个节点, contig_page_data变量中 变量中

基于区的伙伴系统: 基于区的伙伴系统:基本数据结构 (续)

基于区的伙伴系统: 基于区的伙伴系统:伙伴系统
? 内存的分配的两大问题
– 分配效率 – 碎片问题

? 伙伴系统(Buddy System)能够快速的满 伙伴系统 能够快速的满 足各种大小的分配要求, 足各种大小的分配要求,同时不能产生 大量的碎片浪费空间, 大量的碎片浪费空间,是一种常用的比 较好的算法

基于区的伙伴系统:伙伴系统 续 基于区的伙伴系统:伙伴系统(续)
? Linux为每个内存区使用不同的伙伴系统 为每个内存区使用不同的伙伴系统
– 80x86体系结构中,针对不同的内存区,共 体系结构中,针对不同的内存区, 体系结构中 有三种伙伴系统

? 伙伴系统算法
– 内核为分配连续页框而选择的一种分配策略 – 有效解决了外碎片问题:尽量避免为满足对 有效解决了外碎片问题: 小块的请求而把大块的空闲块进行分割: 小块的请求而把大块的空闲块进行分割:

基于区的伙伴系统:伙伴系统 续 基于区的伙伴系统:伙伴系统(续)
? 伙伴系统使用的数据结构
– 页描述符数组 页描述符数组mem_map – free_area数组 第k项管理大小为 k的块: 数组,第 项管理大小为2 的块: 数组 项管理大小为 typedef struct free_area_struct
{ struct list_head free_list;//指向大小为 k的块的双 指向大小为2 指向大小为 向循环链表, 向循环链表,每个元素指向该块的第一个页描述 符 unsigned long* map;//每一位描述大小为 k个页框 每一位描述大小为2 每一位描述大小为 的两个伙伴块的状态 }

基于区的伙伴系统:伙伴系统 续 基于区的伙伴系统:伙伴系统(续)

基于区的伙伴系统:伙伴系统 续 基于区的伙伴系统:伙伴系统(续)
Each request is rounded up to powers of 2 Each request is rounded up to powers of 2
64H 64H 64H 64H

D requests 3 32C 32C 8H 16H 8B 8A
4H 4D

A exits 32C 8H
4H 4D

B exits 32C 8H
4H 4D

8B 8A

8B 8H

16H

Suppose we have 128 units, A needs 6 units Next request by B for 5 units, D for 3 Next request by C for 24 units: hole of size 32 allocated

Slab分配器:缘起 分配器: 分配器
? 伙伴系统解决了外碎片的问题,当请求 伙伴系统解决了外碎片的问题, 的内存小于一个页框时怎么办? 的内存小于一个页框时怎么办? ? 早期 早期Linux的做法: 的做法: 的做法
– 提供大小为2,4,8,16,...,131056字节的内存区 字节的内存区 提供大小为 域 – 需要新的内存区域时,内核从伙伴系统申请 需要新的内存区域时, 页面,把它们划分成一个个区域, 页面,把它们划分成一个个区域,取一个来 满足需求 – 如果某个页面中的内存区域都释放了,页面 如果某个页面中的内存区域都释放了, 就交回到伙伴系统

Slab分配器:缘起(续) 分配器:缘起( 分配器
? 在伙伴算法之上运行存储器区 在伙伴算法之上运行存储器区(memory area)分配算法 分配算法 没有显著的效率
– 不同的数据类型用不同的方法分配内存可能提高效率。比如 不同的数据类型用不同的方法分配内存可能提高效率。 需要初始化的数据结构,释放后可以暂存着, 需要初始化的数据结构,释放后可以暂存着,再分配时就不 必初始化了 – 内核的函数常常重复地使用同一类型的内存区,缓存最近释 内核的函数常常重复地使用同一类型的内存区, 放的对象可以加速分配和释放 – 对内存的请求可以按照请求频率来分类,频繁使用的类型使 对内存的请求可以按照请求频率来分类, 用专门的缓存,很少使用的可以使用类似2.0中的取整到 中的取整到2的幂 用专门的缓存,很少使用的可以使用类似 中的取整到 的幂 次的通用缓存 – 使用 的幂次大小的内存区域时高速缓存冲突的概率较大,有 使用2的幂次大小的内存区域时高速缓存冲突的概率较大 的幂次大小的内存区域时高速缓存冲突的概率较大, 可能通过仔细安排内存区域的起始地址来减少高速缓存冲突 – 缓存一定数量的对象可以减少对 缓存一定数量的对象可以减少对buddy系统的调用,从而节省 系统的调用, 系统的调用 时间并减少由此引起的高速缓存污染

? Slab分配器的设计体现了以上的思想 分配器的设计体现了以上的思想

Slab分配器:基本组成 分配器: 分配器
? Slab分配器把存储器区看作对象 分配器把存储器区看作对象(object) 分配器把存储器区看作对象 ? Slab分配器把对象按照类型分组成不同 分配器把对象按照类型分组成不同 的高速缓存 ? 每个 每个Slab由一个或多个连续的页框组成, 由一个或多个连续的页框组成, 由一个或多个连续的页框组成 这些页框中包含已分配的对象, 这些页框中包含已分配的对象,也包含 空闲的对象 ? Slab分配器通过伙伴系统分配页框 分配器通过伙伴系统分配页框

Slab分配器:基本组成(续) 分配器:基本组成( 分配器

Slab分配器:数据结构 分配器: 分配器
? 高速缓存描述符 高速缓存描述符struct kmem_cache_s
– 链表结构 – 包含三种slab双向循环链表 包含三种 双向循环链表
? slabs_full ? slabs_partial ? slabs_free

? Slab描述符 描述符struct slab_s 描述符
– 在高速缓存中根据类型不同分别存在不同的slab链 在高速缓存中根据类型不同分别存在不同的 链 表中 – 可存放在 可存放在slab外,也可存放在 外 也可存放在slab内 内

Slab分配器:数据结构(续) 分配器:数据结构( 分配器

Slab分配器:数据结构(续) 分配器:数据结构( 分配器
? 对象描述符 对象描述符kmem_bufctl_t
– 存放在数组中,位于相应的 存放在数组中,位于相应的slab描述符之后 描述符之后 – 只在对象空闲时有意义,包含的是下一个空 只在对象空闲时有意义, 闲对象在slab中的下标 闲对象在 中的下标

Slab分配器:数据结构(续) 分配器:数据结构( 分配器

Slab分配器:高速缓存 分配器: 分配器
? 高速缓存分类
– 普通高速缓存 普通高速缓存(general cache)
? Slab分配器自己使用 分配器自己使用

– 专用高速缓存(special cache) 专用高速缓存
? 内核其余部分使用

Slab分配器:高速缓存(续) 分配器:高速缓存( 分配器
? 普通高速缓存
– 第一个高速缓存包含由内核使用的其余高速缓存描 述符 – 另外 个高速缓存包含几何分布的存储区,与其相 另外26个高速缓存包含几何分布的存储区 个高速缓存包含几何分布的存储区, 关的存储器区大小为32,64,…,131072字节 关的存储器区大小为 字节
? cache_sizes_t cache_size[26] ? typedef struct cache_sizes { size_t cs_size; kmem_cache_t *cs_cachep;//用于 用于DMA分配 用于 分配 kmem_cache_t *cs_dmacachep;//用于常规分配 用于常规分配 } cache_sizes_t;

Slab分配器:与伙伴系统的接口 分配器: 分配器

Slab分配器通过伙伴系统为 分配器通过伙伴系统为slab分配连续的页框 分配器通过伙伴系统为 分配连续的页框

Slab分配器:与伙伴系统的接口 分配器: 分配器
void * kmem_getpages(kmem_cache_t *cachep, unsigned long flags) { void *addr; flags |= cachep->gfpflags; addr = (void*) _ _get_free_pages(flags, cachep->gfporder); return addr; } 功能:为创建的slab分配一组空闲连续的页框 参数: cachep指向需要额外页框的高速缓存描述符 flags说明如何请求页框 数据结构: cachep->gfporder单独slab包含的连续页框数的对数 其它函数: __get_free_pages(gft_mask, order) 类似于alloc_pages(),返回第一个所分配页的线性地址

Slab分配器:与伙伴系统的接口(续) 分配器:与伙伴系统的接口 续 分配器
void kmem_freepages(kmem_cache_t *cachep, void *addr) { unsigned long i = (1<<cachep->gfporder); struct page *page = & mem_map[_ _pa(addr) >> PAGE_SHIFT]; while (i--) { PageClearSlab(page); page++; } free_pages((unsigned long) addr, cachep->gfporder); }
功能:释放分配给slab分配器的页框 参数: cachep指向需要释放页框的高速缓存描述符 addr从addr开始释放页框 数据结构: cachep->gfporder请求页框的个数 mem_map页描述符数组 其它函数: free_pages(addr, order):从线性地址addr起释放页框 PageClearSlab(page):清除page中的PG_Slab标志,PG_Slab表 示该页框包含在Slab中

Slab分配器:slab的分配与释放 分配器: 分配器 的分配与释放

通过伙伴系统可以为slab分配到连续的页框,slab组成了高速缓存 分配到连续的页框, 通过伙伴系统可以为 分配到连续的页框 组成了高速缓存

Slab分配器: slab的分配与释放 续) 分配器: 的分配与释放(续 分配器 的分配与释放
? 分配时机
– 已发出分配新对象的请求,并且 已发出分配新对象的请求, – 高速缓存不包含任何空闲对象

? Kmem_cache_grow()
– kmem_getpages()为slab分得页框 为 分得页框
? kmem_cache_slabmgmt()获得新slab描述符 kmem_cache_slabmgmt()获得新slab描述符 获得新

– kmem_cache_init_objs()为新的 为新的slab中的对象申请构造方法 为新的 中的对象申请构造方法 – 扫描所有页框描述符
? 高速缓存描述符地址存页描述符中的 高速缓存描述符地址存页描述符中的list->next ? slab描述符地址存页描述符中的 描述符地址存页描述符中的list->prev 描述符地址存页描述符中的 ? 设置页描述符的PG_slab标志 标志 设置页描述符的

– 将slab描述符加到全空 描述符加到全空slab链表中 描述符加到全空 链表中

Slab分配器: slab的分配与释放 续) 分配器: 的分配与释放(续 分配器 的分配与释放
? 释放时机
– 伙伴系统不能满足新请求的一组页框,并且 伙伴系统不能满足新请求的一组页框, – slab为空 为空

? 内核查找另外的空闲页框时,调用 内核查找另外的空闲页框时, try_to_free_pages()
– 调用 调用kmem_cache_reap()选择至少包含一个 选择至少包含一个 空slab的高速缓存 的高速缓存 – 调用 调用kmem_slab_destroy()从完全空闲的 从完全空闲的slab 从完全空闲的 链表中删除slab并撤销它 链表中删除 并撤销它

Slab分配器: slab的分配与释放 续) 分配器: 的分配与释放(续 分配器 的分配与释放
void kmem_slab_destroy(kmem_cache_t *cachep, slab_t *slabp) { if (cachep->dtor) { int i; for (i = 0; i < cachep->num; i++) { void* objp = & slabp->s_mem[cachep->objsize*i]; (cachep->dtor)(objp, cachep, 0); } } kmem_freepages(cachep, slabp->s_mem - slabp->colouroff); if (OFF_SLAB(cachep)) kmem_cache_free(cachep->slabp_cache, slabp); }
数据结构: slabp->s_mem:指向slab内第一个对象 cachep->num:挤进一个单独slab中的对象个数 cachep->objsize:高速缓存中包含对象的大小 其它函数: cachep->dtor:对象的析构函数 kmem_freepages(kmem_cache_t *cachep, void *addr):释放分配给slab的页框 OFF_SLAB宏:判断slab描述符是否存放在slab的外面 kmem_cache_free (kmem_cache_t *cachep, void *objp):释放slab描述符

Slab分配器:高速缓存中的对象管理 分配器: 分配器

对象存放在slab中,存放的分配到连续的页框,slab组成了高速缓存 中 存放的分配到连续的页框 存放的分配到连续的页框, 对象存放在 组成了高速缓存

Slab分配器:高速缓存中的对象管理 分配器: 分配器 (续)
? kmem_cache_alloc()为高速缓存分配对象 为高速缓存分配对象
kmem_cache_alloc()中的代码片段: void * objp; slab_t * slabp; struct list_head * entry; local_irq_save(save_flags); entry = cachep->slabs_partial.next; if (entry == & cachep->slabs_partial) { entry = cachep->slabs_free.next; if (entry == & cachep->slabs_free) goto alloc_new_slab; list_del(entry); list_add(entry, & cachep->slab_partials); } slabp = list_entry(entry, slab_t, list);

查找空闲对象的slab, 若不存在,则分配一 个新的slab

Slab分配器:高速缓存中的对象管理 分配器: 分配器 (续)
kmem_cache_alloc()中的代码片段(接上): slabp->inuse++; ... objp = & slabp->s_mem[slabp->free * cachep->objsize]; … slabp->free = ((kmem_bufctl_*)(slabp+1))[slabp->free]; if (slabp->free == BUFCTL_END) { list_del(&slabp->list); list_add(&slabp->list, &cachep->slabs_full); } … local_irq_restore(save_flags); return objp; 数据结构: slabp->inuse:当前已分配的对象个数 slabp->s_mem:指向slab内第一个对象 cachep->objsize:高速缓存中包含对象的大小 slabp->free:指向slab内第一个空闲对象 对象描述符数组紧挨着slab描述符,表项指向下一个空闲对象

Slab分配器:高速缓存中的对象管理 分配器: 分配器 (续)
? kmem_cache_free()释放由 释放由slab分配器以 释放由 分配器以 前所获得的对象
kmem_cache_free()中的代码片段: slab_t * slabp; 计算出slab描述符地址,在为slab分配页框时, unsigned int objnr; 描述符地址存于页框描述符的list.prev中 local_irq_save(save_flags); slabp = (slab_t *) mem_map[_ _pa(objp) >> PAGE_SHIFT].list.prev; … objnr = (objp - slabp->s_mem) / cachep->objsize; ((kmem_bufctl_t *)(slabp+1))[objnr] = slabp->free; slabp->free = objnr; 导出对象描述符,将对象追加到空闲对象链表的首部 数据结构: slabp->free:指向slab内第一个空闲对象 对象描述符数组紧挨着slab描述符,表项指向下一个空闲对象

Slab分配器:高速缓存中的对象管理 分配器: 分配器 (续)
kmem_cache_free()中的代码片段(接上): if (--slabp->inuse == 0) { /* slab is now fully free */ list_del(&slabp->list); list_add(&slabp->list, &cachep->slabs_free); } else if (slabp->inuse+1 == cachep->num) { /* slab was full */ list_del(&slabp->list); list_add(&slabp->list, &cachep->slabs_partial); } local_irq_restore(save_flags); return;

最后检查slab是否需要移动另一个链表中

Slab分配器:高速缓存中的通用对象 分配器: 分配器 管理
? 如果对存储区的请求不频繁,就用一组 如果对存储区的请求不频繁, 普通高速缓存来处理
– kmalloc():得到通用对象 : – Kfree():释放通用对象 :

Slab分配器:高速缓存中的通用对象 分配器: 分配器 管理( 管理(续)
void * kmalloc(size_t size, int flags) { cache_sizes_t *csizep = cache_sizes; kmem_cache_t * cachep; for (; csizep->cs_size; csizep++) { if (size > csizep->cs_size) continue; if (flags & _ _GFP_DMA) cachep = csizep->cs_dmacahep; else cachep = csizep->cs_cachep; return _ _kmem_cache_alloc(cachep, flags); } return NULL; } 数据结构: cache_sizes_t cache_size[26]; //26个几何分布的高速缓存 typedef struct cache_sizes { size_t cs_size; kmem_cache_t *cs_cachep;//用于DMA分配 kmem_cache_t *cs_dmacachep;//用于常规分配 } cache_sizes_t; 其它函数: __kmem_cache_alloc (kmem_cache_t *cachep, int flags):在高速缓存中分配对象

Slab分配器:高速缓存中的通用对象 分配器: 分配器 管理( 管理(续)
void kfree(const void *objp) { kmem_cache_t * c; unsigned long flags; if (!objp) return; local_irq_save(flags); c = (kmem_cache_t *) mem_map[_ _pa(objp) >> PAGE_SHIFT].list.next; _ _kmem_cache_free(c, (void *) objp); local_irq_restore(flags); }
功能:释放通用对象 数据结构: mem_map页描述符数组 在为slab分配页框时,高速缓存描述符地址存于页框描述符的list.next中 其它函数: __kmem_cache_free (kmem_cache_t *cachep, void* objp):释放高速缓存中的对象

Slab分配器:小结 分配器: 分配器

非连续存储器区管理

? 非连续存储器区允许将连续的线性地址映射到非 连续的物理页框 ? 非连续存储器区描述符 非连续存储器区描述符vm_struct,单链表 , ? 相关函数
– get_vm_area():创建 创建vm_struct描述符 创建 描述符 – vmalloc():分配非连续存储器区 分配非连续存储器区 – vfree():释放非连续内存区 释放非连续内存区

非连续存储器区管理( 非连续存储器区管理(续)
struct vm_struct * get_vm_area(unsigned long size, unsigned long flags) { unsigned long addr; struct vm_struct **p, *tmp, *area; area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL); if (!area) return NULL; size += PAGE_SIZE; addr = VMALLOC_START; write_lock(&vmlist_lock); for (p = &vmlist; (tmp = *p) ; p = &tmp->next) { if (size + addr <= (unsigned long) tmp->addr) { area->flags = flags; area->addr = (void *) addr; area->size = size; 功能:创建vm_struct描述符 area->next = *p; 数据结构: *p = area; vmlist:指向非连续存储区描述符链 write_unlock(&vmlist_lock); 表头 return area; 其它函数: } addr = tmp->size + (unsigned long) tmp->addr; kmalloc():获得通用对象 if (addr + size > VMALLOC_END) { kfree():释放kmalloc()所获得的对象 write_unlock(&vmlist_lock); kfree(area); return NULL; } } }

非连续存储器区管理( 非连续存储器区管理(续)
void * vmalloc(unsigned long size) { void * addr; struct vm_struct *area; size = (size + PAGE_SIZE - 1) & PAGE_MASK; area = get_vm_area(size, VM_ALLOC); if (!area) return NULL; addr = area->addr; if (vmalloc_area_pages((unsigned long) addr, size, GFP_KERNEL|_ _GFP_HIGHMEM, 0x63)) { vfree(addr); return NULL; 功能:分配非连续存储区 数据结构: } VM_ALLOC表示线性地址的区间将用于非连续存储器分配 return addr; 其它函数: } vmalloc_area_pages():请求非连续的页框
vfree():释放非连续存储区

非连续存储器区管理( 非连续存储器区管理(续)
vmalloc_area_pages的代码片段: end = address + size; … dir = pgd_offset_k(address); spin_lock(&init_mm.page_table_lock); … while (address < end) { pmd_t *pmd = pmd_alloc(&init_mm, dir, address); ret = -ENOMEM; if (!pmd) break; if (alloc_area_pmd(pmd, address, end - address, gfp_mask, prot)) break; address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; ret = 0; } spin_unlock(&init_mm.page_table_lock); return ret;
调用关系:vmalloc->vmalloc_area_pages vmalloc_area_pages(address,size,gfp_mask,prot) 功能:请求非连续的页框 数据结构: PGDIR_SIZE:页目录横跨的线性地址范围 其它函数: pgd_offset_k():获得内核页表的目录项 pmd_alloc():创建页中间目录 alloc_area_pmd():为新页中间目录分配相 关页表

非连续存储器区管理( 非连续存储器区管理(续)
alloc_area_pmd的代码片段: while (address < end) { pte_t * pte = pte_alloc(&init_mm, pmd, address); if (!pte) return -ENOMEM; if (alloc_area_pte(pte, address, end – address, gfp_mask, prot)) return -ENOMEM; address = (address + PMD_SIZE) & PMD_MASK; pmd++; } 调用关系:vmalloc->vmalloc_area_pages->alloc_area_pmd
alloc_area_pmd(pmd, address, size, gfp_mask, prot) 功能:创建页中间目录 数据结构: PMD_SIZE:页中间目录横跨的线性地址范围 其它函数: pte_alloc(m,p,a)返回a在中间目录项p中对应的页表项地址 alloc_area_pte():为页表中相应表项分配所有的页框

非连续存储器区管理( 非连续存储器区管理(续)
alloc_area_pte的代码片段: while (address < end) { unsigned long page; spin_unlock(&init_mm.page_table_lock); alloc_page(gfp_mask); spin_lock(&init_mm.page_table_lock); if (!page) return -ENOMEM; set_pte(pte, mk_pte(page, prot)); address += PAGE_SIZE; pte++; } 调用关系:vmalloc->vmalloc_area_pages->alloc_area_pmd->alloc_area_pte
alloc_area_pte(pte, address,size, gfp_mask, prot) 功能:为页表中的表项pte分配所有的页框 其它函数: alloc_page(gfp_mask):获得单独页框 set_pte(pteptr,pteval):把指定的值写入页表项 mk_pte(page,pgprot):接受一个线性地址和一组访问权限作为参数创建页表项

非连续存储器区管理( 非连续存储器区管理(续)
v_free的代码片段: write_lock(&vmlist_lock); for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) { if (tmp->addr == addr) { *p = tmp->next; vmfree_area_pages((unsigned long)(tmp->addr), tmp->size); write_unlock(&vmlist_lock); kfree(tmp); return; } } write_unlock(&vmlist_lock); printk("Trying to vfree( ) nonexistent vm area (%p)\n", addr); void vfree(void * addr) 功能:释放非连续存储区 其它函数: vmfree_area_pages():释放非连续的页框 kfree():释放描述符

非连续存储器区管理( 非连续存储器区管理(续)
vmfree_area_pages的代码片段: dir = pgd_offset_k(address); while (address < end) { free_area_pmd(dir, address, end - address); address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; }

调用关系:vfree->vmfree_area_pages
vmfree_area_pages(address,size) 功能:释放存储区 其它函数: pgd_offset_k(address):获得address在内核页表的目录项 free_area_pmd(dir,address,size):执行与alloc_area_pmd()的反操作

小结

? 对连续内存区的管理
– 基于区的伙伴系统 – Slab分配器 分配器

? 对非连续内存区的管理

小结( 小结(续)

阅读要求
? include/asm-i386/pgtable-3level.h ? Include/linux/mm.h ? mm/slab.c,mm/vmalloc.c,mm/page_alloc. c

思考题
? Slab分配器为什么比其他分配器有更好的性能和内存 分配器为什么比其他分配器有更好的性能和内存 利用率 ? 在Linux存储管理中,使用了哪几个重要的数据结构? 存储管理中, 存储管理中 使用了哪几个重要的数据结构? ? Linux内核已经考虑到当空闲内存变得紧缺时,由另一 内核已经考虑到当空闲内存变得紧缺时, 内核已经考虑到当空闲内存变得紧缺时 个任务发出的内存分配请求来调用函数 try_to_free_pages(),释放被占用的内存页框,为什么 ,释放被占用的内存页框, 又设计一个专门的内核线程kswad定期进行页换出操 又设计一个专门的内核线程 定期进行页换出操 作? ? Linux如何将三层模型转换到 如何将三层模型转换到i386的二层映射? 的二层映射? 如何将三层模型转换到 的二层映射 ? 假设把 位的虚地址分成四个域,前三个域用于实现 假设把32位的虚地址分成四个域 位的虚地址分成四个域, 三级页表,最后一个域是偏移量。请问: 三级页表,最后一个域是偏移量。请问:虚拟地址空 间的页面数由哪几个域决定?页面的大小由谁决定? 间的页面数由哪几个域决定?页面的大小由谁决定?

Q&A

本讲结束 !


相关文章:
第4章存储管理
3.存储管理主要有四大功能,即( 内存的分配与回收 )( 地址转换 )( 内存共享和保护 、、 )和( 内存扩充 )。 4.内存扩充技术主要有( 覆盖 )和( 交换 )。 ...
第四讲 时间管理与习惯(上)
四讲 时间管理与习惯(上)_管理学_高等教育_教育专区。第四讲 时间管理与习惯(上)习惯是无意识的重复性行动,好的习惯能产生很大的效益,不良的习惯会有很大的...
第四讲 等差数列及其应用
四讲 等差数列及其应用_四年级数学_数学_小学教育_教育专区。第四讲 等差数列及其应用 许多同学都知道这样一个故事: 大数学家高斯在很小的时候, 就利用巧妙的...
第四讲 物理存储器与进程逻辑地址空间的管理
四讲 物理存储器与进程逻辑地址空间的管理_计算机硬件及网络_IT/计算机_专业资料。操作系统实验,物理存储器与进程逻辑地址空间的管理 ...
(第四讲 物理存储器与进程逻辑地址空间的管理)
技术 学生所在学院 计算科学与技术 指导教师 实验室名称地点 21B276 哈尔滨工程大学 计算机科学与技术学院 第四讲 物理存储器与进程逻辑地址空间的管理一、实验概述 ...
第四讲测验
四讲测验_教学研究_教育专区。第四讲测验返回 本次得分为:, 本次测试的...A 你没选择任何选项 4 单选(0.25 分) 有时候幽默与玩笑在冲突管理中也有特殊...
《管理者如何进行沟通与激励》(第四讲)
管理者如何进行沟通与激励,帮助你成为管理者中的管理管理者如何进行沟通与激励,帮助你成为管理者中的管理者隐藏>> 第四讲 有效沟通建议二培养有效倾听技能沟通的...
第四讲 管理就要贯彻到底
四讲 管理就要贯彻到底二、贯彻到位的八项修炼 树干叫智商;树枝叫情商;树叶叫逆境商 果实叫财商;树根叫心商;土壤叫悟性 底下的水叫灵商;太阳叫环境 三、我行...
第4章存储管理作业题答案
4存储管理作业题答案_管理学_高等教育_教育专区。操作系统第4存储管理作业题答案 第四章 存储管理一、单项选择题 1. 在存储管理方案中,___可与覆盖技术...
作业四讲评
作业四讲评_管理学_高等教育_教育专区。工商管理专业《资本经营》作业讲评 4 第四次平时作业 名词解释(每小题 4 分,共 20 分) 1、 跨国资本经营 动。 2、...
更多相关标签: