希冀 操作系统 实验四 段式存储管理

目录

一.补全代码及分析

二.思考题

三.总结


一.补全代码及分析

申请进程apply()函数完成了新开进程的功能,同时还记录了该进程需要的内存空间段数和每段的具体大小,你需要补全该函数。

补全的代码为:

void apply() {
    printf("请输入进程的名字:");
    scanf("%s", duanbiaos[duanbiaonum].processname);
    printf("请输入该进程的段数:");
    scanf("%d", &duanbiaos[duanbiaonum].num);
    if (duanbiaos[duanbiaonum].num > 10) {
        printf("段数太多,申请失败\n");
        return;
    }
    int paddr = 0;
    for (int j = 0; j < duanbiaos[duanbiaonum].num; j++) {
        printf("进程第%d段的大小是:", j + 1);
        scanf("%ld", &duanbiaos[duanbiaonum].duans[j].capacity);
        duanbiaos[duanbiaonum].duans[j].addr = paddr;
        paddr += duanbiaos[duanbiaonum].duans[j].capacity;
    }
    duanbiaos[duanbiaonum].Isdiaoyong = 0;
    duanbiaos[duanbiaonum].total = paddr;
    duanbiaonum++;
}

代码结构分析

  1. 输入进程基本信息阶段
    • 通过scanf函数获取用户输入的进程名字(%s格式)和进程的段数(%d格式)。这里使用duanbiaos数组存储进程相关信息,duanbiaonum可能是用于记录当前进程的索引或者数量。
  2. 段大小输入与地址计算阶段
    • for循环(for (int j = 0; j < duanbiaos[duanbiaonum].num; j++))中:
      • 对于每个段,获取用户输入的段大小(%ld格式)。
      • 计算该段的起始地址(duanbiaos[duanbiaonum].duans[j].addr = paddr;),并通过累加段大小来更新下一段的起始地址(paddr += duanbiaos[duanbiaonum].duans[j].capacity;)。
  3. 进程状态标记与计数更新阶段
    • 将进程标记为未调用状态(duanbiaos[duanbiaonum].Isdiaoyong = 0;)。
    • 计算并存储进程的总大小(duanbiaos[duanbiaonum].total = paddr;)。
    • 增加进程数量的计数(duanbiaonum++;

函数diaodu()的主要功能是将某进程装入内存,在装入之前需要判断剩余可用空间是否能够满足内存需求,如果满足则将其装入内存,否则调入失败。在装入内存的过程中需要注意记录虚拟地址和物理地址的对应关系。

补全的diaodu()的代码为:

void diaodu() {
    printf("您要调度的进程是:p");
    int n;
    scanf("%d", &n);
    if (n > duanbiaonum)
        printf("段表不存在!\n");
    else if (duanbiaos[n - 1].Isdiaoyong == 1)
        printf("操作错误,该进程已经被调入内存!\n");
    else if (duanbiaos[n - 1].total > neicun - zhanyong)
        printf("内存空间不足,调度不成功!\n");
    else {
        printf("下面对进程%s进行地址转换:\n", duanbiaos[n - 1].processname);
        for (int j = 0; j < duanbiaos[n - 1].num; j++) {
            printf("正在进行第%d段的地址转换,请稍候......\n", j + 1);
            duanbiaos[n - 1].duans[j].realaddr = duanbiaos[n - 1].duans[j].addr + zhanyong;
            printf("第%d段的地址转换成功!\n", j + 1);
        }
        zhanyong += duanbiaos[n - 1].total;
        duanbiaos[n - 1].Isdiaoyong = 1;
        printf("调度后的结果是:\n");
        show1(n - 1);
    }
}

代码结构分析

  1. 进程选择与基本检查阶段
    • 通过scanf获取用户要调度的进程编号n
    • 进行三个条件判断:
      • 如果n > duanbiaonum,即进程编号超出已存在的进程数量,提示“段表不存在!”。
      • 如果duanbiaos[n - 1].Isdiaoyong == 1,表示进程已被调入内存,提示“操作错误,该进程已经被调入内存!”。
      • 如果duanbiaos[n - 1].total > neicun - zhanyong,即进程所需内存大于剩余内存,提示“内存空间不足,调度不成功!”。
  2. 地址转换与状态更新阶段
    • 若通过上述检查,对于进程的每个段(for (int j = 0; j < duanbiaos[n - 1].num; j++)):
      • 进行地址转换,计算实际地址(duanbiaos[n - 1].duans[j].realaddr = duanbiaos[n - 1].duans[j].addr + zhanyong;)并提示转换成功。
    • 更新内存占用量(zhanyong += duanbiaos[n - 1].total;)。
    • 将进程标记为已被调入内存(duanbiaos[n - 1].Isdiaoyong = 1;)。
  3. 结果展示阶段
    • 调用show1函数展示调度后的结果(show1(n - 1);)。

 

函数zhuanhuan()实则使用记录的虚拟地址与物理地址的关系,将你需要表示的某段段内偏移这一逻辑地址所对应的物理地址找到。现在也请你将缺失部分补全。

补全的 zhuanhuan():

void zhuanhuan() {
    int n, i, j;
    printf("请输入进程:p");
    scanf("%d", &n);
    if (n > duanbiaonum)
        printf("进程不存在或没被调度!\n");
    else if (duanbiaos[n - 1].Isdiaoyong == 1) {
        printf("请输入段号:\n");
        scanf("%d", &i);
        printf("输入偏移地址:\n");
        scanf("%d", &j);
        if (i > duanbiaos[n - 1].num) {
            printf("段号超出范围\n");
        } else if (j > duanbiaos[n - 1].duans[i - 1].capacity) {
            printf("段内偏移地址超出范围\n");
        } else {
            printf("转换后的地址为%d\n", duanbiaos[n - 1].duans[i - 1].realaddr + j);
        }
    } else
        printf("该进程没有调入内存\n");
}

代码结构分析

  1. 进程选择与基本检查阶段
    • 通过scanf获取用户输入的进程编号n
    • 进行两个条件判断:
      • 如果n > duanbiaonum,表示进程编号超出已存在的进程数量,提示“进程不存在或没被调度!”。
      • 如果duanbiaos[n - 1].Isdiaoyong == 1,表示进程已被调度,进入段号和偏移地址处理阶段;否则提示“该进程没有调入内存”。
  2. 段号与偏移地址处理阶段
    • 在进程已被调度的情况下:
      • 通过scanf获取用户输入的段号i和偏移地址j
      • 进行两个条件判断:
        • 如果i > duanbiaos[n - 1].num,即段号超出进程的段数范围,提示“段号超出范围”。
        • 如果j > duanbiaos[n - 1].duans[i - 1].capacity,即偏移地址超出段的容量范围,提示“段内偏移地址超出范围”。
      • 若段号和偏移地址均合法,则计算并输出转换后的地址(duanbiaos[n - 1].duans[i - 1].realaddr + j)。

 

二.思考题

  现在我们没有考虑淘汰旧的占用段,如果将其考虑进去,使用最简单的先进先出算法淘汰段该如何修改程序?

答:

1. 添加一个淘汰函数 void replace(int new_process_index)

该函数负责在内存不足时逐出旧的段。此函数会查找当前内存中最早进入的进程并将其淘汰。我们可以通过以下步骤实现它:

  1. 遍历 duanbiao 数组,找到第一个已调入内存(Isdiaoyong == 1)的进程。

  2. 将该进程的 Isdiaoyong 设置为 0,释放其占用的内存。

  3. 更新 zhanyong(表示已用内存)以释放该进程占用的空间。

  4. 继续判断是否需要进一步淘汰更多进程,直到满足新进程的内存需求。

2. 修改 diaodu() 函数

在 diaodu() 函数中,我们在内存不足的情况下调用 replace() 函数尝试腾出空间,再继续执行调度过程。

3. 实现代码

以下是修改后的代码片段

void replace(int new_process_index) {
    // 淘汰进程的段,使用 FIFO 算法逐出内存中最早调入的进程
    int replaced = 0;  // 标记是否已经腾出足够空间
    while (!replaced) {
        for (int i = 0; i < duanbiaonum; i++) {
            if (duanbiaos[i].Isdiaoyong == 1) {  // 找到第一个已调入的进程
                printf("内存不足,淘汰进程 %s\n", duanbiaos[i].processname);
                duanbiaos[i].Isdiaoyong = 0;   // 标记该进程已被淘汰
                zhanyong -= duanbiaos[i].total;  // 更新已用内存
                replaced = (zhanyong + duanbiaos[new_process_index].total <= neicun);  // 判断是否已腾出足够空间
                if (replaced) break;  // 如果腾出足够空间则退出循环
            }
        }
        // 如果没有足够的进程可淘汰,无法满足调度请求
        if (!replaced) {
            printf("无法调入新进程 %s,内存空间仍不足\n", duanbiaos[new_process_index].processname);
            return;
        }
    }
}

void diaodu() {
    printf("您要调度的进程是:p");
    int n;
    scanf("%d", &n);
    if (n > duanbiaonum)
        printf("段表不存在!\n");
    else if (duanbiaos[n - 1].Isdiaoyong == 1)
        printf("操作错误,该进程已经被调入内存!\n");
    else {
        // 检查内存是否足够,否则调用 replace() 函数腾出空间
        if (duanbiaos[n - 1].total > neicun - zhanyong) {
            replace(n - 1);
            // 再次检查,若仍不满足则返回
            if (duanbiaos[n - 1].total > neicun - zhanyong) {
                printf("内存空间不足,调度不成功!\n");
                return;
            }
        }
        // 执行进程调度,将进程调入内存
        printf("下面对进程%s进行地址转换:\n", duanbiaos[n - 1].processname);
        for (int j = 0; j < duanbiaos[n - 1].num; j++) {
            printf("正在进行第%d段的地址转换,请稍候......\n", j + 1);
            duanbiaos[n - 1].duans[j].realaddr = duanbiaos[n - 1].duans[j].addr + zhanyong;
            printf("第%d段的地址转换成功!\n", j + 1);
        }
        zhanyong += duanbiaos[n - 1].total;
        duanbiaos[n - 1].Isdiaoyong = 1;
        printf("调度后的结果是:\n");
        show1(n - 1);
    }
}

三.总结

一、整体功能总结

  1. 关联关系
    • 这三个函数(applydiaoduzhuanhuan)共同构建了一个与进程管理相关的系统功能。apply函数用于进程资源的申请,包括获取进程名称、段数以及各段大小等信息;diaodu函数负责进程的调度操作,包含进程存在性、状态以及内存空间的检查,还有地址转换等操作;zhuanhuan函数则聚焦于已调度进程内的地址转换操作。
  2. 目标一致性
    • 它们的目标都是围绕进程的资源管理、调度和地址转换等操作,从不同方面对进程相关的操作进行管理,以确保进程在系统中的正常运行。

二、代码结构共性总结

  1. 输入获取与验证
    • 三个函数都大量使用scanf函数来获取用户输入,如获取进程编号、段数、段号、偏移地址等。然而,它们在输入验证方面都存在不足,对于输入的合法性验证不够全面,容易因用户输入非预期内容(如非数字字符)而导致程序出错。
  2. 条件判断逻辑
    • 都采用了条件判断语句来处理不同的情况。例如,在apply函数中判断段数是否过多;在diaodu函数中判断进程是否存在、是否已被调入内存、内存空间是否足够等;在zhuanhuan函数中判断进程是否存在、是否已被调度、段号和偏移地址是否超出范围等。但在错误提示方面,都有提升空间,提示信息相对简单,缺乏更详细的解决建议。
  3. 数据处理与状态更新
    • 都涉及到对进程相关数据的处理。如apply函数中计算进程总大小并标记进程状态;diaodu函数中进行地址转换、更新内存占用量和进程状态;zhuanhuan函数中根据段号和偏移地址计算转换后的地址。同时,它们在数据处理过程中都依赖于一些全局变量,这在一定程度上影响了代码的可维护性。

你可能感兴趣的:(希冀操作系统实验,希冀,操作系统,实验)