05.内存管理:动态申请和释放内存

news2024/10/6 22:23:19

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

参考:
伙伴算法原理简介
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_desc free_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; // 還原爲0
        bdesc->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; // 還原爲0
        bdesc->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;
    }

完整图示如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/679908.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先 / LeetCode 235. 二叉搜索树的最近公共祖先(二叉搜索树性质,搜索与回溯)

题目&#xff1a; 链接&#xff1a;剑指 Offer 68 - I. 二叉搜索树的最近公共祖先&#xff1b;LeetCode 235. 二叉搜索树的最近公共祖先 难度&#xff1a;中等 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对…

redis存储原理与数据模型学习笔记

目录 1 redis线程模型1.1 线程组成1.2 redis命令处理是单线程 2 redis db 存储分析2.1 先了解代码server.hdict.h 2.2 从kv存储分析2.3 负载因子2.4 渐进式rehash机制数据访问scan 3 数据模型分析以zset为例跳表 1 redis线程模型 1.1 线程组成 redis-server 命令处理 网络事件…

Nerf论文前后介绍以及今后方向(2020年各个方向工作论文分析) NEURAL VOLUME RENDERING:NERF AND BEYOND

你好&#xff01; 这里是“出门吃三碗饭”本人&#xff0c; 本文章接下来将介绍2020年对Nerf工作的一篇总结论文NEURAL VOLUME RENDERING:NERF AND BEYOND&#xff0c;论文作者是佐治亚理工学院的Frank Dellaert同学和 MIT的Lin Yen-Chen同学&#xff0c;非常感谢两位大佬的总结…

序列化和反序列化的认识【protobuf、json、xml】

1. 什么是序列化与反序列化&#xff1f; 程序员在编写应用程序的时候往往需要将程序的某些数据存储在连续的内存中&#xff0c;然后将其写入文件或是将其传输到网络中的另一台计算机上以实现通讯。这个将程序数据转换成能被存储并传输的格式的过程被称为序列化&#xff08;seri…

C++ array (STL array) 序列容器

文章目录 1 类模板2 begin()/end() 和 cbegin()/cend()3 cbegin() 和 cend()4 rbegin()/rend() 和 crbegin()/crend()5 访问array容器中单个元素参考 1 类模板 array 容器以类模板的形式定义在 头文件&#xff0c;并位于命名空间 std 中&#xff0c;如下所示&#xff1a; nam…

一、约束编程简介(Constraints Programming)

文章目录 1、约束编程求解器2、值域传播器THE END 1、约束编程求解器 \qquad 约束编程是一种编程范式&#xff0c;旨在解决那些需要满足一系列约束条件的问题。在约束编程中&#xff0c;问题被定义为一组变量和约束条件&#xff0c;而不是指定解决问题的算法步骤。通过定义变量…

XXE漏洞复现步骤

0X00XXE注入定义 XXE注入&#xff0c;即XML External Entity&#xff0c;XML外部实体注入。通过 XML 实体&#xff0c;”SYSTEM”关键词导致 XML 解析器可以从本地文件或者远程 URI 中读取数据。所以攻击者可以通过 XML 实体传递自己构造的恶意值&#xff0c;是处理程序解析它…

Nginx-负载均衡详解

本文已收录于专栏 《中间件合集》 目录 概念说明什么是Nginx什么是负载均衡 功能介绍配置过程1.修改nginx配置文件添加服务组修改HTTP模块 2.保存配置文件3.重启配置文件4.查看配置文件是否重启成功5.还可以配置其他策略轮询权重最少连接数 常用命令总结提升 概念说明 了解ngi…

MySQL:环境安装和数据库基础

环境安装 说明&#xff1a; • 安装与卸载中&#xff0c;用户全部切换成为root&#xff0c;⼀旦 安装&#xff0c;普通用户能使⽤的 • 初期练习&#xff0c;mysql不进行用户管理&#xff0c;全部使⽤root进行&#xff0c;尽快适应mysql语句&#xff0c;后⾯学了用户管 理&a…

实例讲解,一文弄懂workqueue和waitqueue

本期主题&#xff1a; 讲清workqueue和waitqueu&#xff1a; 从中断讲起waitqueue是什么workqueue总结 往期链接&#xff1a; linux设备驱动中的并发linux设备驱动中的编译乱序和执行乱序linux设备驱动之内核模块linux字符驱动linux字符驱动之ioctl部分linux字符驱动之read、…

数据结构07:查找[C++][朴素二叉排序树BST]

图源&#xff1a;文心一言 考研笔记整理8k字&#xff0c;小白友好、代码可跑&#xff0c;请小伙伴放心食用~~&#x1f95d;&#x1f95d; 第1版&#xff1a;查资料、写BUG、画导图、画配图~&#x1f9e9;&#x1f9e9; 参考用书&#xff1a;王道考研《2024年 数据结构考研复习…

【MATLAB第45期】基于MATLAB的深度学习SqueezeNet卷积神经网络混凝土裂纹图像识别预测模型

【MATLAB第45期】基于MATLAB的深度学习SqueezeNet卷积神经网络混凝土裂纹图像识别预测模型 引言 该文章展示如何微调名为SqueezeNet的预训练深度卷积网络&#xff0c;以执行裂纹图像分类预测。并使用一种称为Grad-CAM的技术来解释和分析分类输出。文章使用L.Zhang介绍的混凝土…

C++初阶之C++入门最全详解

C入门 1. C关键字&#xff08;C98&#xff09;2. 命名空间2.1 命名空间定义2.2 命名空间使用 3. C输入&输出4. 缺省参数4.1 缺省参数概念4.2 缺省参数分类 5. 函数重载5.1 函数重载概念5.1.1 参数类型不同5.1.2 参数个数不同5.1.3 参数类型顺序不同 5.2 C支持函数重载的原理…

Spring Cloud Alibaba Seata(二)

目录 一、Seata 1、Seata-AT模式 1.1、具体案例 1.2、通过Seata的AT模式解决分布式事务 2、Seata-XA模式 3、Seata-TCC模式 4、Seata-SAGA模式 一、Seata 1、Seata-AT模式 概念&#xff1a;AT模式是一种无侵入的分布式事务解决方案&#xff0c;在 AT 模式下&#xff0c…

git修改默认主分支main为master和设置git默认创建的项目默认分支都为master

文章目录 前言一、设置新建仓库默认分支为master1.点击GitHub右上角的头像2. 选中settings&#xff08;设置&#xff09;3.点击Repositories&#xff08;存储库&#xff09;4.更改main为master后点击update 二、设置已建仓库的默认分支为master1.找到你要改的项目点击settings&…

STL序列式容器的概念

文章目录 1 迭代器2 什么是序列式容器3 序列式容器容器中常见的函数成员参考 1 迭代器 迭代器和C指针非常类似&#xff0c;它可以是需要的任意类型&#xff0c;通过迭代器可以指向容器中的某个元素&#xff0c;如果需要&#xff0c;还可以对该元素进行读写操作。 迭代器类别 …

ThreeJS案例一——在场景中添加视频,使用人物动作以及用键盘控制在场景中行走的动画

准备 首先我们需要两个模型&#xff0c;一个是场景模型&#xff0c;另一个是人物模型。 人物模型我这里用的Threejs官网中的给的模型&#xff0c;名称是Xbot.glb。 当然人物模型也可以自己去这个网站下载sketchfab&#xff0c;下载后给模型添加动画mixamo 下载模型动画 先让…

C++ STL vector容器用法

文章目录 1 vector初始化方法2 vector容器迭代器3 data()函数4 emplace_back()和push_back()的区别5 insert()函数6 vector删除元素参考 1 vector初始化方法 方式1&#xff1a; std::vector<double> values;//创建空的vcetor values.reserve(20); //设置容器的内存分配…

【实战】 JWT、用户认证与异步请求(1) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(四)

文章目录 一、项目起航&#xff1a;项目初始化与配置二、React 与 Hook 应用&#xff1a;实现项目列表三、TS 应用&#xff1a;JS神助攻 - 强类型四、JWT、用户认证与异步请求1.login2.middleware of json-server3.jira-dev-tool&#xff08;imooc-jira-tool&#xff09;安装问…

《流浪地球 2》的硬核黑科技

电影中&#xff0c;由刘德华饰演的量子计算机工程师图恒宇有一个惊心动魄的情节。为了同步启动全球地球发动机&#xff0c;需要重启互联网&#xff0c;避免地壳破碎和地质灾害。而重启互联网的关键则是要启动“根服务器”。电影中没有具体交代是什么根服务器&#xff0c;但是当…