华北电力大学本科毕业设计(论文)
}
io_store_eflags(eflg);
if (flg486 != 0) { }
i = memtest_sub(start, end);//这个函数使用汇编写的,返回能用的内存的最大地址 if (flg486 != 0) { }
return i;
cr0 = load_cr0();
cr0 &= ~CR0_CACHE_DISABLE; /* 允许缓存 */ store_cr0(cr0); cr0 = load_cr0();
cr0 |= CR0_CACHE_DISABLE; /* 禁止缓存 */ store_cr0(cr0);
4.3.1.2 用于内存管理的数据结构设计
在操作系统的课程上,主要介绍了两种内存管理的方式,一种是位示图法,即用1位来表示一个单位的内存是否正在使用。例如,128M的内存,共有0x08000000个字节,以0x1000(4KB)为单位进行管理,需要创建0x08000000/0x1000 = 32768个单位进行管理。还有一种是空闲分区链管理法,即记录下每一块空闲区域的起始地址和大小,每当有内存分配时,则遍历所有空闲区域的记录,找到一块合适的分配给它,然后再更新内存的空闲区域记录;释放时也一样,更新相应的空闲区域记录,但是有一点要注意,内存释放的空间上面和下面的空间也可能是空闲的,这样就要与上面和下面空闲的空间合并,在空闲区域记录中归纳成一条,要不然系统运行到后面就没有大块的内存可用,全部都被分割成小块的空闲区域。
MyOS中采用的是第二种管理方法,其原因为:首先占用内存少,而且大块内存分配和以下是用于内存管理的数据结构设计。
#define MEMMAN_FREES
4090 /* 最大个数32KB */
#define MEMMAN_ADDR 0x003c0000 /* 约定的存放内存管理信息(结构体信息)的首地址*/
struct FREEINFO { };
struct MEMMAN {
/* 内存管理器 */
32
释放时都特别迅速,如果用位示图法,需要对大量的内存进行读写。
/* 一块空闲区域 */
unsigned int addr, size;
华北电力大学本科毕业设计(论文)
int frees; /* 空闲内存区域的数量 */
int maxfrees; /* 最大的空闲内存区域大小 */ };
struct FREEINFO free[MEMMAN_FREES]; /* 所有的内存区域记录 */
4.3.1.3 内存分配
MyOS的内存分配使用的是最大内存分配法,即从多个内存空闲区域中找到最大的区域,以下是用于内存分配的函数。
unsigned int memman_alloc(struct MEMMAN *man, unsigned int size) /* 分配内存空间 */ { }
unsigned int i, a;
for (i = 0; i < man->frees; i++) { }
return 0; /* 无可用空间 */
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) { }
return a;
/* 如果free[0]变成了0,就减少一条可用信息 */ man->frees--;
for (; i < man->frees; i++) { }
man->free[i] = man->free[i + 1]; /* 后面整体后移 */
分配给申请者所申请内存大小之后更新这个空闲内存区域的大小。
4.3.1.4 内存释放
内存释放时要注意的是,每次释放一块内存,必须检查这块内存上面和下面是否同样以下适用于内存释放的函数。
int memman_free(struct MEMMAN *man, unsigned int addr, unsigned int size)
33
是空闲的内存,如果是,则需要合并,更新相应的内存区域信息。
华北电力大学本科毕业设计(论文)
/* 释放内存空间 */ {
int i, j;
/* 为了方便整理释放后的内存空间,将free[]按照地址顺序排列 */ for (i = 0; i < man->frees; i++) { }
/* free[i - 1].addr < addr < free[i].addr */ if (i > 0) { }
/* 不能够与前面的空间结合在一起 */ if (i < man->frees) {
/* 后面也有 */
if (addr + size == man->free[i].addr) {
/* 可以与后面的内容归纳在一起整理 */
34
if (man->free[i].addr > addr) { }
break;
}
if (man->free[i - 1].addr + man->free[i - 1].size == addr) { /* 当前释放的内存前面也有空闲的内存 */ man->free[i - 1].size += size; if (i < man->frees) { }
return 0;
/* 后面也有 */
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]; /* 整体挪动一个位置 */
华北电力大学本科毕业设计(论文)
}
}
}
man->free[i].addr = addr; man->free[i].size += size; return 0;/* 成功 */
/* 前面后面都没有空余的空间,单独处理 */ if (man->frees < MEMMAN_FREES) { }
/* 不能往后移动 */ man->losts++;
man->lostsize += size; return -1; /* 失败 */
/* free[i]之后的整体完后挪动一个位置 */ for (j = man->frees; j > i; j--) { }
man->frees++;
if (man->maxfrees < man->frees) { }
man->free[i].addr = addr; man->free[i].size = size; return 0; /* 成功 */
man->maxfrees = man->frees; /* 更新最大值 */ man->free[j] = man->free[j - 1];
4.3.1.5 以4K为单位分配和释放内存
如果总是以1个字节为单位分配内存,会产生很多外部碎片,而内存控制器的空闲区域记录个数是有限的,所以为了防止空闲区域记录个数被耗尽,需要修改分配和释放函数,以4K为单位分配和释放内存。
以下是以4K为单位分配和释放内存的函数。
unsigned int memman_alloc_4k(struct MEMMAN *man, unsigned int size) /*以4K为单位分配内存*/ {
unsigned int a;
size = (size + 0xfff) & 0xfffff000; a = memman_alloc(man, size);
35
华北电力大学本科毕业设计(论文)
}
return a;
int memman_free_4k(struct MEMMAN *man, unsigned int addr, unsigned int size) /*以4K为单位释放内存*/ { }
int i;
size = (size + 0xfff) & 0xfffff000; i = memman_free(man, addr, size); return i;
4.3.1.6 其他与内存操作有关的函数
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;
4.3.2 定时器设计
定时器对于操作系统有非凡的意义,简单点来说,定时器的工作原理就是:隔一段时间就发送一个中断信号给CPU。而如果有了定时器,CPU就不用亲自去计算时间。
定时器的操作者是和处理器集成在一起的8254芯片,可以通过CPU对8254芯片进行编程(通俗点说就是用户写指令,让CPU去执行,执行内容是对8254芯片的各种寄存器进行写入操作,执行结果8254芯片按我们想要的工作模式来工作)。 4.3.2.1 设置定时器芯片
8254芯片的官方定义是PIT(“Programmable Interval Timer”,可编程的间隔型定时器)。可以通过设置这个PIT,让定时器每隔多少秒就产生一次中断,而产生的中断怎样传输到CPU呢?PIT是和中断控制器(后面的“基本输入输出管理”中会详细介绍)的IRQ的0号相连接,所以,只要设定了PIT就可以设定IRQ0的中断时间,这样,CPU只需要关注IRQ0是否产生中断就可以了
通过对8254芯片的资料可以知道,控制其中断周期变更的命令是:OUT(0x34,AL),其中AL = 0x34;OUT(0x40,AL),其中 AL = 中断周期的低8位 ; OUT(0x40,AL),其中AL = 中断周期的高8位。
36