内核版本:linux-2.6.11


  • 伙伴系统

伙伴系统是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内核笔记——内存管理之块内存分配的更多相关文章

  1. Linux内核笔记——进程管理之执行体

    内核版本:linux-2.6.11 在Linux中,有多种执行体(指令流.执行单位),它们是CPU调度和分配资源的基本单位,它们是内核态可见的,即内核态下,每一种执行体都有对应的唯一数据结构task_ ...

  2. Linux内核笔记--内存管理之用户态进程内存分配

    内核版本:linux-2.6.11 Linux在加载一个可执行程序的时候做了种种复杂的工作,内存分配是其中非常重要的一环,作为一个linux程序员必然会想要知道这个过程到底是怎么样的,内核源码会告诉你 ...

  3. Linux内核笔记——内存管理之slab分配器

    内核版本:linux-2.6.11 内存区和内存对象 伙伴系统是linux用于满足对不同大小块内存分配和释放请求的解决方案,它为slab分配器提供页框分配请求的实现. 如果我们需要请求具有连续物理地址 ...

  4. 【转载】linux内核笔记之高端内存映射

    原文:linux内核笔记之高端内存映射 在32位的系统上,内核使用第3GB~第4GB的线性地址空间,共1GB大小.内核将其中的前896MB与物理内存的0~896MB进行直接映射,即线性映射,将剩余的1 ...

  5. Linux中的Buffer Cache和Page Cache echo 3 > /proc/sys/vm/drop_caches Slab内存管理机制 SLUB内存管理机制

    Linux中的Buffer Cache和Page Cache echo 3 > /proc/sys/vm/drop_caches   Slab内存管理机制 SLUB内存管理机制 http://w ...

  6. 内存管理概述、内存分配与释放、地址映射机制(mm_struct, vm_area_struct)、malloc/free 的实现

    http://blog.csdn.net/pi9nc/article/details/23334659 注:本分类下文章大多整理自<深入分析linux内核源代码>一书,另有参考其他一些资料 ...

  7. 【转载】linux内核笔记之进程地址空间

    原文:linux内核笔记之进程地址空间 进程的地址空间由允许进程使用的全部线性地址组成,在32位系统中为0~3GB,每个进程看到的线性地址集合是不同的. 内核通过线性区的资源(数据结构)来表示线性地址 ...

  8. 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配

    垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己 ...

  9. Win3内存管理之私有内存跟共享内存的申请与释放

    Win3内存管理之私有内存跟共享内存的申请与释放 一丶内存简介私有内存申请 通过上一篇文章.我们理解了虚拟内存与物理内存的区别. 那么我们有API事专门申请虚拟内存与物理内存的. 有私有内存跟共享内存 ...

随机推荐

  1. Dijkstra 算法

    all the nodes should be carectorized into three groups: (visited, front, unknown) we should pay spec ...

  2. 【新手总结】在.Net项目中使用Redis作为缓存服务

    最近由于项目需要,在系统缓存服务部分上了redis,终于有机会在实际开发中玩一下,之前都是自己随便看看写写,很零碎也没沉淀下来什么,这次算是一个系统学习和实践过程的总结. 和Redis有关的基础知识 ...

  3. intellij idea Maven 创建项目时出现的一些问题

    1.关于maven仓库的问题 在下载资源的时候特别慢,原因是因为天朝的网络你们都懂的.解决方式使用国内镜像,原本可以用的OSChina的镜像,由于其服务器关闭,现在无法使用. 解决方案是使用阿里云的m ...

  4. Linux远程执行Shell命令或脚本

    ## 远程执行shell命令 ssh [user]@[server] '[command]' # eg. ssh root@192.168.1.1 'uptime' ## 远程执行本地shell脚本 ...

  5. C#多线程 线程的启动

    在实例化Thread的实例,需要提供一个委托,在实例化这个委托时所用到的参数是线程将来启动时要运行的方法.在.net中提供了两种启动线程的方式,一种是不带参数的启动方式,另一种是带参数的启动的方式. ...

  6. C语言小结

    1.输入输出: char s; printf("Enter a string"); scanf("%s",&s); printf(''Hello,%s& ...

  7. window下安装anaconda ipython和spyder都打不开

    1. 环境 win7 64位,软件是Anaconda2-4.1.1-Windows-x86_64.exe 2. 出现的问题 ipython打不开,一闪而过 spyder点击没有反应 anaconda ...

  8. VC++ 设置软件开机自启动的方法

    0  概述 软件开机自启动是比较常用的做法,设置方法也有好几种. 1  使用者模式 在"开始菜单"的所有程序中有个"启动"文件夹,可以将需要设置为开机启动的应用 ...

  9. RecyclerView的使用之多种Item加载布局

    精益求精,为了更加透彻熟练得掌握,本文再次给大家介石介绍下如何利用RecyclerView实现多Item布局的加载,多Item布局的加载的意思就是在开发过程中List的每一项可能根据需求的不同会加载不 ...

  10. Redis之清除所有缓存

    方法: /// <summary> /// 清除redis所有缓存 /// </summary> /// <param name="redisUrl" ...