《30day自制操作系统》学习笔记09

(今天首先将鼠标文件拆分整理了下,然后对内存检查、内存分配和释放)

1. 内存容量检查

BIOS函数自带内存检查,但是BIOS版本不同,调用函数也不同。在内存检查之前,486以后的芯片由于使用了高速缓存,必须先将高速缓存禁用。
内存检查时,要往内存写入一个值,然后读取,来检查写入和读取的值是否相等。

;bootpack.c
#define EFLAGS_AC_BIT		0x00040000
#define CR0_CACHE_DISABLE	0x60000000

	/*确认CPU类型,并禁用高速缓存*/
unsigned int memtest(unsigned int start, unsigned int end)
{
	char flg486 = 0;
	unsigned int eflg, cr0, i;

	/* 确认CPU类型,EFLAGS是否有AC标志位 */
	eflg = io_load_eflags();          /*读取EFLAGS */
	eflg |= EFLAGS_AC_BIT;         /* 将AC-bit = 1 */   
	io_store_eflags(eflg);            /*存入EFLAGS */
	eflg = io_load_eflags();         /*读取EFLAGS */
	if ((eflg & EFLAGS_AC_BIT) != 0) {         /* AC标志位为1,就是486 */
		flg486 = 1;
	}
	eflg &= ~EFLAGS_AC_BIT;         /* AC-bit = 0 */
	io_store_eflags(eflg);                   /*存入EFLAGS */

	if (flg486 != 0) {                           /*是486,就*/
		cr0 = load_cr0();                      /*读取cr0 */
		cr0 |= CR0_CACHE_DISABLE;            /* cr0的禁用缓存标志位置为0,禁用缓存 */
		store_cr0(cr0);
	}

	i = memtest_sub(start, end);                    /*检查从start~end的最大尾部可用空间,并返回地址*/

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

	return i;
}

第一版memtest_sub

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) {   /*检查4字节太多了,间隔0x1000字节*/
		p = (unsigned int *) (i + 0xffc);          /*跳到0x1000最后四个字节的位置*/
		old = *p;			/* 保存初始值 */
		*p = pat0;			/* 给p赋值 */
		*p ^= 0xffffffff;	   /* 取反 */
		if (*p != pat1) {	      /* 检查反转结果 */
not_memory:
			*p = old;
			break;
		}
		*p ^= 0xffffffff;	    /* 再次反转 */
		if (*p != pat0) {     	/* 检查反转结果 */
			goto not_memory;
		}
		*p = old;			/*取得初始值 */
	}
	return i;             /*返回最大地址 */
}
i = memtest(0x00400000, 0xbfffffff) / (1024 * 1024);            /*检查0x00400000~0xffffffff范围的内存,并返回最大内存地址 */
sprintf(s, "memory %dMB", i);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 32, COL8_FFFFFF, s);            /*显示字符 */

根据QUME的设定,内存应该有32MB,以上程序结果显示的是3GB(即最后一个检查地址)。
通过make -r bootpack.nas 可以确认汇编语言版本,发现C编译器最后结果相当于只是执行了一个循环,没有执行赋值、取反等操作。(具体过程见书)

2. 内存管理

内存管理的基础是:一、内存分配;二、内存释放。

方法一:分段,记录每个段的使用状态

先假设有128MB(0x08000000)内存,假设以0x1000个字节(4KB)为单位进行管理,需要0x08000=32768个字节区域。

char a[32768];
for(i=0;i<1024;i++){
	a[i]=1;
}
for(i=1024;i<32768;i++){
	a[i]=0;
}

如果需要100KB空间,只需要从a中找出连续25个标记为0的地方就可以了。

/*查找25个空字节*/
j=0;
再来一次:
for(i=0;i<25;i++){
	if(a[j+i]!=0){
		j++;
		if(j<32768-25)goto 再来一次;
		没有内存可用;
		
/*将25个字节置为1*/
for(i;i<25;i++){
	a[j+i]=1;
}

释放内存:(假设从0x123000开始的0x1000字节)

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

这种方法存在缺点就是占用内存空间太大(每一个内存分段都需要1字节或1bit记录结果)。

方法二:只记录连续可用内存的首地址、大小

定义内存管理用的结构体:

#define MEMMAN_FREES		4090	/* 32KB,4090个内存 */
#define MEMMAN_ADDR			0x003c0000

struct FREEINFO {	/* 可用空间的地址、大小*/
	unsigned int addr, size;
};

struct MEMMAN {		/* 内存管理 */
	int frees, maxfrees, lostsize, losts;
	struct FREEINFO free[MEMMAN_FREES];
};

unsigned int memtest(unsigned int start, unsigned int end);
void memman_init(struct MEMMAN *man);
unsigned int memman_total(struct MEMMAN *man);
unsigned int memman_alloc(struct MEMMAN *man, unsigned int size);
int memman_free(struct MEMMAN *man, unsigned int addr, unsigned int size);

检查内存:

#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;

	/* 386偐丄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偵偟偰傕帺摦偱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;
}

内存空间管理结构体初始化:

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;
	/* ? */
	/* ? */
	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; /* 失败 */
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值