goahead源码分析---块内存分配--balloc.c文件

到目前为止,个人发现的goahead的一个特点:
1、函数执行过程中需要的缓冲区都不是由外部提供的,而是什么时候需要什么时候申请,用完后释放,如果申请的内存存放了调用者需要的信息,则将这块内存的地址通过参          数 传出去,同时由调用者负责释放。

2、在HTTP请求等地方频繁的进行了内存的申请和分配。


也许系统的malloc函数有种种缺点,goahead提供了自己的块内存分配函数,其核心文件为balloc.c文件。其设计思想如下:

1.该模块不是用来替代整个系统的内存的管理,他只用来管理程序中指定的一块内存Mem,这块内存可以是动态分配的,也可以是一个大的数组.
2.该模块以块为单位来管理内存,块的大小为16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536.分配时不时你申请多次就分配多少,而是分配一个满足你       申请的大小的最小的块.
3.这些以块为单位的内存并不是一开始就存在的,而是你申请时从Mem中划分出来,只是在你释放的时候并不返还给Mem,返还给管理模块,逐步建立起来不同的块.

4.对于大于最大块的内存,管理模块根据配置来决定是动态分配还是返回分配失败


基于上面所述,要解决如下的问题:


1.必须跟踪Mem的使用情况.系统定义了如下变量来跟踪
static char				*bFreeBuf;				/* Pointer to free memory */
static char				*bFreeNext;				/* Pointer to next free mem */
static int				bFreeSize;				/* Size of free memory */
static int				bFreeLeft;				/* Size of free left for use */


bFreeBuf在初始化的时候指向Mem,bFreeSize记录Mem的大小,bFreeLeft永远指向剩余内存的起始地址,bFreeLeft记录剩余内存的大小.


2.如何描述块内存,同时如何区分已分配的一块内存是来自管理模块分配的块还是动态分配的.系统定义了如下结构体

typedef struct {
	union {
		void	*next;							/* Pointer to next in q */
		int		size;							/* Actual requested size */
	} u;
	int			flags;							/* Per block allocation flags */
} bType;



size用来记录该块的大小(不包括管理块用的内存)
flags用来记录该块的特性,动态分配的还是块内存等.
next用来形成链表.


3.如何管理这些块
这个比较简单,将相同大小的块用next域形成链表,再定义一个数组指向不同块大小的链表的第一个元素.
static bType			*bQhead[B_MAX_CLASS];	/* Per class block q head */

bQhead就是那个数组,整体结构如下图






该模块不适用于多线程,同时假定新定义的变量初值为0.



1.模块打开函数


//参数说明:
//buf:用户指定的一块缓冲区,如果为NULL,则函数根据flags标志来决定是否自己分配
//bufsize:传入的缓冲区大小
//flags:程序中使用的为B_USE_MALLOC,允许分配内存
int bopen(void *buf, int bufsize, int flags)
{
	bFlags = flags;


	//初始化一个随机数产生器函数 不知道做什么用的
	#ifdef BASTARD_TESTING
	srand(time(0L));
	#endif /* BASTARD_TESTING */


/*
 * If bopen already called by a shared process, just increment the count
 * and return;
 */
	if (++bopenCount > 1) {
	return 0;
	}
	//如果传入的为NULL且大小为0则函数自己分配一个默认大小的内存块
	if (buf == NULL) {
		if (bufsize == 0) {
			bufsize = B_DEFAULT_MEM;
		}
//IRIX是一个操作系统 其内存要求4字节对齐
#ifdef IRIX
		bufsize = ROUNDUP4(bufsize);
#endif
		if ((buf = malloc(bufsize)) == NULL) {
         /* resetting bopenCount lets client code decide to attempt to call
          * bopen() again with a smaller memory request, should it desire to.
          * Fix suggested by Simon Byholm.
          */
         		--bopenCount;
			return -1;
		}
//记录动态分配内存的总大小,用于统计内存时使用
#ifdef B_STATS
		bStatsMemMalloc += bufsize;
#endif
	} else {
		bFlags |= B_USER_BUF;
	}


	//对于要管理的内存有总大小记录,剩余大小记录
	bFreeSize = bFreeLeft = bufsize;
	//bFreeBuf为要管理的内存块的起始地址 bFreeNext 为剩余内存的起始地址
	bFreeBuf = bFreeNext = buf;
	//对相同大小的块构成一个链表,bQhead中的每一个元素指向相应大小的可用链表的第一个块
	memset(bQhead, 0, sizeof(bQhead));
	//为了调试,可以填充分配的缓冲区为指定值
#if (defined (B_FILL) || defined (B_VERIFY_CAUSES_SEVERE_OVERHEAD))
	bFillBlock(buf, bufsize);
#endif
//不知道用处
#ifdef B_STATS
	bStackStart = &buf;
#endif
#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD
	//wzf? 此处调用此函数会有问题
	verifyFreeBlock(buf, bufsize);
#endif
	return 0;
}


2.该模块的关闭操作,当应用程序退出时调用

void bclose()
{
#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD
	verifyBallocSpace();
#endif
	if (--bopenCount <= 0 && !(bFlags & B_USER_BUF)) {
		free(bFreeBuf);
		bopenCount = 0;
	}
}


3.内存的分配
先放一张流程图





//参数说明:
//B_ARGS_DEC:该宏展开后为一个字符指针,一个整形参数,用来传入调用者所在的文件和行号
//size:请求内存的大小
//对比流程如图,比较容易理解
void *balloc(B_ARGS_DEC, int size)
{
	bType	*bp;
	int		q, memSize;

/*
 *	Call bopen with default values if the application has not yet done so
 */
	//如果没有调用过bopen函数,则在第一次使用时调用
	if (bFreeBuf == NULL) {
		if (bopen(NULL, B_DEFAULT_MEM, 0) < 0) {
			return NULL;
		}
	}
	//验证分配的内存是否有问题
#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD
	verifyBallocSpace();
#endif
	//检查请求的内存大小是否合法
	if (size < 0) {
		return NULL;
	}

	//应该是用来模拟分配失败的情况
#ifdef BASTARD_TESTING
	if (rand() == 0x7fff) {
		return NULL;
	}
#endif /* BASTARD_TESTING */

	//计算满足请求大小的最小的内存块大小和加上管理开销后的总内存大小
	memSize = ballocGetSize(size, &q);

	//如果块的大小大于最大管理的块大小则根据配置来决定是否动态分配内存
	if (q >= B_MAX_CLASS) {
/*
 *		Size if bigger than the maximum class. Malloc if use has been okayed
 */
		//允许动态分配内存
		if (bFlags & B_USE_MALLOC) {
#ifdef B_STATS
			bstats(0, NULL);
#endif
#ifdef IRIX
			memSize = ROUNDUP4(memSize);
#endif
			bp = (bType*) malloc(memSize);
			if (bp == NULL) {
				traceRaw(T("B: malloc failed\n"));
				return NULL;
			}
#ifdef B_STATS
			bStatsMemMalloc += memSize;
#endif
#if (defined (B_FILL) || defined (B_VERIFY_CAUSES_SEVERE_OVERHEAD))
			bFillBlock(bp, memSize);
#endif

		} else {
			//不允许动态分配则直接返回失败
			traceRaw(T("B: malloc failed\n"));
			return NULL;
		}

/*
 *		the u.size is the actual size allocated for data
 */
		bp->u.size = memSize - sizeof(bType);
		bp->flags = B_MALLOCED;

	} else if ((bp = bQhead[q]) != NULL) {
		//需要的内存在管理的范围之内且有可用块
/*
 *		Take first block off the relevant q if non-empty
 */
		bQhead[q] = bp->u.next;
#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD
		verifyFreeBlock(bp, q);
#endif
#if (defined (B_FILL) || defined (B_VERIFY_CAUSES_SEVERE_OVERHEAD))
		bFillBlock(bp, memSize);
#endif
		bp->u.size = memSize - sizeof(bType);
		bp->flags = 0;

	} else {
		//需要的块大小在管理的范围内但是当前没有可用的块,如果剩余的可分配内存足够则从剩余内存中划分
		if (bFreeLeft > memSize) {
/*
 *			The q was empty, and the free list has spare memory so 
 *			create a new block out of the primary free block
 */
			bp = (bType*) bFreeNext;
#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD
			verifyFreeBlock(bp, q);
#endif
			bFreeNext += memSize;
			bFreeLeft -= memSize;
#if (defined (B_FILL) || defined (B_VERIFY_CAUSES_SEVERE_OVERHEAD))
			bFillBlock(bp, memSize);
#endif
			bp->u.size = memSize - sizeof(bType);
			bp->flags = 0;

		} else if (bFlags & B_USE_MALLOC) {
			//如果剩余内存不够但允许动态分配则动态分配
#ifdef B_STATS
			static int once = 0;
			if (once++ == 0) {
				bstats(0, NULL);
			}
#endif
/*
 *			Nothing left on the primary free list, so malloc a new block
 */
#ifdef IRIX
			memSize = ROUNDUP4(memSize);
#endif
			if ((bp = (bType*) malloc(memSize)) == NULL) {
				traceRaw(T("B: malloc failed\n"));
				return NULL;
			}
#ifdef B_STATS
			bStatsMemMalloc += memSize;
#endif
#if (defined (B_FILL) || defined (B_VERIFY_CAUSES_SEVERE_OVERHEAD))
			bFillBlock(bp, memSize);
#endif
			bp->u.size = memSize - sizeof(bType);
			bp->flags = B_MALLOCED;

		} else {
			traceRaw(T("B: malloc failed\n"));
			return NULL;
		}
	}

#ifdef B_STATS
	bStatsAlloc(B_ARGS, bp, q, memSize);
#endif
	bp->flags |= B_INTEGRITY;

/*
 *	The following is a good place to put a breakpoint when trying to reduce
 *	determine and reduce maximum memory use.
 */
#if 0
#ifdef B_STATS
	if (bStatsBallocInUse == bStatsBallocMax) {
		bstats(0, NULL);
	}
#endif
#endif
	return (void*) ((char*) bp + sizeof(bType));
}



4.释放一个不再使用的内存块给管理模块

	a_assert((bp->flags & B_INTEGRITY_MASK) == B_INTEGRITY);

	if ((bp->flags & B_INTEGRITY_MASK) != B_INTEGRITY) {
		return;
	}

	memSize = ballocGetSize(bp->u.size, &q);

#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD
	verifyUsedBlock(bp,q);
#endif
#ifdef B_STATS
	bStatsFree(B_ARGS, bp, q, bp->u.size+sizeof(bType));
#endif
	if (bp->flags & B_MALLOCED) {
		free(bp);
		return;
	}
		
#ifdef B_VERIFY_CAUSES_SEVERE_OVERHEAD
	bFillBlock(bp, memSize);
#endif

/*
 *	Simply link onto the head of the relevant q
 */
	bp->u.next = bQhead[q];
	bQhead[q] = bp;

	bp->flags = B_FILL_WORD;
}

5.安全释放函数

这个没什么要说的,主要是检查了下指针是否为空,不为空则调用释放函数

void bfreeSafe(B_ARGS_DEC, void *mp)
{
	if (mp) {
		bfree(B_ARGS, mp);
	}
}

6.在UNICODE编码下复制一个字符串

char *bstrdupA(B_ARGS_DEC, char *s)
{
	char	*cp;
	int		len;

	if (s == NULL) {
		s = "";
	}
	len = strlen(s) + 1;
	//分配一块空间,拷贝字符串
	if (cp = balloc(B_ARGS, len)) {
		strcpy(cp, s);
	}
	return cp;
}

7.重新分配一块内存

重新分配一块较大的内存,并将原来内存中的数据拷贝到新内存中.

要注意的是模块是按块分配的,有可能已分配的内存也满足新请求的大小,此时直接返回原来的内存就可以

void *brealloc(B_ARGS_DEC, void *mp, int newsize)
{
	bType	*bp;
	void	*newbuf;

	//如果是空指针,直接分配返回
	if (mp == NULL) {
		return balloc(B_ARGS, newsize);
	}
	//找到管理数据所在的位置
	bp = (bType*) ((char*) mp - sizeof(bType));
	a_assert((bp->flags & B_INTEGRITY_MASK) == B_INTEGRITY);

/*
 *	If the allocated memory already has enough room just return the previously
 *	allocated address.
 */
	//如果当前块已经满足则返回
	if (bp->u.size >= newsize) {
		return mp;
	}
	//分配新内存,然后拷贝数据
	if ((newbuf = balloc(B_ARGS, newsize)) != NULL) {
		memcpy(newbuf, mp, bp->u.size);
		bfree(B_ARGS, mp);
	}
	return newbuf;
}

8.由块大小计算出其包含管理数据的大小,和该块在bQhead数组中的位置

static int ballocGetSize(int size, int *q)
{
	int	mask;

	//计算
	mask = (size == 0) ? 0 : (size-1) >> B_SHIFT;//B_SHIFT为4  宏定义
	for (*q = 0; mask; mask >>= 1) {
		*q = *q + 1;
	}
	return ((1 << (B_SHIFT + *q)) + sizeof(bType));
}

9.将内存填充为固定值,方便调试和检查

static void bFillBlock(void *buf, int bufsize)
{
	memset(buf, B_FILL_CHAR, bufsize);
}


还差一点内存使用统计和一些小函数
---------未完待续------


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
操作系统课程设计报告 题 目: 文件系统的模拟设计 一、设计目的 本课程设计是学习完"操作系统原理"课程后进行的一次全面的综合训练,通过课程 设计,更好地掌握操作系统的原理及实现方法,加深对操作系统基础理论和重要算法的 理解,加强学生的动手能力。 二、设计内容 (1)概述 为Linux系统设计一个简单的二级文件系统。要求做到以下几点: 1.可以实现下列几条命令: login 用户登录 dir 列目录 create 创建文件 delete 删除文件 open 打开文件 close 关闭文件 read 读文件 write 写文件 cd 进出目录 2.列目录时要列出文件名,物理地址,保护码和文件长度 3.源文件可以进行读写保护 (2)设计原理和系统总体功能框架图 1、在内存中开辟一个虚拟磁盘空间作为文件存储器,在其上实现一个多用户多目录 的文件系统。 2、文件物理结构可采用显式链接或其他方法。 3、磁盘空闲空间的管理可选择位示图或其他方法。如果采用位示图来管理文件存储 空间,并采用显式链接分配方式,则可以将位示图合并到FAT中。 4、文件目录结构采用多用户多级目录结构,每个目录项包含文件名、物理地址、长 度等信息,还可以通过目录项实现对文件的读和写的保护。 5、对文件或目录的操作采用windows系统的API函数来实现。 6、设计一个较实用的用户界面,方便用户使用。要求提供以下相关文件操作:用户 登录、创建目录、删除目录、创建文件、删除文件、读文件、写文件、复制文件、移动 文件、返回根目录、退出。 功能结构图如图1.0所示: (3)详细设计及少数重要编码的说明 这次课程设计中,我负责4个板,分别是显示列表函数和目录、创建函数文件创 建函数、打开文件函数和关闭文件系统函数。 1. 显示列表函数和目录 流程图如图1.1所示 2. 文件创建函数 算法:查找当前目录下是否有同名文件,是则退出,否则让用户输入文本文件内容 ,以'###'结束。申请inode空间(ialloc函数),申请硬盘block空间(balloc函数), 申请失败则结束。将文件内容写入inode空间和block空间。修改当前目录的结构,修改 inode位图,block位图,写入模拟硬盘。详细过程如流程图1.2所示: 、打开文件函数 对文件进行关闭操作,若此时文件不存在或处于读状态或写状态,则返回关闭失败信 息;若此时文件处于空闲状态,则调用Close函数进行关闭操作。流程图如图1.3所示 : 、关闭文件系统函数 对文件进行打开操作,若此时文件不存在或已处于打开状态或读状态或写状态,则返回 打开失败信息;若此时文件处于空闲状态,则调用Open函数进行打开操作。流程图如图 1.4所示: (4)运行结果分析 程序结果如图1.5-1.7所示 图1.5 图1.6 图1.7 (5)设计小结 操作系统课程设计是本课程重要的实践教学环节。课程设计的目的,一方面使学生 更透彻地理解操作系统的基本概念和原理,使之由抽象到具体;另一方面,通过课程设 计加强学生的实验手段与实践技能,培养学生独立分析问题、解决问题、应用知识的能 力和创新精神。与本课程的实验教学相比,课程设计独立设课,具有更多的学时,给学 生更多自行设计、自主实验的机会,充分放手让学生真正培养学生的实践动手能力,全 面提高学生的综合素质。 对于本次操作系统课程设,由于Linux比较陌生,刚起步阶段花了很大时间去查阅各 种资料。当完成设计时,感觉掌握了以前学到的知识,并且还对操作系统应用有了更深 入的认识。对二级文件系统也有了很好的了解,熟练C语言布局的使用,如何解决实现里 面功能的各种问题。 (6)参考文献 [1]计算机操作系统(第3版),汤小丹,西安电子科技大学出版社,2007年7月 [2]C语言程序设计,孟庆昌,人民邮电出版社,2006年4月 [3]计算机操作系统教程(第3版)习题解答与实验指导,张尧学,清华大学出版社, 2006年11月 (7)附源代码 12、显示列表函数dir( )和目录创建函数mkdir( )等(文件名dir.c) #include "stdio.h" #include "string.h" #include "filesys.h" _dir( ) /*dir*/ { unsigned int di_mode; int i,j,one; struct inode *temp_inode; printf("\nCURRENT DIRECTORY:\n"); for(i=0;i<DIRNUM;i++) {if(!(strcmp(dir.direct[i].d_name,""))&&(dir.direct[i].d_ino==0)) { dir.size = i;break; }} for(i=0;i<dir
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
由于Linux内核的ext4文件系统实现非常复杂,涉及的源代码也非常庞大,因此无法在此进行逐行注释。以下是一些重要的源代码文件和函数,以及它们的作用和功能: 1. fs/ext4/super.c:定义了ext4文件系统的超级结构体和相关函数,用于管理文件系统的元数据和数据结构。 2. fs/ext4/inode.c:定义了ext4文件系统的inode结构体和相关函数,用于管理文件和目录的元数据信息。 3. fs/ext4/dir.c:定义了ext4文件系统的目录结构体和相关函数,用于管理目录的元数据信息和目录项。 4. fs/ext4/namei.c:定义了ext4文件系统的名称解析函数,用于将路径名转换为inode结构体。 5. fs/ext4/file.c:定义了ext4文件系统的文件操作函数,用于读写文件和管理文件的元数据信息。 6. fs/ext4/extents.c:定义了ext4文件系统的扩展结构体和相关函数,用于管理大文件的数据。 7. fs/ext4/mballoc.c:定义了ext4文件系统的分配函数,用于管理文件系统的空闲位图。 8. fs/ext4/balloc.c:定义了ext4文件系统的数据分配函数,用于管理文件系统的数据位图。 9. fs/ext4/page-io.c:定义了ext4文件系统的页缓存管理函数,用于管理文件系统的数据缓存。 10. fs/ext4/xattr.c:定义了ext4文件系统的扩展属性结构体和相关函数,用于管理文件和目录的扩展属性。 这些源代码文件和函数涵盖了ext4文件系统的主要功能和特性,对于深入了解和掌握ext4文件系统的实现原理和操作方法非常有帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值