动态分配内存,进行内存管理

参考:
伙伴算法原理简介
linux 0.11源码

本文主要针对Linux0.11的malloc和free进行分析。是一种类似伙伴系统的内存管理方法,不过伙伴系统的内存通常是申请大于一页的内存,但是在该内核版本的内存管理,申请的内存是小于一页的。

1.结构体

桶描述符bucket_desc ,用于关联物理页page,和空闲内存freeptr。bucket_size桶的大小用于确定每次分配的内存大小。同时桶描述符会进行链表连接bucket_desc。

struct bucket_desc { /* 16 bytes */void              *page;          // 管理的物理页struct bucket_desc *next;          // 下一个bucket地址void              *freeptr;       // 下一个可供分配的unsigned short       refcnt;         // 引用计数,释放物理页时要用unsigned short       bucket_size;    // 每个桶的大小
};
/** This contains a linked list of free bucket descriptor blocks 这包含空闲bucket描述符块的链接列表*/
struct bucket_desc *free_bucket_desc = (struct bucket_desc *) 0;

桶目录,用于确定分配的内存大小和与桶描述符进行关联。bucket_dir的size是2的n次幂,最大是4096,即为1页大小(4K),当超过1页时,无法分配。

struct _bucket_dir { /* 8 bytes */int            size;struct bucket_desc *chain;
};struct _bucket_dir bucket_dir[] = {{ 16, (struct bucket_desc *) 0},{ 32, (struct bucket_desc *) 0},{ 64, (struct bucket_desc *) 0},{ 128,    (struct bucket_desc *) 0},{ 256,    (struct bucket_desc *) 0},{ 512,    (struct bucket_desc *) 0},{ 1024, (struct bucket_desc *) 0},{ 2048, (struct bucket_desc *) 0},{ 4096, (struct bucket_desc *) 0},{ 0,    (struct bucket_desc *) 0}
};   /* End of list marker */

2.初始化存储桶描述页

static inline void init_bucket_desc()
{struct bucket_desc *bdesc, *first;int  i;first = bdesc = (struct bucket_desc *) get_free_page();if (!bdesc)return ;for (i = P_SIZE/sizeof(struct bucket_desc); i > 1; i--) {    // 進行拼接bdesc->next = bdesc+1;bdesc++;}/** This is done last, to avoid race conditions in case* get_free_page() sleeps and this routine gets called again....* 这是最后完成的,以避免在get_free_page()休眠并且再次调用此例程时出现竞争条件。。。*/bdesc->next = free_bucket_desc;free_bucket_desc = first;
}

init_bucket_desc会申请一个物理页,用于存储bucket_desc 描述符,如下图所示:

申请的物理页会存储256(4096 / 16)个桶描述符bucket_descfree_bucket_desc会指向第一个空闲的bucket_desc

3.申请内存

void* malloc(size_t len) {struct _bucket_dir *bdir;struct bucket_desc    *bdesc;void         *retval;/** First we search the bucket_dir to find the right bucket change* for this request.*/// 首先,我们搜索bucket_dir以找到此请求的正确bucket更改。for (bdir = bucket_dir; bdir->size; bdir++) //找到合适的大小的slab链表if (bdir->size >= len)break;if (!bdir->size) {printk("malloc called with impossibly large argument (%d)\n",len);return NULL;}/** Now we search for a bucket descriptor which has free space   现在我们搜索一个有空闲空间的bucket描述符*/CLI    /* Avoid race conditions    關中斷、避免竟態條件 */for (bdesc = bdir->chain; bdesc; bdesc = bdesc->next)if (bdesc->freeptr)break;/** If we didn't find a bucket with free space, then we'll* allocate a new one. 如果我们没有找到一个有空闲空间的桶,那么我们会分配一个新的。*/if (!bdesc) {char        *cp;int     i;if (!free_bucket_desc)init_bucket_desc();bdesc = free_bucket_desc;free_bucket_desc = bdesc->next; // 還原爲0bdesc->refcnt = 0;bdesc->bucket_size = bdir->size;bdesc->page = bdesc->freeptr = (void *) (cp = (char *) get_free_page());if (!cp)return NULL;/* Set up the chain of free objects 设置空閒对象链*/for (i=P_SIZE/bdir->size; i > 1; i--) {char *next_block = cp + bdir->size;   // 计算下一个内存块的地址*(char**) cp = next_block; // 在 cp 所指向的内存块中存储下一个内存块的地址cp += bdir->size;}*((char **) cp) = 0;bdesc->next = bdir->chain; /* OK, link it in! */bdir->chain = bdesc;}retval = (void *) bdesc->freeptr;   // 下一个可供分配的bdesc->freeptr = *((void **) retval);bdesc->refcnt++;STI   /* OK, we're safe again */return(retval);
}

假设使用malloc申请一个8字节的内存:malloc(8):

  • 1.搜索bucket_dir,找到第一个size > 8的_bucket_dir
    for (bdir = bucket_dir; bdir->size; bdir++) //找到合适的大小的slab链表if (bdir->size >= len)break;if (!bdir->size) {printk("malloc called with impossibly large argument (%d)\n",len);return NULL;}


16 > 8,bucket_dir[0]满足条件。

  • 2.现在搜索一个有空闲空间的bucket描述符
    for (bdesc = bdir->chain; bdesc; bdesc = bdesc->next)if (bdesc->freeptr)break;
  • 3.如果没有找到一个有空闲空间的桶,那么会分配一个新的。
    if (!bdesc) {char        *cp;int     i;if (!free_bucket_desc)init_bucket_desc();bdesc = free_bucket_desc;free_bucket_desc = bdesc->next; // 還原爲0bdesc->refcnt = 0;bdesc->bucket_size = bdir->size;bdesc->page = bdesc->freeptr = (void *) (cp = (char *) get_free_page());if (!cp)return NULL;/* Set up the chain of free objects 设置空閒对象链*/for (i=P_SIZE/bdir->size; i > 1; i--) {char *next_block = cp + bdir->size;   // 计算下一个内存块的地址*(char**) cp = next_block; // 在 cp 所指向的内存块中存储下一个内存块的地址cp += bdir->size;}*((char **) cp) = 0;bdesc->next = bdir->chain; /* OK, link it in! */bdir->chain = bdesc;}

首先会判断 free_bucket_desc是否为0,如果为0说明未初始化,则需要进行初始化。当完成初始化之后,将free_bucket_desc赋值给桶描述符bdesc,并将free_bucket_desc重新赋值为bdesc->next,以此进行关联。并申请一个物理页get_free_page()bdesc关联。再通过for (i=P_SIZE/bdir->size; i > 1; i--)设置空閒对象链。

  • 4.返回空余内存retval,并将 bdesc->freeptr指向下一个可分配的。
    完整图示如下:

4.释放内存

  • 1.计算此对象所在的页面
    page = (void *)  ((unsigned long) obj & 0xfffff000);
  • 2.搜索该页面的bucket
    for (bdir = bucket_dir; bdir->size; bdir++) {prev = 0;/* If size is zero then this conditional is always false 如果大小为零,则此条件始终为false*/if (bdir->size < size)continue;for (bdesc = bdir->chain; bdesc; bdesc = bdesc->next) {if (bdesc->page == page)goto found;prev = bdesc;}}return;
  • 3.释放内存,并将引用计数减一。
    *((void **)obj) = bdesc->freeptr;bdesc->freeptr = obj;bdesc->refcnt--;
  • 4.如果引用计数为0,释放整个页面page。
    if (bdesc->refcnt == 0) {/** We need to make sure that prev is still accurate.  It* may not be, if someone rudely interrupted us....*/if ((prev && (prev->next != bdesc)) ||(!prev && (bdir->chain != bdesc)))for (prev = bdir->chain; prev; prev = prev->next)if (prev->next == bdesc)break;if (prev)prev->next = bdesc->next;else {if (bdir->chain != bdesc)return;bdir->chain = bdesc->next;}free_page( bdesc->page);bdesc->next = free_bucket_desc;free_bucket_desc = bdesc;}

完整图示如下:

05.内存管理:动态申请和释放内存相关推荐

  1. C++之内存管理:申请与释放

    目录 前言 1.C/C++内存分布 1.1虚拟内存分段 1.2理解一些概念 1.2.1栈帧向下增长 1.2.2堆向上生长 1.2.3栈和堆会碰撞吗? 1.2.4关于const的说明 2.C语言中动态内 ...

  2. php的内存划分,解析PHP中的内存管理,PHP动态分配和释放内存

    摘要 内存管理对于长期运行的程序,例如服务器守护程序,是相当重要的影响:因此,理解PHP是如何分配与释放内存的对于创建这类程序极为重要.本文将重点探讨PHP的内存管理问题. 一. 内存 在PHP中,填 ...

  3. C++内存管理——空间申请、释放的新玩法

    malloc free与new delete的不同 1.底层原理上的差异 malloc 只是在堆上开辟空间(以字节为单位),free配合malloc进行空间的释放 new针对自定义类型,会先调用ope ...

  4. 【C 语言】内存管理 ( 动态内存分配 | 栈 | 堆 | 静态存储区 | 内存布局 | 野指针 )

    相关文章链接 : 1.[嵌入式开发]C语言 指针数组 多维数组 2.[嵌入式开发]C语言 命令行参数 函数指针 gdb调试 3.[嵌入式开发]C语言 结构体相关 的 函数 指针 数组 4.[嵌入式开发 ...

  5. 内存管理-动态分区分配方式模拟

    内存管理 - 动态分区分配方式模拟 操作系统第二次课程作业 - 动态分区分配方式模拟 项目需求 假设初始态下,可用内存空间为640K,并有下列请求序列,请分别用首次适应算法和最佳适应算法进行内存块的分 ...

  6. android内存池,两种常见的内存管理方法:堆和内存池

    描述 本文导读 在程序运行过程中,可能产生一些数据,例如,串口接收的数据,ADC采集的数据.若需将数据存储在内存中,以便进一步运算.处理,则应为其分配合适的内存空间,数据处理完毕后,再释放相应的内存空 ...

  7. Spark内存管理(1)—— 静态内存管理

    Spark内存管理简介 Spark从1.6开始引入了动态内存管理模式,即执行内存和存储内存之间可以相互抢占  Spark提供了2种内存分配模式: 静态内存管理 统一内存管理 本系列文章将分别对这两种内 ...

  8. 从内存管理原理,窥探OS内存管理机制

    摘要:本文将从最简单的内存管理原理说起,带大家一起窥探OS的内存管理机制,由此熟悉底层的内存管理机制,写出高效的应用程序. 本文分享自华为云社区<探索OS的内存管理原理>,作者:元闰子 . ...

  9. 两种常见的内存管理方法:堆和内存池

    在程序运行过程中,可能产生一些数据,例如,串口接收的数据,ADC采集的数据.若需将数据存储在内存中,以便进一步运算.处理,则应为其分配合适的内存空间,数据处理完毕后,再释放相应的内存空间.为了便于内存 ...

最新文章

  1. kset_create_and_add
  2. python编程基础是什么-编程学习第一步,让你20天搞定Python编程
  3. 产品层级提升,产品实战系列之教你如何对货运APP进行运单界面优化
  4. windows 执行bat脚本
  5. python程序设计语言的执行方式_编程语言用Python执行程序的4种方式
  6. 【qduoj - 纳新题】小明的dp(快速幂 + 乘法原理)(简单组合数学)
  7. Competitive Programming 3题解
  8. 关于easyui combobox下拉框实现多选框的实现
  9. 痛与快乐有一个代码是什么_痛与快乐有一个代码是什么_痛苦与快乐
  10. League of Legends 通过 游戏ID查询玩家QQ号码。
  11. 收集的13个杀毒软件和安全防护软件(有图哦)
  12. 【算法】leetcode887鸡蛋掉落题之方法二解析
  13. 惊爆:江民公司官方网站今日被黑
  14. 南航里程每年清空吗_速度用!南航里程即将大量贬值!还有每年3张南航处卡!...
  15. freeswitch-PSTN
  16. Chapter 5 (Eigenvalues and Eigenvectors): The characteristic equation (特征方程)
  17. C 语言 某人在国外留学,不熟悉当地的天气预报中的华氏温度值,请编程按每隔10°输出0°到300°之间的华氏温度的对照表,方便他对照查找
  18. 大牛在谷歌工作十年的总结
  19. Excel的简单编程
  20. 用python分析小说_用Python分析《斗破苍穹》

热门文章

  1. 利用ffmpeg进行音频转码
  2. 文档翻译(0)-Boost软件许可证
  3. pl/sql developer登录oracle初始化失败问题
  4. python语言基础与应用慕课课堂测试_Python语言基础与应用_中国大学mooc慕课_期末考试选修课答案...
  5. 获取时间戳(1970年1月1日零点整至当前时间的毫秒数)
  6. http请求415错误Unsupported Media Type
  7. 电脑出现initialization failure:0x0000000c的问题
  8. 2007年肯定火的歌 等一分钟
  9. 2010-09-12
  10. 题目七:重要的话说三遍