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

news/2024/11/24 11:03:59/

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

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

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


http://www.ppmy.cn/news/551477.html

相关文章

Linux2.基础指令(下)

1.uname -r :输出Linux内核版本信息。 2.linux2.6.*内核默认支持的文件系统有ext3,ext2,ext4,xfs&#xff0c;不支持ufs。 3.linux查看CPU占用的命令:top。 4.题目 5.题目 6.题目 7.重定向 echo "字符串1" :在屏幕上打印字符串1。 echo "字符串1" &g…

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

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

java GB28181视频监控 大华 海康摄像机国标对接源码源代码程序

java GB28181视频监控 大华 海康摄像机国标对接源码源代码程序 WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的网络视频平台&#xff0c;负责实现核心信令与设备管理后台部分&#xff0c;支持NAT穿透&#xff0c;支持海康、大华、宇视等品牌的IPC、NVR、DVR接入。支持国标…

Da黄蜂vep云课堂6.05录屏截屏提取为mp4教程

最近下载了一套视频&#xff0c;视频是vep加密的&#xff0c;只有通过 大 黄 蜂 云课堂才能播放。 经过了解知道vep的加密视频是重编码加密&#xff0c;所以几乎很难破解。 所以给大家找了一款 大 黄蜂云课堂6.05内部版&#xff0c;可直接翻录为mp4格式。只要本地能播放就可以…

【双目相机】python使用双目摄像头录像、调用摄像头、调用视频

1.调用摄像头 #读取摄像头 import cv2 capcv2.VideoCapture(0) #capcv2.VideoCapture(output.avi) if not cap.isOpened():print("Cannot open camera")exit() while True:# 逐帧捕获ret,framecap.read()# 如果正确读取帧&#xff0c;ret为Trueif not ret:break# 显…

android调用摄像头录像的代码(录像并生成mp4文件)

直接给代码吧&#xff0c;首先是MainActivity&#xff1a; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat;import android.Manifest; import…

EasyCVR实时录像接口教程:如何获取国标接入的摄像头设备录像?

EasyCVR视频融合云服务平台支持多协议、多类型的设备接入&#xff0c;包括主流标准协议国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK&#xff0c;如海康Ehome、海康SDK、大华SDK等&#xff0c;对外可分发RTSP、RTMP、FLV、HLS、WebRTC等格式的高清/超高清…

Qt海康威视二次开发,摄像头,抓图,预览,布防,录像,停止录像

多说无意义 直接看图 下面有下载链接 这个VIP是别人帮我开的 VIP文章也不是我设置的 是万恶的资本家CSDN弄得 如果看不了 不要骂我 下面也就是几张演示图 和下载链接 我放到前面来了 环境是VS2013Qt5.6.2 CSDN下载链接 CSDN 完整开发包和海康的库 okjokullgmail.com 增加…