第9天:内存管理

9.1、整理文件

把各个函数功能写到指定功能文件里,管理方便。
整理文件

9.2、内存容量检查

做内存管理,肯定要知道有多少内存。

方法:往内存中写入指定数据,立刻取出,检查是不是和写入的内容相同,如果内存正常的话,读出的内容就和写入的一样,说明内存有效。如果写入的内存不正常(地址不存在或其他原因),那么读出的内容将会是乱七八糟,那么就确定这个内存无效。

在486开始的CPU都带有高速缓存机制,写入读出都是在缓存中,无法测量内存,所以检查内存时,如果CPU是486以上就关闭高速缓存。

#define EFLAGS_AC_BIT		0x00040000
#define CR0_CACHE_DISABLE	0x60000000

unsigned int memtest(unsigned int start, unsigned int end)
{
	char flg486 = 0;
	unsigned int eflg, cr0, i;

	/* 确认CPU是不是486及以上 */
	eflg = io_load_eflags();
	eflg |= EFLAGS_AC_BIT; /* AC-bit = 1 */
	io_store_eflags(eflg);
	eflg = io_load_eflags();
	if ((eflg & EFLAGS_AC_BIT) != 0) { /* 如果是386及以下,即使AC设为1,AC的值还是自动会回到0 */
		flg486 = 1;
	}
	eflg &= ~EFLAGS_AC_BIT; /* AC-bit = 0 */
	io_store_eflags(eflg);

	if (flg486 != 0) {
		cr0 = load_cr0();
		cr0 |= CR0_CACHE_DISABLE; /* 禁止缓存 */
		store_cr0(cr0);
	}

	i = memtest_sub(start, end); /* 内存检查 */

	if (flg486 != 0) {
		cr0 = load_cr0();
		cr0 &= ~CR0_CACHE_DISABLE; /* 允许缓存 */
		store_cr0(cr0);
	}

	return i;
}

AC是EFLAGS的第18位,如果是386CPU,就没有这个标志位,一直是0。
关闭高速缓存的方法是将CR0的第二、三位置为1。

unsigned int memtest_sub(unsigned int start, unsigned int end)
{
	unsigned int i, *p, old, pat0 = 0xaa55aa55, pat1 = 0x55aa55aa;
	for (i = start; i <= end; i += 0x1000) { /* 一次增加4KB */
		p = (unsigned int *) (i + 0xffc); /*只检查最后四位 */
		old = *p;			/* 记住修改前的值 */
		*p = pat0;			/* 写入pat0 */
		*p ^= 0xffffffff;	/* 取反 */
		if (*p != pat1) {	/* 检查取反结果是否等于pat1;不等于就结束检查 */
not_memory:
			*p = old;
			break;
		}
		*p ^= 0xffffffff;	/* 如果等于pat1,再取反 */
		if (*p != pat0) {	/* 检查结果是否等于第一次写入的值;不等于就结束检查 */
			goto not_memory;
		}
		*p = old;			/* 恢复原来的值 */
	}
	return i;
}

HariMain

i = memtest(0x00400000, 0xbfffffff) / (1024 * 1024);
sprintf(s, "memory %dMB", i);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 32, COL8_FFFFFF, s);

检查0x00400000 - 0xbfffffff,范围3GB - 0x400000B。
前0x400000之前在asmhead.nas中已经使用过了。
内存检查1

检查结果一直都是3072MB。但是虚拟机并没有分配这多内存,这锅C编译器背,因为C编译器的优秀,它把很多认为“不必要的”程序给省略了,“提高”运行效率。
memtest_sub函数就被优化成:

unsigned int memtest_sub(unsigned int start, unsigned int end)
{
	unsigned int i;
	for (i = start; i <= end; i += 0x1000) {}
	return i;
}

完全没有检查到内存,就是做了一个循环。

所以还是采用汇编来写吧:

// naskfunc.nas
_memtest_sub:	; unsigned int memtest_sub(unsigned int start, unsigned int end)
		PUSH	EDI						; (由于还要使用EBX, ESI, EDI)
		PUSH	ESI
		PUSH	EBX
		MOV		ESI,0xaa55aa55			; pat0 = 0xaa55aa55;
		MOV		EDI,0x55aa55aa			; pat1 = 0x55aa55aa;
		MOV		EAX,[ESP+12+4]			; i = start;
mts_loop:
		MOV		EBX,EAX
		ADD		EBX,0xffc				; p = i + 0xffc;
		MOV		EDX,[EBX]				; old = *p;
		MOV		[EBX],ESI				; *p = pat0;
		XOR		DWORD [EBX],0xffffffff	; *p ^= 0xffffffff;
		CMP		EDI,[EBX]				; if (*p != pat1) goto fin;
		JNE		mts_fin
		XOR		DWORD [EBX],0xffffffff	; *p ^= 0xffffffff;
		CMP		ESI,[EBX]				; if (*p != pat0) goto fin;
		JNE		mts_fin
		MOV		[EBX],EDX				; *p = old;
		ADD		EAX,0x1000				; i += 0x1000;
		CMP		EAX,[ESP+12+8]			; if (i <= end) goto mts_loop;
		JBE		mts_loop
		POP		EBX
		POP		ESI
		POP		EDI
		RET
mts_fin:
		MOV		[EBX],EDX				; *p = old;
		POP		EBX
		POP		ESI
		POP		EDI
		RET

内存检查2这就对了。

9.3、内存管理

例:管理128MB内存

9.3.1、方案一:4KB单位管理

以4KB为一个单位进行管理。
128MB / 4KB = 32768
1、创建32768 Byte大小的数组,如果数组值为1,代表内存正在使用。0为未分配内存。

char a[32768];
for(i=0; i<1024; i++){//4MB正在使用
	a[i] = 1;
}
for(i=1024; i<32768; i++){//剩下未分配
	a[i] = 0;
}	

如果需要分配100KB内存空间,只需要找到25个连续的0:

j = 0;
allocate:
	for(i=0; i<25; i++){
		if(a[j+i] != 0){
			j++;
			if(j<32768-25){
				goto allocate;
			}else{
				// 没有空间的了,不做分配了
			}
		}
	}

// 如果找到100KB的连续空间,就把数组置为1
for(i=0; i<25; i++){
	a[j+i] = 1;
}
// addr = j * 0x1000 处就是分配内存的开始地址

如果内存用完要释放:

j = addr / 0x1000
for(i=0; i<25; i++){
	a[j+i] = 0;
}

以上是举例100KB,所以25都是写死的,实际25应该按分配大小进行计算。

9.3.2、 方案二:列表管理方法:

列表管理方法:
标记,从addr地址开始,有size大小的空间空闲。

struct FREEINFO {	/* 标记 */
	unsigned int addr, size;
};

struct MEMMAN {		/* 内存管理 */
	int frees;
	struct FREEINFO free[1000];
};

struct MEMMAN meman;
meman.frees = 1; // 1块空闲内存区间
meman.free[0].addr = 0x00400000; // 从0x00400000开始0x07c00000大小(124M)
meman.free[0].size = 0x07c00000;

如果分配100KB空间:

for(i = 0; i < meman.frees; i++){
	if(meman.free[i].size >= 100 *1024){
		// 有100KB空间可以分配
		"meman.free[i].addr 往后100KB可以使用"
		addr = meman.free[i].addr;
	}
}
"没有空间可以分配"

分配完成后,管理信息中需要删除这段空间:

for(i = 0; i < meman.frees; i++){
	if(meman.free[i].size >= 100 *1024){
		// 有100KB空间可以分配
		"meman.free[i].addr 往后100KB可以使用"
		addr = meman.free[i].addr;
		meman.free[i].addr += 100 * 1024; // 管理信息中删除这段内存
		meman.free[i].size -= 100 * 1024;
	}
}
"没有空间可以分配"

最后:
分配内存后,如果size = 0了,就要frees - 1,并且结构体数组前移。

释放内存后,如果不能和之前的内存合并,就要再加一条记录,同时frees + 1;如果能够合并,直接合并就好。

9.3.3、优化方案二

刚刚只设置了1000条记录,但是,如果内存太散,可能会耗光记录,那就增加一些管理记录:

struct FREEINFO { /* 可用信息 */
	unsigned int addr, size;
};
struct MEMMAN { /* 内存管理 */
	int frees, maxfrees, lostsize, losts;
	struct FREEINFO free[MEMMAN_FREES];
};
void memman_init(struct MEMMAN *man){
	man->frees = 0;    /* 可用信息数目 */
	man->maxfrees = 0; /* 用于观察可用状况:frees的最大值 */
	man->lostsize = 0; /* 释放失败的内存的大小总和 */
	man->losts = 0;    /* 释放失败次数 */
	return;
}

unsigned int memman_total(struct MEMMAN *man)
/* 报告空余内存大小的合计 */
{
	unsigned int i, t = 0;
	for (i = 0; i < man->frees; i++) {
		t += man->free[i].size;
	}
	return t;
}

unsigned int memman_alloc(struct MEMMAN *man, unsigned int size)
/* 分配 */
{
	unsigned int i, a;
	for (i = 0; i < man->frees; i++) {
		if (man->free[i].size >= size) {
			/* 找到了足够大的内存 */
			a = man->free[i].addr;
			man->free[i].addr += size;
			man->free[i].size -= size;
			if (man->free[i].size == 0) {
				/* 如果free[i]变成了0,就减掉一条可用信息 */
				man->frees--;
				for (; i < man->frees; i++) {
					man->free[i] = man->free[i + 1]; /* 代入结构体 */
				}
			}
			return a;
		}
	}
	return 0; /* 没有可用空间 */
}

释放内存:

int memman_free(struct MEMMAN *man, unsigned int addr, unsigned int size)
/* 释放 */
{
	int i, j;
	/* 为便于归纳内存,将free[]按照addr的顺序排列 */
	/* 所以,先决定应该放在哪里 */
	for (i = 0; i < man->frees; i++) {
		if (man->free[i].addr > addr) {
			break;
		}
	}
	/* free[i - 1].addr < addr < free[i].addr */
	if (i > 0) {
		/* 前面有可用内存 */
		if (man->free[i - 1].addr + man->free[i - 1].size == addr) {
			/* 可以与前面的可用内存归纳到一起 */
			man->free[i - 1].size += size;
			if (i < man->frees) {
				/* 后面也有 */
				if (addr + size == man->free[i].addr) {
					/* 也可以与后面的可用内存归纳到一起 */
					man->free[i - 1].size += man->free[i].size;
					/* man->free[i]删除 */
					/* free[i]变成0后归纳到前面去 */
					man->frees--;
					for (; i < man->frees; i++) {
						man->free[i] = man->free[i + 1]; /* 结构体赋值 */
					}
				}
			}
			return 0; /* 成功完成 */
		}
	}
	/* 不能与前面的可用空间归纳到一起 */
	if (i < man->frees) {
		/* 后面还有 */
		if (addr + size == man->free[i].addr) {
			/* 可以与后面的内容归纳到一起 */
			man->free[i].addr = addr;
			man->free[i].size += size;
			return 0; /* 成功完成 */
		}
	}
	/* 既不能与前面归纳到一起,也不能与后面归纳到一起 */
	if (man->frees < MEMMAN_FREES) {
		/* free[i]之后的,向后移动,腾出一点可用空间 */
		for (j = man->frees; j > i; j--) {
			man->free[j] = man->free[j - 1];
		}
		man->frees++;
		if (man->maxfrees < man->frees) {
			man->maxfrees = man->frees; /* 更新最大值 */
		}
		man->free[i].addr = addr;
		man->free[i].size = size;
		return 0; /* 成功完成 */
	}
	/* 不能往后移动 */
	man->losts++;
	man->lostsize += size;
	return -1; /* 失败 */
}

HariMain:


#define MEMMAN_ADDR 0x003c0000

unsigned int memtotal;
struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;

memtotal = memtest(0x00400000, 0xbfffffff);
memman_init(memman);
memman_free(memman, 0x00001000, 0x0009e000); /* 0x00001000 - 0x0009efff */
memman_free(memman, 0x00400000, memtotal - 0x00400000);

sprintf(s, "memory %dMB free : %dKB", memtotal / (1024 * 1024), memman_total(memman) / 1024);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 32, COL8_FFFFFF, s);

结果

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值