📜  门| GATE 2017 MOCK II |第33章(1)

📅  最后修改于: 2023-12-03 15:12:35.016000             🧑  作者: Mango

门| GATE 2017 MOCK II |第33章

本文是关于 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 个整数设为 0num-1(包含 0num-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 的区块和剩余区块。显然,最佳适配法可以看作是最先适配法的优化,通过选择最小的空闲分区来避免剩余零散的空闲分区。

  • 外部碎片计算

在最佳适配法中,空闲分区链表中总是有一些不够用的空闲分区,这些空闲区较之最先适配法小的多,因此最佳适配法能更充分的利用内存,减少外部碎片的大小。外部碎片的计算方法同最先适配法类似,依据修改后的空闲分区数组,计算剩余的各个空闲分区大小的和即为外部碎片的大小。

Markdown 代码片段

根据上述思路,计算最先适配法和最佳适配法的外部碎片:

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

至此,我们得出了最先适配法和最佳适配法的结果。