系统管理进程在系统中各种运行活动都离不开对于进程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管理与调度程序