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

本文主要是介绍操作系统 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;
}

这篇关于操作系统 c语言简单模仿进程创建和时间片轮转调度算法中的进程调度的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/996780

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

服务器集群同步时间手记

1.时间服务器配置(必须root用户) (1)检查ntp是否安装 [root@node1 桌面]# rpm -qa|grep ntpntp-4.2.6p5-10.el6.centos.x86_64fontpackages-filesystem-1.41-1.1.el6.noarchntpdate-4.2.6p5-10.el6.centos.x86_64 (2)修改ntp配置文件 [r

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu2289(简单二分)

虽说是简单二分,但是我还是wa死了  题意:已知圆台的体积,求高度 首先要知道圆台体积怎么求:设上下底的半径分别为r1,r2,高为h,V = PI*(r1*r1+r1*r2+r2*r2)*h/3 然后以h进行二分 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#includ

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

usaco 1.3 Prime Cryptarithm(简单哈希表暴搜剪枝)

思路: 1. 用一个 hash[ ] 数组存放输入的数字,令 hash[ tmp ]=1 。 2. 一个自定义函数 check( ) ,检查各位是否为输入的数字。 3. 暴搜。第一行数从 100到999,第二行数从 10到99。 4. 剪枝。 代码: /*ID: who jayLANG: C++TASK: crypt1*/#include<stdio.h>bool h

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO