计算机操作系统实验---进程调度

操作系统实验报告--进程调度计科02-8 王长青05年4月17日计算机操作系统实验 ——进程调度一.实验目的进程调度是处理机管理的核心内容通过本实验可以加深理解有关进程控制块、进程队列的概念,并体会和了解优先数调度算法的具体实施办法二.程序功能本程序使用VC++编译调试,用于实现进程优先数调度的模拟主要包含三个模块:1、主界面:用于显示进程调度的过程2、数据录入模块:用于获取进程的初始值,其中有三种获取方式,手动输入方式、随即生成方式和从文件中读去数据的方式当用户在主窗口中点击“开始”菜单项时即可打开数据录入对话框,用户通过这三种方式之一均可完成数据的录入3、进程控制模块:主要实现创建新的进程,就绪队列的管理,完成队列的管理,进程的调度三.实验原理(1)本程序采用优先数调度算法对进程进行调度,每个进程可有三个状态,即:就绪状态,运行状态,完成状态并假设初始状态为就绪状态这三种状态的转换情况如右图:(2)为了便于处理,程序中的某进程运行时间以时间片为单位计算各进程的优先数以及进程需运行的时间片数的初始值均由用户给定(通过数据录入模块完成)3)程序通过设置一个定时器来实现时间片的轮转,时间片的大小是1秒,在定时器消息的响应函数中从用户录入的数据中读取一个创建进程,将其加入到就绪队列中,然后进行调度和执行。
在调度函数中,对于遇到优先数一致的情况,采用FIFO策略解决4)在优先数算法中,进程每执行一次,优先数减3,进程还需要运行的时间数减1四.详细设计(1)设计进程控制块PCB结构:struct PCB{ int pid; //进程号 int pri; //进程优先数 int time; //进程所需运行时间 int status; // 进程状态 0就绪,1 执行,-1完成};(2)将进程的各种操作封装在类CProMoni中,该类的定义如下:class CProMoni {public: CProMoni(); virtual ~CProMoni(); void InsertRQ(PCB* p); //将p所指的进程插入到就绪队列中 void InsertFQ(PCB* p); //将p所指的进程插入到完成队列中 void ProSchedule(); //进程调度函数 void ProRun(); //运行函数 void Display(CDC* pDC); //以表格形式输出运行过程 bool GetFinishFlag(); bool OpenLogFile(); //打开日志文件 void CloseLogFile(); //关闭日志文件 bool WriteLogToFile(); //向日志文件中写入数据private: PCB *m_pRunning; //指向当前运行的进程 CPtrList m_readyList; //就绪队列 CPtrList m_finishList; //完成队列 bool m_finish; //完成标志 CString m_LogFileName; //日志文件名 CStdioFile m_LogFile; //日志文件public: int m_clock; //时钟序列};(3)主要成员函数的实现:void CProMoni::InsertRQ(PCB* p){ //将p插入到就绪队列中 POSITION pre,pos=m_readyList.GetHeadPosition(); PCB *q; while(pos!=NULL) { pre=pos; q=(PCB*)m_readyList.GetNext(pos); if(q->pri < p->pri) { m_readyList.InsertBefore(pre,p); return; } } if(pos==NULL) { m_readyList.AddTail(p); }}void CProMoni::ProSchedule(){//进程调度 PCB *p; if(m_pRunning==NULL) { if(m_readyList.IsEmpty()) { m_finish=true; return; } else { p=(PCB*)m_readyList.RemoveHead(); m_pRunning=p; m_pRunning->status=1; } } else { if(!m_readyList.IsEmpty()) { p=(PCB*)m_readyList.GetHead(); //m_readyList将头节点与当前PCB的权值比较 if(p->pri > m_pRunning->pri ) { PCB *q=m_pRunning; m_pRunning=(PCB*)m_readyList.RemoveHead(); m_pRunning->status=1; q->status=0; InsertRQ(q); } } }}void CProMoni::ProRun(){//运行进程 if(!m_finish) { if(m_pRunning==NULL) { AfxMessageBox("当前运行的进程不存在!"); return; } m_pRunning->pri-=3; m_pRunning->time-=1; if(m_pRunning->time <= 0 ) { m_pRunning->time=0; PCB*p=m_pRunning; p->status=-1; InsertFQ(p); m_pRunning=NULL; } }}(4)试图类的主要成员函数:PCB* CProcessView::CreatePCB(){//创建PCB PCB* p=new PCB; p->pid=n+1; p->pri=m_pris[n]; p->time=m_times[n]; p->status=0; n++; return p;}#include"pritimedlg.h"void CProcessView::OnStart() { CPriTimeDlg dlg; //定义数据录入对话框 dlg.DoModal(); if(dlg.m_ok) { m_proTotal=dlg.m_proNum; for(int i=0;i 但在实验的过程中也遇到了一些问题起初,按照实验要求,从文件中读去实验数据,我首先就想到了使用MFC中的CFile这个文件类,但是当把读去的数据转换成整数时却变成了乱码,由于当时没有想出很好的解决办法,于是我就退而求其次,便通过对话框让用户输入实验所需的数据,后来在不断调试过程中,为了省去反复输入数据的麻烦,就又增加了随机生成数据的功能不过最后想出了从文件中读去实验数据的办法,那就是先通过CStdioFile这个处理文本文件的类,从文件中先读去数据字符串,在从字符串中提取出实验数据遇到的另一个问题是关于调度过程的显示,首先我是按照时间序列的增长,把每一对应时刻的调度情况通过一个表格的形式显示出来但这只能反映某一时刻的情况不能一次反映出整体的调度过程,于是我又在CProMoni类中增加了用于记录进程调度整体过程的三个链表然后每次调度的时候就把相应的调度过程信息添加到这三个链表中,这样就可以通过读取这三个链表中的数据来反映出整个调度的情况当然实验过程中遇到的不仅是这些问题,其他的就不一一列举拉总之,尽管实验中碰到了很多问题,但经过不断的修改和完善最终完成了这次实验当然,这个程序还不能算很完美,仍然存在一些小问题,比如:如果输入的数据过多,在显示调度过程时,由于就绪队列过长,可能会导致就绪队列中的进程信息在窗口中显示时会超出其显示范围而跑到完成队列的显示区域中。 当然解决的方法应该是根据就绪队列的大小动态地设置其显示区域还有窗口的滚动条也应该根据需要动态的进行设置其滚动范围,由于时间关系也只能这样牵强拉七、实验说明本程序在VC++6.0环境下调试通过,由于篇幅所限,报告中只给出了部分关键代码。