当前位置: 首页 > news >正文

操作系统 c语言简单模仿进程创建和时间片轮转调度算法中的进程调度

1.实验目的

加深对进程概念的理解,明确进程和程序的区别;

深入了解系统如何组织进程、创建进程;

进一步认识如何实现处理器调度。

2.实验预备知识

进程的概念;

进程的组织方式;

进程的创建;

进程的调度。

3.实验内容

编写程序完成单处理机系统中的进程调度,要求采用时间片轮转调度算法。实验具体包括:首先确定进程控制块的内容,进程控制块的组成方式;然后完成进程创建原语和进程调度原语;最后编写主函数对所作工作进程测试。

4.提示与讲解

这个实验主要要考虑三个问题:如何组织进程、如何创建进程和如何实现处理器调度。

考虑如何组织进程,首先就要设定进程控制块的内容。进程控制块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  front;

 int  rear;

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

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

以上是如何组织进程,下面考虑如何创建进程。

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

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

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

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

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

进程创建流程图如图2.2所示。

多道程序设计的系统中,处于就绪态的进程往往是多个,它们都要求占用处理器,可是单处理器系统的处理器只有一个,进程调度就是解决这个处理器竞争问题的。进程调度的任务就是按照某种算法从就绪进程队列中选择一个进程,让它占有处理器。因此进程调度程序就应该包括两部分,一部分是在进程就绪队列中选择一个进程,并将其进程控制块从进程就绪队列中摘下来,另一部分工作就是分配处理器给选中的进程,也就是将指向正在运行进程的进程控制块指针指向该进程的进程控制块,并将该进程的进程控制块信息写入处理器的各个寄存器中。

图2.2  进程创建CreateProcess流程图

实验中采用时间片轮转调度算法。时间片轮转调度算法让就绪进程按就绪的先后次序排成队列,每次总是选择就绪队列中的第一个进程占有处理器,但是规定只能使用一个“时间片”。时间片就是规定进程一次使用处理器的最长时间。实验中采用每个进程都使用相同的不变的时间片。

在时间片轮转调度算法中,当一个进程的时间片用完或者被其他高优先级进程抢占时,该进程会被放入就绪队列的尾部等待下一次调度。如果该进程在时间片用完之前已经执行完毕,那么它将被移出就绪队列。当调度器需要选择下一个要执行的进程时,它会从就绪队列的头部开始,

完成上述功能后,编写主函数进行测试:首先建立一个就绪队列,手工输入信息建立几个进程;然后进行进程调度;最后将指向正在运行进程的指针指向的进程控制块的内容输出,察看结果。

图2.3  进程调度ProcessSchedule流程图

#include <stdio.h>
#include <unistd.h>
#define n 10               //假定系统允许进程个数为10
#define aready 1
#define running 2
#define T 5struct pcb
{int name;               //进程标识符int status;                 //进程状态int ax,bx,cx,dx;         //进程现场信息 通用寄存器内容int pc;                       //进程现场信息 程序计数器内容int psw;                    //进程现场信息 程序状态字寄存器内容int next;float totalTime;             //总的运行时间
};
struct pcb pcbarea[n];          //模拟进程控制块区域的数组
int run;                                   //定义指向正在运行进程的进程控制块的指针
struct                                      //指向就绪队列的头指针head和尾指针tail
{int front;int rear;
}ready;
int pfree;                              //指向空闲进程队列的指针
float time;
void CreateProcess();
void RoundRobinSchedule();
void ProcessSchedule();
void AddAlready();int main()
{ready.rear=-1,ready.front=-1;           //初始化pfree = 0;for (int i=0;i<n-1;i++)pcbarea[i].next=i+1;pcbarea[n-1].next=-1;                   //初始化数组里的nextCreateProcess();                        //创建进程RoundRobinSchedule();               //时间片轮转调度算法
}
void CreateProcess()
{if (pfree==-1){printf("进程创建失败,空间已满");return;}while (pfree!=-1)               //没有空一直循环输入创建进程{int i = pfree;pfree = pcbarea[pfree].next;int name,ax,bx,cx,dx,pc,psw,totaltime;printf("请输入8个数字 空格间隔\n进程标识符 通用寄存器内容(4个),程序计数器内容,程序状态字寄存器内容,运行总时间 \n");scanf("%d%d%d%d%d%d%d%d",&name,&ax,&bx,&cx,&dx,&pc,&psw,&totaltime);pcbarea[i].name = name;pcbarea[i].status = aready;pcbarea[i].ax = ax;pcbarea[i].bx = bx;pcbarea[i].cx = cx;pcbarea[i].dx = dx;pcbarea[i].pc = pc;pcbarea[i].psw = psw;pcbarea[i].totalTime = totaltime;if (ready.front == -1)              //如果队列为空ready.front = i;                    //头指针指向新创建的elsepcbarea[ready.rear].next = i;       //不为空让队列里最后一个next指向新创建的ready.rear = i;                            //就绪队列的尾指针指向新创建的pcbarea[i].next = -1;                   //这个新的进程后面为空}if (pfree==-1)printf("进程创建成功,空间已满\n");
}void RoundRobinSchedule()
{time = T/(float)(ready.rear-ready.front+1);            //时间片与总的进程数量成反比,与一个周期总时间T成正比
printf("\n%f\n",time);while(ready.front!=-1)                                 //如果就绪队列不为空{ProcessSchedule();                                 //进程调度1个sleep(time);                                            //处理机处理时间if(pcbarea[run].totalTime>0)                 //如果这个进程还没有执行完AddAlready();                                       //继续把他加入就绪队列的尾部}
}void ProcessSchedule()
{int i=ready.front;                          //取就绪队列的第一个float TIME = time;                            //把时间片的值传递给处理机if (pcbarea[ready.front].next==-1)                          //如果就绪队列只有1个ready.rear = -1;                                        //把尾部指向空ready.front = pcbarea[ready.front].next;        //把就绪队列的头指针指向下一个 如果只有一个那么为空pcbarea[i].status = running;                        //状态改为运行中pcbarea[i].totalTime = pcbarea[i].totalTime-TIME;                         //更新总的时间 没运行一次减少一个时间片int AX=pcbarea[i].ax,BX=pcbarea[i].bx,CX=pcbarea[i].cx,DX=pcbarea[i].dx,PC=pcbarea[i].pc,PSW=pcbarea[i].psw,REMAIN=pcbarea[i].totalTime;        //现场恢复  pcb中的数据传递给处理机run = i;                                                            //运行进程的指针指向这一个printf("ax:%d bx:%d cx:%d dx:%d pc:%d psw:%d   totalTime:%f\n",pcbarea[run].ax,pcbarea[run].bx,pcbarea[run].cx,pcbarea[run].dx,pcbarea[run].pc,pcbarea[run].psw,pcbarea[run].totalTime);
}
void AddAlready()
{if (ready.front==-1)            //如果队列为空ready.front=run;               //把头指针指向正在运行的elsepcbarea[ready.rear].next = run; //不为空直接挂在最后一个进程后面ready.rear = run;                   //尾指针向后移动pcbarea[run].next = -1;     //把新加入的后面置为空pcbarea[run].status = aready;
}

相关文章:

  • 使用决策树对金融贷款数据进行分析
  • docker swarm多主机之间的端口无法访问,但能ping通 问题排查及解决
  • SQL常用基础语句(一)-- FGHIJ开头
  • 【C++初阶】--- C++入门(上)
  • 开源大模型与闭源大模型:技术哲学的较量
  • 微服务远程调用 RestTemplate
  • 【MySQL精通之路】SQL优化(1)-查询优化(8)-嵌套联接优化
  • 在docker中安装官方rocketmq
  • 【C语言回顾】联合和枚举
  • CTFshow之文件上传web入门151关-161关解密。包教包会!!!!
  • 基于树的存储数据结构demo
  • Ubuntu系统版本查看办法
  • (Qt) 默认QtWidget应用包含什么?
  • 汽车工厂安灯系统能够快速知晓生产现场的状况
  • github下载代码
  • [case10]使用RSQL实现端到端的动态查询
  • 【5+】跨webview多页面 触发事件(二)
  • 30天自制操作系统-2
  • C++类中的特殊成员函数
  • CSS相对定位
  • Leetcode 27 Remove Element
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • MYSQL 的 IF 函数
  • PHP 的 SAPI 是个什么东西
  • uva 10370 Above Average
  • webpack项目中使用grunt监听文件变动自动打包编译
  • 从0实现一个tiny react(三)生命周期
  • 利用jquery编写加法运算验证码
  • 猫头鹰的深夜翻译:Java 2D Graphics, 简单的仿射变换
  • 前端设计模式
  • 前端学习笔记之观察者模式
  • 如何合理的规划jvm性能调优
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • 我看到的前端
  • 物联网链路协议
  • 小程序 setData 学问多
  • 大数据全解:定义、价值及挑战
  • #pragma预处理命令
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • (1)(1.9) MSP (version 4.2)
  • (145)光线追踪距离场柔和阴影
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (Forward) Music Player: From UI Proposal to Code
  • (笔记)Kotlin——Android封装ViewBinding之二 优化
  • (二)linux使用docker容器运行mysql
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • (算法)Game
  • (文章复现)基于主从博弈的售电商多元零售套餐设计与多级市场购电策略
  • (学习日记)2024.02.29:UCOSIII第二节
  • (一)pytest自动化测试框架之生成测试报告(mac系统)
  • (一)基于IDEA的JAVA基础12
  • (原創) 如何使用ISO C++讀寫BMP圖檔? (C/C++) (Image Processing)
  • .h头文件 .lib动态链接库文件 .dll 动态链接库