操作系统第5次实验报告:内存管理
零、个人信息
- 姓名:陈韵
- 学号:201821121053
- 班级:计算1812
一、记录内存空间使用情况
- 空闲区和已分配区均采用的是链表的结构,每个结点代表一块空间,含有起始地址以及空间大小。
空闲内存:
已分配内存:
- 通过指针读取链表的每个节点,获取他们的size和start_addr。来确定这个结点所指向的空间,以及这块空间的使用情况。
之后通过循环打印出每个空间的使用情况。
/* 显示空闲区 */
printf("空闲区:\n");
printf("%20s %20s\n", " 起始地址", " 大小");
while (fbt != NULL)
{
printf("%20d %20d\n", fbt->start_addr, fbt->size);
fbt = fbt->next;
}
/* 显示已分配区 */
printf("\n已分配区:\n");
printf("%10s %20s %10s %10s\n", "PID", "进程名", "起始地址", " 大小");
while (ab != NULL)
{
printf("%10d %20s %10d %10d\n", ab->pid, ab->process_name, ab->start_addr, ab->size);
ab = ab->next;
}
二、记录空闲分区
- 使用的是链表结构记录空闲分区。
- 初始化空闲分区块,我们需要先分配一块内存,默认大小为1024. 根据内存大小,进行空闲分区的初始化。
/*初始化空闲块,默认为一块,可以指定大小及起始地址*/
struct free_block_type *init_free_block(int mem_size)
{
struct free_block_type *fb, *p;
struct allocated_block *q;
fb = (struct free_block_type *)malloc(sizeof(struct free_block_type));//空闲区头结点
p = (struct free_block_type *)malloc(sizeof(struct free_block_type));//空闲区起始
q = (struct allocated_block *)malloc(sizeof(struct allocated_block));//分配区头结点
if (fb == NULL || p == NULL || q == NULL)
{
printf("没有分配内存\n");
return NULL;
}
q->next = NULL;
allocated_block_head = q;
p->size = mem_size;
p->start_addr = DEFAULT_MEM_START;
p->next = NULL;
fb->next = p; //头结点
return fb;
}
其中fb、q为空闲区链的指针,而q为已分配区链的指针。因为这是初始化,q获得了全部的内存空间。为了方便排序,fb作为q的前一个结点,即头结点。同样地,q为已分配区链的头结点。
三、内存分配算法
- 这里采用的是最佳分配算法,最佳分配算法(BF)的策略:
首先遍历整个空闲列表,找到和请求大小一样或更大的空闲块,然后返回这组候选者中最小的一块。
它的优点十分明显,只需要遍历一次空闲列表,就足以找到正确的块并返回。
- 利用BF算法重新排序:
1 /*按BF算法重新整理内存空闲块链表*/ 2 void rearrange_BF() 3 { 4 //m代表头,p。q分别代表交换的两个节点 n代表交换截至位置 5 struct free_block_type *p, *q, *m, *n; 6 7 if (free_block->next->next != NULL) 8 { 9 n = NULL; 10 while (n != free_block->next->next) 11 { 12 m = free_block; 13 p = free_block->next; 14 q = p->next; 15 while (q != n) 16 { //比较选出最小的一块 17 if (p->size > q->size) 18 { 19 p->next = q->next; 20 q->next = p; 21 m->next = q; 22 } 23 //结点后移进行下一次比较 24 m = m->next; 25 p = m->next; 26 q = p->next; 27 } 28 n = p; 29 } 30 } 31 }
- 内存分配实现:
1 int allocate_mem(struct allocated_block *ab) 2 { 3 struct free_block_type *p, *q, *temp; 4 int sum = 0; 5 if (free_block->next == NULL) 6 { 7 printf("no memory"); 8 return 0; 9 } 10 //BF算法进行整理内存 11 rearrange_BF(); 12 13 //从链首开始查找,如果找到,则开始分配,如果没有找到,采用分区紧缩技术,如果可以分配,则分配,不能分配,返回-1 14 if (ma_algorithm == MA_FF || ma_algorithm == MA_BF) 15 { 16 //开始寻找符合条件的节点 17 for (p = free_block, q = p->next; q && q->size < ab->size; p = p->next, q = p->next) 18 ; 19 20 //找到 21 if (q) 22 { 23 //判断是否达到最小碎片程度 24 25 if ((q->size - ab->size) <= MIN_SLICE) 26 { 27 //全部分配 28 ab->start_addr = q->start_addr; 29 ab->size = q->size; 30 //释放q节点 31 p->next = q->next; 32 free(q); 33 } 34 else 35 { 36 ab->start_addr = q->start_addr; 37 q->start_addr = q->start_addr + ab->size; 38 q->size = q->size - ab->size; 39 } 40 } 41 //开始内存紧缩 42 else 43 { 44 for (q = free_block->next; q; q = q->next) 45 { 46 sum += q->size; 47 if (sum >= ab->size) 48 { 49 break; 50 } 51 } 52 if (q) 53 { 54 //第一个空闲分区大小更改为sum,并且除了第一个,其他结点全部删除 55 free_block->next->size = sum; 56 p = free_block->next; 57 for (temp = p->next; temp != q; temp = p->next) 58 { 59 p->next = temp->next; 60 free(temp); 61 } 62 p->next = q->next; 63 free(q); 64 65 p->start_addr = p->start_addr + ab->size; 66 p->size = p->size - ab->size; 67 //如果剩余空间为0 则直接释放当前的空间 68 } 69 else 70 71 { 72 return -1; 73 } 74 } 75 return 1; 76 }
- 根据BF算法在空闲分区链表中搜索合适空闲分区进行分配:
1. 找到可满足空闲分区且分配后剩余空间足够大,则分割
2. 找到可满足空闲分区且但分配后剩余空间(最小碎片化)比较小,则一起分配
3. 找不到可满足需要的空闲分区但空闲分区之和能满足需要,则采用内存紧缩技术,进行空闲分区的合并,然后再分配
4. 在成功分配内存后,应保持空闲分区按照BF算法有序排列
5. 分配成功则返回1,否则返回-1
- 其他算法的小结与比较:
- 最差匹配WF:
- 策略:最差匹配则与最优匹配相反,它尝试找最大的空闲块,分割并满足需求后,将剩余的块(很大)加入空闲列表。
- 表现:最差匹配需要遍历整个空闲列表,它的表现非常差,导致过量的碎片,同时还具有很大的开销。
- 首次匹配FF:
- 策略:找到第一个足够大的块,将请求的空间返给用户。同样,剩余的空闲空间留给后续请求。
- 表现:首次匹配有速度优势,但是低地址部分不断被划分,留下许多难以利用、很小的空闲区,而每次查找又都从低地址部分开始,会增加查找的开销。
四、内存释放算法
- 思路:进程终止时,要做的两部分内容:更新分区表,并释放结点。
- 更新分区表:释放结点:调用函数找到该结点,释放即可。
- 将新释放的结点插入到空闲分区链的末尾。
- BF排列空闲链表
- 合并相邻的空闲分区
- 利用BF再次排序
- 源码:
1 //释放内存 2 int free_mem(struct allocated_block *ab) 3 { 4 struct free_block_type *fbt,*pre,*work; 5 fbt = (free_block_type *)malloc(sizeof(free_block_type)); 6 if(!fbt) return -1; 7 //分配空闲分区表的中的一个节点 每一次选择末尾入,并且完成之后查看当前算法,重新整理链表 8 fbt->size = ab->size; 9 fbt->start_addr = ab->start_addr; 10 11 //插至末尾 12 work = free_block; 13 if(work == NULL){ 14 free_block = fbt; 15 fbt->next == NULL; 16 }else{ 17 while(work ->next != NULL){ 18 work = work->next; 19 } 20 fbt->next = work->next; 21 work->next = fbt; 22 } 23 //BF排序 24 rearrange_BF(); 25 26 //合并分区 27 pre = free_block; 28 while(pre->next){ 29 work = pre->next; 30 if(pre->start_addr + pre->size == work->start_addr ){ 31 pre->size = pre->size + work->size; 32 pre->next = work->next; 33 free(work); 34 continue; 35 }else{ 36 pre = pre->next; 37 } 38 } 39 40 //BF排序 41 rearrange_BF(); 42 return 1; 43 } 44 45 //释放要杀死进程的节点 46 int dispose(struct allocated_block *ab) 47 { 48 struct allocated_block *p; 49 //找到该结点 50 for (p = allocated_block_head; p && p->next != ab; p = p->next) 51 ; 52 p->next = ab->next; 53 free(ab); 54 return 1; 55 } 56 57 //杀死进程 58 void kill_process() 59 { 60 struct allocated_block *ab; 61 int pid; 62 printf("释放内存, 输入pid="); 63 scanf("%d", &pid); 64 getchar(); 65 ab = find_process(pid); //找到pid对应的结点 66 if (ab != NULL) 67 { 68 free_mem(ab); //更新分区表 69 dispose(ab); //释放结点 70 } 71 }
五、运行结果
- 产生随机测试数据:利用随机数随机生成大小,进行进程的创建。
1 //创建一个新的进程 2 int new_process() 3 { 4 struct allocated_block *ab; 5 int size; 6 int ret; 7 ab = (struct allocated_block *)malloc(sizeof(struct allocated_block)); 8 if (!ab) 9 exit(-5); 10 ab->next = NULL; 11 pid++; 12 //进程取名 13 sprintf(ab->process_name, "进程-%02d", pid%3+1); 14 ab->pid = pid; 15 16 //随机分配空间 17 srand((int)time(0)); //产生随机种子 18 size=rand()%(mem_size/10); 19 if (size > 0) 20 ab->size = size; 21 printf("进程%s:已随机分配大小:%d", ab->process_name,ab->size); 22 23 ret = allocate_mem(ab); //从空闲区分配内存,ret==1表示分配ok 24 25 /*如果此时allocated_block_head尚未赋值,则赋值*/ 26 if ((ret == 1) && (allocated_block_head->next == NULL)) 27 { 28 allocated_block_head->next = ab; 29 return 1; 30 } 31 /*分配成功,将该已分配块的描述插入已分配链表*/ 32 else if (ret == 1) 33 { 34 ab->next = allocated_block_head->next; 35 allocated_block_head->next = ab; 36 return 2; 37 } 38 else if (ret == -1) 39 { 40 /*分配不成功*/ 41 printf("分配失败\n"); 42 free(ab); 43 return -1; 44 } 45 return 3; 46 }
- 运行结果:
- 选取4组结果分析:
第一次创建PID:1,从地址0处开始,分配了22个大小的空间。分配后空闲内存从地址22开始,大小为1002。
第二次创建PID:2,从地址22开始,分配了11个大小的空间。分配后空闲内存从地址33开始,大小为991。
第三次释放PID1,空闲内存获得了从0开始大小为22的空间。
第四次创建了PID:3,从地址33开始,分配了大小为80的空间。同样的空闲内存由原来33开始的991大小空间变成了113开始的911大小空间。