【快速解决】实验一:模拟实现进程的创建《操作系统上机》实验报告

目录

实验要求

正文开始 

​编辑 难点讲解

 结语 


实验要求

实验一:进程的创建

一、实验项目类型:设计型

二、实验目的和要求

加深对进程概念的理解,熟悉PCB的组织,深入了解创建进程的一般过程掌握用队列组织进程的方法。

三、实验内容

编程实现创建原语,形成就绪队列,模拟实现进程的创建过程。

具体内容包括:

1、确定进程控制块的内容,用链表组织进程控制块;

2、完成进程创建原语;

四、实验主要仪器设备

个人计算机、C程序编译器

五、实验具体内容和步骤的说明

这个实验主要考虑二个问题:如何组织进程、如何创建进程。

1、进程的组织:

首先就要设定进程控制块的内容。进程控制块PCB记录各个进程执行时的所有信息,不同的操作系统,进程控制块所记录的信息内容不一样。操作系统功能越强,软件也越庞大,进程控制块所记录的内容也就越多。本次实验只使用必不可少的信息。一般操作系统中,无论进程控制块中信息量多少,信息都可以大致分为以下四类: 

① 标识信息

每个进程都要有一个惟一的标识符,用来标识进程的存在和区别于其他进程。这个标识符是必不可少的,可以用符号或编号实现,它必须是操作系统分配的。本实验中要求,采用编号方式,也就是为每个进程依次分配一个不相同的正整数。

② 说明信息

用于记录进程的基本情况,例如进程的状态、等待原因、进程程序存放位置、进程数据存放位置等等。本模拟实验中,因为进程没有数据和程序,仅使用进程控制块模拟进程,所以这部分内容仅包括进程状态

③ 现场信息

现场信息记录各个寄存器的内容。当进程由于某种原因让出处理器时,需要将现场信息记录在进程控制块中,当进行进程调度时,从选中进程的进程控制块中读取现场信息进行现场恢复。现场信息就是处理器的相关寄存器内容,包括通用寄存器、程序计数器和程序状态字寄存器等。在实验中,可选取几个寄存器作为代表。用大写的全局变量AX、BX、CX、DX模拟通用寄存器、大写的全局变量PC模拟程序计数器、大写的全局变量PSW模拟程序状态字寄存器。本实验要求读取一个寄存器的值,予以输出

④ 管理信息

管理信息记录进程管理和调度的信息。例如进程优先数、进程队列指针等。实验中,仅包括队列指针。

综合上面内容,建议进程控制块结构定义如下:

struct  pcb  

{int  name;  //进程标识符

int  status;  //进程状态

int  ax,  bx,  cx,  dx;  //进程现场信息,通用寄存器内容

int  pc;                //进程现场信息,程序计数器内容

int  psw;              //进程现场信息,程序状态字寄存器内容

int  next;              //下一个进程控制块的位置

}

    

进程控制块定义好后,考虑如何组织进程控制块。多道程序设计系统中,往往同时创建多个进程。在单处理器的情况下,每次只能有一个进程处于运行态,其他的进程处于就绪状态或阻塞状态。为了便于管理,通常把处于相同状态的进程的进程控制块链接在一起。

单处理器系统中,正在运行的进程只有一个。因此,单处理器系统中的进程控制块分成三个队列

①、一个正在运行进程的进程控制块;

②、就绪进程的进程控制块组成的就绪队列;

③、阻塞进程的进程控制块组成的阻塞队列。

由于实验模拟的是进程调度,没有对阻塞队列的操作,所以实验中只有一个指向正在运行进程的进程控制块指针一个就绪进程的进程控制块队列指针。操作系统的实现中,系统往往在主存中划分出一个连续的专门区域存放系统的进程控制块,实验中应该用数组模拟这个专门的进程控制块区域,定义如下:

#define   n   10                //假定系统允许进程个数为10

struct   pcb   pcbarea[n];        //模拟进程控制块区域的数组

这样,进程控制块的链表实际上是数据结构中使用的静态链表。进程控制块的链接方式可以采用单向和双向链表,实验中,进程控制块队列采用单向静态链表。为了管理空闲进程控制块,还应该将空闲控制块链接成一个队列。

实验中将采用时间片轮转调度算法,这种算法是将进程控制块按照进入就绪队列的先后次序排成队列。对就绪队列的操作就是从队头摘下一个进程控制块和从队尾挂入一个进程控制块。因此为就绪队列定义两个指针,一个头指针,指向就绪队列的第一个进程控制块;一个尾指针,指向就绪队列的最后一个进程控制块。

实验中指向运行进程的进程控制块指针、就绪队列指针和空闲进程控制块队列指针定义如下:

int  run;   //定义指向正在运行进程的进程控制块的指针

struct

{int   head;

int   tail;

}ready;   //定义指向就绪队列的头指针head和尾指针tail

int   pfree;   //定义指向空闲进程控制块队列的指针

2、进程创建。

进程创建是一个原语,因此在实验中应该用一个函数实现,进程创建的过程应该包括:

①申请进程控制块:进程控制块的数量是有限的,如果没有空闲进程控制块,则进程不能创建,如果申请成功才可以执行第②步;

②申请资源:除了进程控制块外,还需要有必要的资源才能创建进程,如果申请资源不成功,则不能创建进程,并且归还已申请的进程控制块;如果申请成功,则执行第三步,实验无法申请资源,所以模拟程序忽略了申请资源这一步;

③填写进程控制块:将该进程信息写入进程控制块内,实验中只有进程标识符、进程状态可以填写,每个进程现场信息中的寄存器内容由于没有具体数据而使用进程(模拟进程创建时,需输入进程标识符字,进程标识符本应系统建立,并且是惟一的,输入时注意不要冲突),刚刚创建的进程应该为就绪态,然后转去执行第四步;

④挂入就绪队列:如果原来就绪队列不为空,则将该进程控制块挂入就绪队列尾部,并修改就绪队列尾部指针;如果原来就绪队列为空,则将就绪队列的头指针、尾指针均指向该进程控制块,进程创建完成。

3、主程序:

完成上述功能后,编写主函数进行测试:首先建立一个就绪队列,手工输入信息建立几个进程;然后输出每个进程的进程id,察看结果。

注意:

这篇文章中小光会带你们将老师布置的实验内容这部分,详细的讲解一下,大家可以直接复制代码,也可以学习一下怎么写,在这个基础上加以拓展,这样就不会查重了。 

正文开始 

让我们先来看一下实验要求中的代码(代码如下

#include <stdio.h>
#include <stdlib.h>
#define READY 1
#define MAX_PCB 10 

struct PCB {
  int pid;  
  int state;
  
  int ax;
  int bx;
  int cx;
  int dx;

  int pc;
  int psw;
   
  struct PCB* next;
};

struct PCB pcbPool[MAX_PCB];
int allocPCBCount = 0;  

struct PCB* readyQueueHead;
struct PCB* readyQueueTail;

void createProcess() {

  struct PCB* pcb;
  if(allocPCBCount >= MAX_PCB) {
    printf("No free PCB\n");
    return;
  } else {
    pcb = &pcbPool[allocPCBCount++]; 
    allocPCBCount++;
  }

  printf("Enter pid: ");
  scanf("%d", &pcb->pid);

  pcb->state = READY; 
  
  // 初始化寄存器值
  pcb->ax = 0; //将该PCB的ax寄存器值设置为0
  pcb->bx = 0;
  pcb->cx = 0;
  pcb->dx = 0;
  pcb->pc = 0;
  pcb->psw = 0;

  if(readyQueueHead == NULL) {
    readyQueueHead = readyQueueTail = pcb; 
  } else {
    readyQueueTail->next = pcb;
    readyQueueTail = pcb;
  }
}

int main() {

  readyQueueHead = readyQueueTail = NULL;

  createProcess();
  createProcess();

  struct PCB* p;
  for(p = readyQueueHead; p != NULL; p = p->next) {
    printf("pid: %d state: %d ax: %d bx: %d cx: %d dx: %d pc: %d psw: %d\n",
           p->pid, p->state, p->ax, p->bx, p->cx, p->dx, p->pc, p->psw);
  }

  return 0;
}

 对以上代码进行一下解释防止有人看不到创建过程:

  1. 首先定义了进程状态和最大进程数的宏,以及进程控制块(PCB)的数据结构,其中包含了进程的所有必要信息。

  2. 然后使用一个PCB池来预先分配PCB的内存,并定义两个指针readyQueueHead和readyQueueTail来维护就绪队列。

  3. createProcess函数实现了进程的创建和PCB的初始化: (1) 首先检查PCB池是否还有可用PCB,如果满了则返回错误 (2) 然后从池中分配一个PCB,并填写进程信息如PID (3) 初始化PCB中的寄存器状态值为0 (4) 最后将PCB插入到就绪队列的尾部

  4. main函数先做就绪队列的初始化,然后调用createProcess来创建两个进程。

  5. 通过遍历就绪队列并打印每个PCB的信息,可以验证进程创建和队列管理的正确性。

  6. 这样通过PCB池、就绪队列和创建函数的配合,实现了进程控制块的动态分配与回收,以及进程状态的维护和管理。

展示一下运行效果图(如果大家的运行效果是这样就说明完成了)

我使用是编译环境是DevC++大家也可以使用其他的编译环境。

 难点讲解

pcb->state = READY;这个是什么意思

pcb->state = READY;这行代码的作用是设置PCB的状态为就绪(READY)状态。

这里我使用了一个宏定义:

#define READY 1

将就绪状态定义为1。

所以pcb->state = READY; 就会将pcb的state字段设置为1,表示该PCB对应的进程现在是就绪(ready)状态。

之所以要设置状态,是为了后续在调度算法选择新进程运行时,可以根据进程状态来判断,只有就绪状态的进程才能被调度运行。

此外还可以定义其它状态,如:

#define RUNNING 2 
#define BLOCKED 3

设置状态主要是为了在程序中表示和跟踪一个进程的当前情况。

createProcess();   createProcess();这个为什么要写两个 

createProcess(); 函数是用来创建一个新的进程并将其添加到就绪队列中的。

之所以示例代码中调用了两个 createProcess();,主要是为了创建两个进程做测试:

createProcess();  // 创建第一个进程
createProcess();  // 创建第二个进程

创建第一个进程后,就绪队列中只有一个进程。

再创建第二个进程后,就绪队列中就已经有两个进程了。

这样当我们后面遍历就绪队列并打印每个进程的信息时,可以看到两个进程的输出,验证创建和就绪队列管理是否正确。

如果只调用了一次 createProcess();,那么就绪队列中只会有一个进程,输出也就只有一个进程的信息,不太容易观察到链表和过程是否正确。

所以调用两次主要是为了创建多个进程,方便后续的打印调试与验证。

PCB的组成部分和需要体现的信息。 

struct PCB {
  int pid; // 进程标识符
  int state; // 进程状态 
  
  int ax; // 通用寄存器
  int bx;
  int cx;
  int dx;
  
  int pc; // 程序计数器
  int psw; // 程序状态字
  
  struct PCB* next; // 指向下一个PCB的指针  
};

以上PCB主要修改了以下部分:

  1. 添加了ax、bx、cx、dx通用寄存器
  2. 添加了pc程序计数器
  3. 添加了psw程序状态字寄存器

这样PCB中就包含了所需的标识信息、说明信息、现场信息和管理信息。

后续在创建进程时,可以要求输入或随机生成这些寄存器的值;在调度运行过程中,需要保存和恢复这些寄存器的现场。

 初始化寄存器值   pcb->ax = 0;   pcb->bx = 0;   pcb->cx = 0;   pcb->dx = 0;   pcb->pc = 0;   pcb->psw = 0;是什么意思

 这几行代码是用于初始化新创建的PCB中的寄存器的值,将它们都设置为0。

具体来说:

pcb->ax = 0; 意思是将该PCB的ax寄存器值设置为0。

pcb->bx = 0; 意思是将该PCB的bx寄存器值设置为0。

以此类推,将cx、dx、pc和psw寄存器都设置为了0。

之所以要这么做初始化,主要有以下两个原因:

  1. 刚创建的新进程,它的寄存器内容应该是未定义的。在实际中,可能是随机数或其它值,但比较简单的方法是初始化为0。

  2. 为了后面打印输出时,这些寄存器有一个确定的值,便于显示,验证PCB结构和数据是否正确。

如果不进行初始化,打印输出时就是一些未定义的随机值,不方便调试。

所以这些代码就是设置新PCB的寄存器值为0,主要是为了简化处理和打印输出。

if(allocPCBCount >= MAX_PCB)这个什么意思 

这个if语句是用于检查当前PCB池中是否还有可用的PCB来分配给新进程。

具体逻辑是:

  1. 定义了最大PCB数量MAX_PCB,例如10

  2. allocPCBCount记录当前已经分配出去的PCB数量

  3. 当去申请新PCB时,先检查:

    if(allocPCBCount >= MAX_PCB)

    如果已分配数量大于等于最大数量,表示PCB池已满,没有可分配的PCB

  4. 这个时候就会打印输出 "No free PCB" 来标示分配失败

  5. 并且return直接退出,不再执行后续PCB分配逻辑

所以这个if语句就是控制整个PCB申请过程的关键逻辑,用来检查和处理PCB池为空时的情况。

在主函数中,可以多次调用createProcess来模拟申请PCB,当分配到最大数后,再调用就会失败,这就完成了PCB申请和管理的基本机制。

这样就结束了,感谢大家的观看,百分百成功,不会出现运行不了的问题。但一定要按照小光的代码复制,全部复制粘贴就行了。

 结语 

关注小光,小光帮你写实验报告(不是真的帮你写,就是我写好,你直接复制拿走的那种)也可以看看小光的其他文章。

🌌点击下方个人名片,交流会更方便哦~(欢迎到博主主页加入我们的 CodeCrafters联盟一起交流学习↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓    

  • 36
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
一、实验题目:页面置换算法(请求分页) 二、实验目的: 进一步理解父子进程之间的关系。 1) 理解内存页面调度的机理。 2) 掌握页面置换算法的实现方法。 3) 通过实验比较不同调度算法的优劣。 4) 培养综合运用所学知识的能力。 页面置换算法是虚拟存储管理实现的关键,通过本次试验理解内存页面调度的机制,在模拟实现FIFO、LRU等经典页面置换算法的基础上,比较各种置换算法的效率及优缺点,从而了解虚拟存储实现的过程。将不同的置换算法放在不同的子进程中加以模拟,培养综合运用所学知识的能力。 三、实验内容及要求 这是一个综合型实验,要求在掌握父子进程并发执行机制和内存页面置换算法的基础上,能综合运用这两方面的知识,自行编制程序。 程序涉及一个进程和两个子进程。父进程使用rand()函数随机产生若干随机数,经过处理后,存于一数组Acess_Series[]中,作为内存页面访问的序列。两个子进程根据这个访问序列,分别采用FIFO和LRU两种不同的页面置换算法对内存页面进行调度。要求: 1) 每个子进程应能反映出页面置换的过程,并统计页面置换算法的命中或缺页情况。 设缺页的次数为diseffect。总的页面访问次数为total_instruction。 缺页率 = disaffect/total_instruction 命中率 = 1- disaffect/total_instruction 2)将为进程分配的内存页面数mframe 作为程序的参数,通过多次运行程序,说明FIFO算法存在的Belady现象。
1. 目的: 调试、修改、运行模拟程序,通过形象化的状态显示,使学生理解进程的概念,了解同步和通信的过程,掌握进程通信和同步的机制,特别是利用缓冲区进行同步和通信的过程。通过补充功能,使学生能灵活运用相关知识,培养创能力。 2. 内容及要求: 1) 调试、运行模拟程序。 2) 发现并修改程序中不完善的地方。 3) 修改程序,使用随机数控制创建生产者和消费者的过程。 4) 在原来程序的基础上,加入缓冲区的写互斥控制功能,模拟多个进程存取一个公共缓冲区,当有进程正在写缓冲区时,其他要访问该缓冲区的进程必须等待,当有进程正在读取缓冲区时,其他要求读取的进程可以访问,而要求写的进程应该等待。 5) 完成1)、2)、3)功能的,得基本分,完成4)功能的加2分,有其它功能改进的再加2分 3. 程序说明:   本程序是模拟两个进程,生产者(producer)和消费者(Consumer)工作。生产者每次产生一个数据,送入缓冲区中。消费者每次从缓冲区中取走一个数据。缓冲区可以容纳8个数据。因为缓冲区是有限的,因此当其满了时生产者进程应该等待,而时,消费者进程应该等待;当生产者向缓冲区放入了一个数据,应唤醒正在等待的消费者进程,同样,当消费者取走一个数据后,应唤醒正在等待的生产者进程。就是生产者和消费者之间的同步。   每次写入和读出数据时,都将读和写指针加一。当读写指针同样时,又一起退回起点。当写指针指向最后时,生产者就等待。当读指针为零时,再次要读取的消费者也应该等待。 为简单起见,每次产生的数据为0-99的整数,从0开始,顺序递增。两个进程的调度是通过运行者使用键盘来实现的。 4. 程序使用的数据结构 进程控制块:包括进程名,进程状态和执行次数。 缓冲区:一个整数数组。 缓冲区说明块:包括类型,读指针,写指针,读等待指针和写等待指针。 5. 程序使用说明   启动程序后,如果使用'p'键则运行一次生产者进程,使用'c'键则运行一次消费者进程。通过屏幕可以观察到两个进程的状态和缓冲区变化的情况。
1.目的: 自行编制模拟程序,通过形象化的状态显示,深入理解进程的概念、进程之间的状态转换及其所带来的PCB内容 、组织的变化,理解进程与其PCB间的一一对应关系。 2. 内容及要求: 1) 设计并实现一个模拟进程状态转换及其相应PCB内容、组织结构变化的程序。 2) 独立编写、调试程序。进程的数目、进程的状态模型(三状态、五状态、七状态或其它)以及PCB的组织形式可自行选择。 3) 合理设计与进程PCB相对应的数据结构。PCB的内容要涵盖进程的基本信息、控制信息、资源需求及现场信息。 4) 设计出可视性较好的界面,应能反映出进程状态的变化引起的对应PCB内容、组织结构的变化。 5) 代码书写要规范,要适当地加入注释。 6) 认真进行预习,完成预习报告。 7) 实验完成后,要认真总结,完成实验报告。 3.使用的数据结构及说明: 在本实验中,主要用到的数据结构是PCB的结构,其中PCB的数据结构如下: struct PCB { int P_Id; //PCB的ID号 char P_Name[10]; //PCB的名称 char P_State[10]; //PCB状态 int P_Runtime; //PCB的所需要的运行时间 int P_Requiry; //PCB所需要的资源要求 struct PCB * next ; //PCB块的下一个指针 } ; 其中,P_Id,和P_Name用来标示一个进程,而P_State用来标示进程的五种状态:Create_state,Ready_state,Block_state,Run_state,Exit_state。P_Runtime标示要完成一个进程所需要的时间。P_Requiry标示一个进程的执行所需要的其他条件,当其他的条件满足,则P_Requiry置1,否则置0。Struct PCB * next 用来指向同一队列中的下一个PCB块。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

命运之光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值