Linux内核笔记——内存管理之块内存分配
2015-09-12 14:57
330 查看
内核版本:linux-2.6.11
管理区描述符里,有一个元素数为11的
例如,该数组第三个元素的
__rmqueue,传入参数为order和管理区zone的描述符指针。
该函数会遍历
list_entry是一个宏,功能是取得一个成员变量的父结构体的指针,这里的意思是获得第一个参数
接着从链表中删除它的这一个页框描述符,并减少管理区描述符中的
从上述过程可以知道,在获取order对应大小的空闲块时,如果没有正好大小的空闲块,那么将会从更大的空闲块中分离出order对应大小的空闲块,分离后剩下的部分要按照伙伴系统的规则正确的记录到管理区描述符的free_area数组中,
至此,已经找到了需要的空闲块,返回其中的第一个页框描述符指针。
__free_pages_bulk,传入参数依次为要释放块的第一个页框描述符的指针,管理区的
除去一些报错的代码后,主要过程就是寻找这个释放块的伙伴,并将二者合并。
函数实现部分,寻找伙伴这部分的代码理解起来比较晦涩,但十分聪明
page_idx是释放块在
循环结束后,page_idx记录了合并块的第一个页框描述符在
至此,块内存释放结束。
伙伴系统
伙伴系统是linux用于满足对不同大小块物理内存分配和释放请求的解决方案。内存管理区
linux将物理内存分成三个内存管理区,分别为ZONE_DMA
ZONE_NORMAL
ZONE_HIGHMEM,并使用三个管理区描述符管理这三个ZONE。
管理区描述符里,有一个元素数为11的
free_area数组,分别对应1、2、4、8、16.....不同块的大小,其中的每个元素的类型都是一个名为free_area的结构体,代码位置
mm/mmzone.h
struct free_area { struct list_head free_list; unsigned long nr_free; };
例如,该数组第三个元素的
free_list保存了2的4次方即16个页框大小的空闲块链表,
nr_free保存了空闲块的数量。
块内存分配函数
代码位置mm/page_alloc.c
__rmqueue,传入参数为order和管理区zone的描述符指针。
static struct page *__rmqueue(struct zone *zone, usigned int order);
该函数会遍历
2的order次方个页框大小的块空闲链表,若该链表空,则将order+1继续遍历,找到非空的空闲块链表后,取得它的第一个页框描述符。
page = list_entry(area->free_list.next, struct page, lru);
list_entry是一个宏,功能是取得一个成员变量的父结构体的指针,这里的意思是获得第一个参数
area->free_list.next所在的page结构体的指针。
接着从链表中删除它的这一个页框描述符,并减少管理区描述符中的
free_pages的值。
从上述过程可以知道,在获取order对应大小的空闲块时,如果没有正好大小的空闲块,那么将会从更大的空闲块中分离出order对应大小的空闲块,分离后剩下的部分要按照伙伴系统的规则正确的记录到管理区描述符的free_area数组中,
expand函数在做这件事的同时,将我们需要的空闲块的第一个页框描述符的private字段设置成order(因为这个private仍然是空闲块未分割时的order值)。
return expand(zone, page, order, current_order, area);
至此,已经找到了需要的空闲块,返回其中的第一个页框描述符指针。
块内存释放函数
代码位置mm/page_alloc.c
__free_pages_bulk,传入参数依次为要释放块的第一个页框描述符的指针,管理区的
zone_mem_map字段(即管理区中的第一个页框描述符指针),管理区描述符,释放块的order值
static inline void __free_pages_bulk (struct page *page, struct page *base, struct zone *zone, unsigned int order)
除去一些报错的代码后,主要过程就是寻找这个释放块的伙伴,并将二者合并。
函数实现部分,寻找伙伴这部分的代码理解起来比较晦涩,但十分聪明
buddy_idx = (page_idx ^ (1 << order)); buddy = base + buddy_idx;
page_idx是释放块在
zone_mem_map中的下标,为了寻找它的伙伴块在
zone_mem_map中的下标,将page_idx与块大小进行异或操作,该步骤可以将page_idx的第order位取反,即可以得到比page_idx高一个块大小的下标或者比page_idx低一个块大小的下标,这个下标值就是释放块的伙伴块在
zone_mem_map中的下标。接着调用函数
page_is_buddy来检查该伙伴块是否可以合并,若不行,退出循环,若行,将order+1并继续循环寻找是否有更大的伙伴块可以合并。
coalesced = base + page_idx; set_page_order(coalesced, order); list_add(&coalesced->lru, &zone->free_area[order].free_list); zone->free_area[order].nr_free++;
循环结束后,page_idx记录了合并块的第一个页框描述符在
zone_mem_map中的下标,加上base即
zone_mem_map本身的地址后,得到合并块的第一个页框描述符的指针coalesced(这单词也够隐晦),该合并块成为最终的释放块,更新第一个页框描述符的private字段,并将其插入到适当链表,最后再增加该链表空闲块数量。
至此,块内存释放结束。
相关文章推荐
- Linux 系统安全 及 lnmp 安装
- linux 学习笔记之权限管理命令
- linux学习笔记之文件搜索命令
- linux 学习笔记之文件的压缩和解压
- linux 学习之帮助命名
- 个人学习笔记---linux原子操作的实现原理
- Linux下彻底卸载mysql数据库
- centos安装git客户端以及常用指令
- 使用iptables配置linux服务器请求转发
- linux关闭sendmail服务
- 查看Linux系统版本信息方法
- centos卸载系统自带JDK版本
- centOS安装JDK
- Linux简介
- Linux目录
- linux进程管理工具二
- linux模块驱动之led(ioremap)
- Linux下MySql忘记 root密码的解决办法
- 【Linux】Ubuntu14.04 用root登录,亲测好用
- Linux下VirtualBox安装XP,U盘、U盾无法识别解决办法