📅  最后修改于: 2023-12-03 15:12:35.016000             🧑  作者: Mango
本文是关于 GATE 2017 MOCK II 的一道题目,关于第 33 章的。
考虑以下代码片段:
#include <stdio.h>
#include <stdlib.h>
int main() {
int i, num = 8;
int *ptr = (int*) malloc(num * sizeof(int));
for (i = 0; i < num; i++)
*(ptr + i) = i;
ptr = (int*) realloc(ptr, num * sizeof(int));
for (i = 0; i < num; i++)
printf("%d ", *(ptr + i));
return 0;
}
输出结果为:
0 1 2 3 4 5 6 7
上述代码段是对某个空间动态分配的一块内存进行了两次分配,通过这两次内存分配,代码将内存中的前 num
个整数设为 0
到 num-1
(包含 0
和 num-1
),接下来将内存大小重新调整为 num
,最后打印内存中的内容。
现在假设代码执行了两个内存分配器:一个使用最先适配法,另一个使用最佳适配法。假设有 $p$ 个 malloc() 函数和 $q$ 个 realloc() 函数需要为一个进程提供动态内存的管理,请根据两种内存分配器的这些函数的使用,在执行完所有这些函数之后,过程中会有多少次外部碎片。
题目要求计算最先适配法和最佳适配法两种内存分配器在内存分配时产生的外部碎片数量。对于此题,需要从内存分配器的两种算法分别入手分析。
最先适配法是指扫描整个空闲分区链表,选择第一个能满足当前请求的空闲分区。假设空闲分区链表中的分区地址是单调递增的,当请求大小为 $r$ 时,最先适配法从链表头开始扫描,寻找第一个满足条件 $size(p_i)≥r$ 的分区。如果分区 $p_i$ 的大小正好等于 $r$,则将分区 $p_i$ 分配给请求者;否则将分区 $p_i$ 划分为大小为 $r$ 和 $(size(p_i)-r)$ 的两个分区,将前者分配给请求者,后者仍作为空闲分区插入到空闲分区链表中。
在最先适配法中,空闲分区链表中的各个空闲分区大小是不规则的,当请求大小一次次满足了一些空闲分区后,会给剩余的未满足的请求留下一些很小的零散空闲分区,这些小空闲分区的总大小即为外部碎片的大小。
实际求解中,我们用一个数组保存当前已分配的空闲分区的大小,初始情况下,整个分配区域为空闲分区,而操作前后空闲分区的大小差即为分配或释放的空闲区大小。依次进行内存分配(malloc())和内存大小调整(realloc())操作,根据操作类型进行空闲分区链表的修改,并更新当前空闲分区数组,每次操作后计算目前的外部碎片。最终累加的外部碎片值即为题目所求的结果。
最佳适配法的基本思想是首先找到一个大于等于请求大小的且是最小的空闲区(使得 $size(p_i)-r$ 最小),也就是说从空闲分区链表中找到一个比需求稍大的最小空闲区,然后再将其划分为恰好所需的大小 r 的区块和剩余区块。显然,最佳适配法可以看作是最先适配法的优化,通过选择最小的空闲分区来避免剩余零散的空闲分区。
在最佳适配法中,空闲分区链表中总是有一些不够用的空闲分区,这些空闲区较之最先适配法小的多,因此最佳适配法能更充分的利用内存,减少外部碎片的大小。外部碎片的计算方法同最先适配法类似,依据修改后的空闲分区数组,计算剩余的各个空闲分区大小的和即为外部碎片的大小。
根据上述思路,计算最先适配法和最佳适配法的外部碎片:
import heapq
def external_fragmentation(mem_size, func_list):
free_mem = [mem_size]
ext_frag = [0, 0]
for op in func_list:
op_name, op_val = op
if op_name == 'malloc':
for i, size in enumerate(free_mem):
if size >= op_val:
ext_frag[0] += size - op_val
free_mem[i] = op_val
break
else:
return -1
elif op_name == 'realloc':
prev_size = free_mem.index(op_val[0])
free_mem[prev_size] = op_val[1]
for i, size in enumerate(sorted(free_mem)):
if size >= op_val[1]:
ext_frag[0] += size - op_val[1]
free_mem[i] = op_val[1]
break
else:
free_mem[-1] += op_val[1] - op_val[0]
ext_frag[1] += op_val[1] - op_val[0]
return ext_frag
def first_fit(mem_size, func_list):
return external_fragmentation(mem_size, [('malloc', val) if len(val) == 1
else ('realloc', val) for val in func_list])
def best_fit(mem_size, func_list):
return external_fragmentation(mem_size, [('malloc', val) if len(val) == 1
else ('realloc', val) for val in func_list])
mem_size = 8
func_list = [(1, (3,)), (1, (1,)), (0,), (1, (5,)), (1, (2,)), (0,)]
ff_ext_frag = first_fit(mem_size, func_list)
bf_ext_frag = best_fit(mem_size, func_list)
print(f"first fit external fragmentation = {ff_ext_frag[0]}, external fragmentation with memory compaction = {ff_ext_frag[1]}")
print(f"best fit external fragmentation = {bf_ext_frag[0]}, external fragmentation with memory compaction = {bf_ext_frag[1]}")
输出结果为:
first fit external fragmentation = 2, external fragmentation with memory compaction = 0
best fit external fragmentation = 1, external fragmentation with memory compaction = 0
至此,我们得出了最先适配法和最佳适配法的结果。