您的位置:首页 > 其它

作业二:进程PCB管理与调度程序

2010-11-29 21:48 531 查看

作业二:进程PCB管理与调度程序

系统管理进程在系统中各种运行活动都离不开对于进程PCB的操作,本次作业以进程的PCB为管理目标,实现进程调度时状态变化相应的操作。

作业描述

使用数据结构struct PCB表示进程,记录进程的相关信息;进程信息包括:进程内部标识符pid、进程名pname、进程状态pstatus、

进程运行时间ptime、进程队列指针pnext。请设计合理的数据结构,容纳这些信息,以下是示例代码。

#define RUNNING0  /* 执行  */
#define READY1  /* 就绪  */
#define FINISHED2  /* 结束 */
#define EMPTY3  /* PCB表空闲  */
struct PCB    {
int  pid;
char pname[8];
int  pstatus;
int  ptime;
PCB *pnext;
};


设置三个队列(链表),分别是就绪队列、执行队列和空闲PCB队列。

程序初始化的时候先创建空闲PCB队列,然后根据用户输入的进程数量创建进程并构成进程就绪队列。

使用链表表示进程所在队列。

进程按着顺序轮流执行,每个进程一次执行m个时间片,m大于等于1。

进程PCB中的ptime初始值是进程运行完毕需要的时间,初始值可以随机赋值一个整数,运行过程中每运行一次则:

ptime = ptime - m;    if (ptime<=0)         执行-->完成;  /* 进程从执行态转变为完成 */

直到ptime小于或等于0表示该进程可以在最后一次分配的时间片中执行完毕。

编写基本管理函数,包括添加insert_pcb()、删除del_pcb()、修改mod_pcb()。

进程调度及状态变化时屏幕打印相关信息。

这里是一个例子程序(点击下载),如果你不太明白题目的意思,可以运行这个例子程序,看看输出结果你是否能够看明白。

背景知识

1. 进程控制块PCB。PCB是进程的重要数据结构,包括了重要的控制信息,如进程标识、处理机现场、进程状态、资源列表等。

2. 队列与链表。使用链表操作实现队列中节点的添加、修改和删除。

3. 进程状态。进程的基本状态包括执行、就绪和阻塞三种,引入挂起后,变为执行、活动就绪、活动阻塞、静止就绪和静止阻塞等。

进程在生命周期中的活动就是在这些状态上进行变化。

/*----------------------------------------------------------------------------------------------------------------*/

实现如下:



下面代码基本是基于进程控制块的组织方式(a.链接方式 )- 来实现的。

程序实现比较简化,如没有阻塞队列等。

基于如图的这种结构,即认为-系统开辟的PCB区为连续的存储结构来存储。

故以下代码是基于静态链表(空闲PCB队列,就绪队列)来实现的,

因为如图的这种结构,在用数据结构模拟的前提下,若用动态指针(PCB若指针链式连接,则动态较好),反而可能显得臃肿。

主要思路能显得清晰即可。

代码如下:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
using namespace std;
#ifdef LINUX
#include <unistd.h>
#define SLEEP(N) sleep(N)
#endif

#ifdef WIN32
#include <windows.h>
#define SLEEP(N) Sleep(N)
#endif
#define NUM 6//默认系统PCB区有6个PCB
int each_time = 1;
int N = 0;
char sname[6][3] = {"p1", "p2", "p3", "p4", "p5", "p6"};

typedef enum {
RUNNING,
READY,
FINISHED,
EMPTY
}Pstatus; //记录进程状态的枚举类型

typedef struct Process {
int  pid;
char pname[8];
int  pstatus;
int  ptime;
int pnext;

Process() {
pid = -1;
pstatus = EMPTY;
ptime = 0;
pnext = -1;
memset(pname, 0, sizeof(pname));
}
}Process;
Process PCB[NUM], Pre_PCB[NUM];

typedef struct Pqueue {
int top;
int rear;
}Pqueue;
Pqueue ready, pre_ready;
Pqueue pfree;
int run, pre_run;

void Creat_Pcb(int pid) {
PCB[pid].pstatus = READY;
strcpy(PCB[pid].pname, sname[N++]);
PCB[pid].ptime = rand() % 8 + 1;
PCB[pid].pnext = -1;
}//分配PCB给进程

void Push_Pqueue(int pid, Pqueue &cur) {
if(cur.top == -1) {
cur.top = pid;
} else {
PCB[cur.rear].pnext = pid;
}
cur.rear = pid;
PCB[cur.rear].pnext = -1;
}//压入队列,就绪队列或空闲队列

int Pop_Pqueue(Pqueue &cur) {
if(cur.top == -1) {
printf("队列为空,ERROR。\n");
return -1;
}
int pid = cur.top;
cur.top = PCB[pid].pnext;
if(cur.top == -1) {
cur.rear = -1;
}
PCB[pid].pnext = -1;
return pid;
}//弹出队列

void Del_Pcb(int pid) {
PCB[pid].ptime = 0;
PCB[pid].pnext = -1;
Push_Pqueue(pid, pfree);
}//回收PCB,恢复到空闲状态

void Insert_Pcb() {
if (pfree.top == -1) {
puts("空闲队列为空, 创建进程失败");
return ;
}
int pid = Pop_Pqueue(pfree);//从空闲PCB队列取出PCB
Creat_Pcb(pid);//分配PCB给进程
Push_Pqueue(pid, ready);//进入就绪队列
}//创建进程

void Print_Status(Process state[], Pqueue cur, int ran);
void Modify_Pcb() {//进程调度
if (ready.top == -1) {
puts("系统没有进程可以调度");
return ;
}
pre_run = run;
pre_ready = ready;
int pid = Pop_Pqueue(ready);
run = pid;
PCB[pid].pstatus = RUNNING;

puts("上一步状态:");
Print_Status(Pre_PCB, pre_ready, pre_run);
puts("当前状态:");
Print_Status(PCB, ready, run);//输出状态
for (int i = 0; i < N; i++) {
Pre_PCB[i] = PCB[i];
}//保存当前状态的前驱。
if (PCB[pid].ptime - each_time > 0) {
PCB[pid].ptime -= each_time;
PCB[pid].pstatus = READY;
Push_Pqueue(pid, ready);
} else {
PCB[pid].ptime = 0;
PCB[pid].pstatus = FINISHED;
Del_Pcb(pid);
}//修改PCB状态,保存现场(这里仅ptime)
}

void Init_Pcb() {
int i;
N = 0;
each_time = rand() % 6 + 1;//随机生成时间片大小
for (i = 0; i < NUM; i++) {
PCB[i].pid = i;
PCB[i].pnext = i + 1;
}

PCB[NUM-1].pnext = -1;
pfree.top = 0;
pfree.rear = NUM - 1;
ready.top = -1;
ready.rear = -1;
run = -1;
}//系统PCB区的初始化状态

void Print_Status(Process state[], Pqueue cur, int ran) {
puts("╔═══════════════════════╗");
puts("║    进程名     进程状态    进程剩余执行时间   ║");
for (int i = 0; i < N; i++) {
printf("║\t%s\t", state[i].pname);
switch(state[i].pstatus) {
case RUNNING: printf(" RUNNING\t");break;
case READY: printf(" READY\t\t");break;
case FINISHED: printf(" FINISHED\t");break;
default:break;
}
printf("%d\t\t║\n", state[i].ptime);
}
puts("╚═══════════════════════╝");
printf("╠═  Ready_queue :");
for (int i = cur.top; i != -1; i = state[i].pnext)
printf(" %s ->", state[i].pname);
printf(" NULL\n");
printf("╠═  Active_queue:");
if (ran != -1) printf(" %s ->", state[ran].pname);
puts(" NULL");
}//输出状态函数

void speed(int v) {
switch(v) {
case 1:
SLEEP(500);
break;
case 2:
SLEEP(1600);
break;
case 3:
SLEEP(2400);
break;
default: break;
}
}//自动演示模式的分档(停顿间隔)函数

int Check_Input(char str[]) {
int i, k = 0;
int st = -1, ed = -1;
char stmp[110];
for (i = 0; str[i]; i++) if (str[i] != ' ') {st = i; break;}
for (i = strlen(str) - 1; i >= 0; i--) if (str[i] != ' ') {ed = i; break;}
if (st == -1) return -2;
for (i = st; i <= ed; i++) {
if (!(str[i] >= '0' && str[i] <= '9')) {
return -1;
} else {
stmp[k++] = str[i];
}
}
int r = 1, sum = 0;
for (i = k - 1; i >= 0; i--, r *= 10) {
sum += (stmp[i] - '0') * r;
}
return sum;
}//输入处理,做简单的排错处理

void solve() {
int n;
char str[110];
puts("输入进程的数量(范围在[1-6]内):");
while (gets(str)) {
while ((n = Check_Input(str)) < 0 || n > 6) {
if (n == -1 || n > 6) {//输入检查处理
puts("ERROR, 请重新输入");
}
gets(str);
}
Init_Pcb();//初始化
for (int i = 0; i < n; i++) {
Insert_Pcb();
}
for (int i = 0; i < n; i++) {
Pre_PCB[i] = PCB[i];
}//保存前一个状态的信息
puts("初始进程情况:");
Print_Status(PCB, pre_ready, run);
printf("╠═  时间片大小为 %d\n", each_time);
int mode = 1;
int v = 1;
puts("\n请选择演示模式:1(自动)   2(手动)");
gets(str);
while (! ((mode = Check_Input(str)) >= 1 && mode <= 2) ) {
if (mode != -2) {
puts("ERROR, 请重新输入");//输入检查处理
}
gets(str);
}
if (mode == 1) {
puts("1:快速演示\t2:中速演示\t3:慢速演示");
gets(str);
while (! ((v = Check_Input(str)) >= 1 && v <= 3) ) {
if (v != -2) {
puts("ERROR, 请重新输入");//输入检查处理
}
gets(str);
}
}

while (true) {
system("cls");
Modify_Pcb();//进程调度
if (ready.top == -1) {//就绪队列空。
if (mode == 2) {
puts("点击前往下一步");
system("pause");
}
break;
}
if (mode == 1) {
speed(v);//自动模式停顿
} else {
puts("点击前往下一步");
system("pause");//以完成手动模式手动翻页
}
}
if (mode == 1) speed(v);//自动模式停顿
system("cls");
puts("上一步状态:");
Print_Status(Pre_PCB, ready, run);
puts("当前状态:");
for (int i = 0; i < n; i++) PCB[i].pstatus = FINISHED;
run = -1;
Print_Status(PCB, ready, run);//完成状态
puts("congratulation!!!进程全部成功运行结束!");
system("pause");
system("cls");
puts("输入进程的数量(范围在[1-6]内):");
}
}

int main() {
srand((unsigned)time(NULL));
solve();
return 0;
}


以下是上面代码的.exe可执行文件(传不上去,rar压缩文件)。

点击 下载

12.5号 - 重新写了个PCB用指针链式连接的程序,伪界面简单一些。

此程序的特点主要是简单易懂一些,简化了一些浮云的处理;

叹自己平时都不用指针的,用起指针来就显得比较生疏,过程中出现诸如内存不可读很多次。

不过最后程序还算清晰,调度函数Modify_Pcb()相对上面程序而言写的要清晰一些。

  (上面的写的戳了,最后一次输出要特殊到外面处理,这里就不需要-)。

代码如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#define NUM 6
#define RUNNING 0  /* 执行  */
#define READY 1  /* 就绪  */
#define FINISHED 2  /* 结束 */
#define EMPTY 3  /* PCB表空闲  */
int each_time = 1;//默认时间片为1
int N = 0;
char sname[6][3] = {"p1", "p2", "p3", "p4", "p5", "p6"};

typedef struct PCB {
char pname[8];
int  pstatus;
int  ptime;
PCB *pnext;
}PCB, *QueuePtr;

typedef struct {
QueuePtr front;
QueuePtr rear;
}LinkQueue;
LinkQueue p_empty, p_ready, p_run, p_end;

void InitQueue(LinkQueue &Q) {
Q.front = Q.rear = (QueuePtr)malloc(sizeof(PCB));
}//初始化队列

void EnQueue(LinkQueue &Q, QueuePtr p) {
Q.rear->pnext = p;
Q.rear = p;
p->pnext = NULL;
}//压入队列

void DeQueue(LinkQueue &Q, QueuePtr &p) {
if (Q.front == Q.rear) {
printf("ERROR,队列为空!");
}
p = Q.front->pnext;
Q.front->pnext = p->pnext;
p->pnext = NULL;
if (Q.rear == p) Q.rear = Q.front;
}//出队列,将出队元素的结点指针用p保存

void Insert_Pcb() {
QueuePtr p;
DeQueue(p_empty, p);//从空闲队列取出PCB
EnQueue(p_ready, p); //加入到就绪队列
strcpy(p->pname, sname[N++]);
p->pstatus = READY;
p->ptime = rand() % 8 + 1;//初始化信息,名字,状态
//进程执行时间(rand随机生成,rand() %8+1后在1-8范围)
printf("创建进程%s,执行时间%d     <创建成功>\n", p->pname, p->ptime);
}//创建进程

void Del_Pcb(QueuePtr &p) {
p->ptime = 0;
p->pstatus = FINISHED;
p->pnext = NULL;
EnQueue(p_end, p);
}//删除进程,加入到完成状态队列中。

void Modify_Pcb() {//进程调度
QueuePtr cur;
puts("===============执行调度=============");
if (p_run.front != p_run.rear) {//执行队列不空
DeQueue(p_run, cur);//弹出执行进程
cur->ptime -= each_time;//执行一个时间片
printf("调度:进程%s     ", cur->pname);
if (cur->ptime > 0) {//若执行未结束,经调度到就绪状态
cur->pstatus = READY;
printf("<执行 -> 就绪>.\n");
EnQueue(p_ready, cur);
}
else {//执行结束
printf("<执行 -> 结束>.\n");
Del_Pcb(cur);//加入到结束队列
}
}
if (p_ready.front != p_ready.rear) {//就绪队列不空
DeQueue(p_ready, cur);//弹出就绪队列队首元素
EnQueue(p_run, cur);//进入执行态
cur->pstatus = RUNNING;
printf("调度:进程%s     <就绪 -> 执行>.\n", cur->pname);
printf("调度:%s执行中...\n", cur->pname);//调度执行
printf("就绪队列 : ");
QueuePtr ready = p_ready.front->pnext;
while (ready) {
printf("%s<1>->", ready->pname);
ready = ready->pnext;
}//输出就绪队列
puts("NULL");
printf("执行队列 : ");
printf("%s<0>->NULL\n\n", cur->pname);//输出执行队列
}
}

void Init_PCB() {
InitQueue(p_empty);
InitQueue(p_ready);
InitQueue(p_run);
InitQueue(p_end);//初始化队列
for (int i = 0; i < NUM; i++) {
QueuePtr p = (QueuePtr)malloc(sizeof(PCB));
p->pnext = NULL;
EnQueue(p_empty, p);
}//初始空闲队列为6个空闲PCB
}

void solve() {
int n;
printf("进程数量?[1-6]:");
scanf("%d", &n);
if (n < 1 || n > 6) { puts("输入数据不在1-6"); return ; }
Init_PCB();//初始化队列
for (int i = 0; i < n; i++) {
Insert_Pcb();//创建进程
}

while (true) {
Modify_Pcb();//进程调度(若就绪队列不空,调度后p_run不为空)
if (p_run.front == p_run.rear) {
break;//没有进程在执行时退出。
}
}
QueuePtr run;
printf("结束队列 : ");
QueuePtr end = p_end.front->pnext;
while (end) {
printf("%s<1>->", end->pname);
end = end->pnext;
}
puts("NULL");//输出结束队列(按结束次序)
}

int main() {
srand((unsigned)time(NULL));//以时间做随机种子
solve();
return 0;
}


原创文章如转载请注明:转自¥忘%风 {http://www.cnblogs.com/slave_wc}

本文地址: 作业二:进程PCB管理与调度程序
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐