第一篇:计算机操作系统-司机与售票员的进程问题-实验报告
计算机操作系统实验报告
-------售票员和汽车司机的进程同步问题
一、设计分析
司机与售票员要协同工作:一方面只有售票员把门关好之后司机才能开车,因此售票员关好门之后要通知司机开车;另一方面,也只有司机把车停下之后售票员才能开门让乘客下车和上车,此时司机应通知售票员。汽车当前正在始发站停车让乘客上车。因此,必须设置一定的信号量来实现他们之间的同步问题。
把司机与售票员的信号量设置为全局变量,并把客车上的人数:现在人数、下车人数、上车人数设置为全局变量;设置司机与售票员各自的线程。考虑到第一站和最后一站的问题,应单独处理,故在各自的线程中分情况讨论:
由于下车的人数是随机的,设计时考虑到了人数可能会超过客车的最大上限的问题。具体的思路是下面的图示。
二、算法实现(源代码)
#include
//假设汽车的最大容量为88 #define total_pork 9
//总的站数
int recent_num=0;
//某一时刻的客车上的人数 int get_on_num;
//上车的人数 int get_off_num;
//下车的人数 int pork=1;
//赋初始值 HANDLE SJ;
//司机的信号量 HANDLE SPY;
//售票员的信号量
int Get_random(int min, int max)
//产生一定范围的随机数,可避免下面程序的判断超出客车的最大容量问题{ int a;srand((int)time(0));
while(1){ a=rand()%(total_num+1);if(a>=min && a<=max)return a;} } //司机的线程
DWORD WINAPI Thread_Driver(LPVOID Driver){ while(pork<=total_num){ if(pork==total_pork){ WaitForSingleObject(SJ,INFINITE);cout<<“到达总站,欢迎您下次乘坐**路公交车”< { ReleaseSemaphore(SPY,1, NULL);WaitForSingleObject(SJ,INFINITE);cout<<“汽车启动”< } Sleep(1000);} return 0;} //售票员的线程 DWORD WINAPI Thread_Conductor(LPVOID SPY){ while(1){ if(pork cout<<“这是第”< WaitForSingleObject(SPY,INFINITE);if(pork==1){ cout<<“SPY开始售票”< get_on_num=Get_random(0,total_num-recent_num); cout< recent_num+=get_on_num;cout<<“共有”< { cout<<“SJ停好车,乘客开始上下车”< get_off_num=Get_random(0,recent_num); cout< return 0;} //主函数 int main(){ HANDLE SJ;HANDLE SPY;SJ=CreateSemaphore(NULL,0,1,“semaphore_driver”);//创建司机的信号量 SPY=CreateSemaphore(NULL,0,1,“semaphore_conductor”);//创建售票员的信号量 SJ=CreateThread(NULL,0,Thread_Driver,&SJ,0,NULL);//创建司机的线程 SPY=CreateThread(NULL,0,Thread_Conductor,&SPY,0,NULL);//创建售票员的线程 CloseHandle(SJ);CloseHandle(SPY);while(1);system(“pause”);return 0;} 三.实现结果 四、心得体会 1、因为司机与售票员是两条单独处理的线程。程序先对司机的线程进行设计,接着再进行售票员的线程设计。因为两者是需要相互协调,又先后顺序的,所以编起程序来比较复杂,而且很乱,尤其对于第一次接触的我们而言。 2、上下车的人数是随机的,所以,我们在编程序时必须注意使程序能够判断所出现的随机数在汽车可以承载的最大容量之内。 3、C++语言基础不是很好,所以编起程序来比较费力,这种设计性的实验对于我们而言还是有一定的难度的,所以部分程序是参照网上的类似程序。 实验报告说明书 设计名称: 操作系统课程设计 实 验 : 进程调度设计 学生姓名: 专 业: 网络工程 班 级: 08级一班 学 号: 指导教师:雷晓平王东 黄营 杨跃武 日 期: 2011年 6月 19日 课程设计任务书 网络工程 专业 08 年级 1 班 一、具体要求 本课程设计共2周,采取集中方式。㈠主要设计内容 1、进程调度 2、存储管理 3、文件管理 ㈡操作系统分项设计 1、设计一 :进程管理系统设计 目的与要求:本设计的目的是加深对进程概念及进程管理各部分内容的理解;熟悉进程管理中主要数据结构的设计及进程调度算法、进程控制机构、同步机构及通讯机构的实施。 要求设计一个允许n个进程并发运行的进程管理模拟系统。该系统包括有简单的进程控制、同步与通讯机构,其进程调度算法可任意选择。每个进程用一个PCB表示,其内容根据具体情况设置。各进程之间有一定的同步关系(可选)。系统在运行过程中应能显示或打印各进程的状态及有关参数的变化情况,以便观察诸进程的运行过程及系统的管理过程。 具体详见:设计任务书1--进程调度算法.doc 2、设计二:存贮器管理系统设计 目的与要求:本设计的目的是使学生熟悉存贮器管理系统的设计方法;加深对所学各种存贮器管理方案的了解;要求采用一些常用的存贮器分配算法,设计一个存贮器管理模拟系统并调试运行。模拟环境应尽量接近真实。 具体详见:设计任务书2--内存分区管理模拟.doc 3、设计三:虚拟存储器管理系统设计 本设计的目的是通过设计一个简单的虚拟存储器管理系统来模拟实际的页面调度算法与过程,以掌握这种有用的技术。要求将其输入/输出处理程序编成一个独立的进程模块并与其它请求输入/输出的进程并发运行。并要求加入设备管理子模块。 具体分析为:页面调度算法主要有FIFO、最近最少使用调度算法(LRU)、最近最不常用调度算法(LFU)、最佳算法(OPT)等。题目要求: ① 实现三种算法: 1、先进先出; 2、OPT; 3、LRU ② 页面序列从指定的文本文件(TXT文件)中取出 ③ 输出:第一行:每次淘汰的页面号,第二行:显示缺页的总次数 4、设计四:文件管理系统设计 目的与要求:本设计的目的是通过设计和调试一个简单的外部文件系统,主要是模拟文件操作,使学生对主要文件操作命令的实质和执行过程有比较深入的了解,掌握它们的基本实施方法。 基本要求如下: 实现三种算法: 先来先服务、最短寻道优先、电梯算法 磁道服务顺序从指定的文本文件(TXT文件)中取出 输出:第一行:磁道的服务顺序;第二行:显示移动总道数 5、设计五:多道程序的转换调度系统 题目要求:(作业调度和进程调度结合在一起)作业流信息是从指定文本文件(TXT文件)中读取 作业信息: 作业号 进入系统时间 估计运行时间 优先级 内存需求量 磁带机需求量 都为整型 作业调度算法:先来先服务、最短作业优先(二者选一) 进程调度算法:先来先服务、基于优先级的算法(静态优先级)(二者选一)输出格式:作业号 时间间隔 800-810(/* 8:00-8:10 */)2 810-900 1 900-930平均周转时间:总的周转时间/作业总数 周转时间就是作业结束时间减去作业进入系统时间 具体详见:多道程序转换.doc 以上设计以20-25人为组,选择一个设计进行。㈢操作系统整体设计 将第㈡部分中的全部或部分有机地组合起来,构成一个小型的操作系统。 二、进度安排 1、教师下达设计任务书(半天)任务书内容包括题目、主要技术指标和要求、给定条件及原始数据、所用仪器设备和参考资料及文献等。教师讲授必要的设计思路和设计方法。 2、学生完成预设计(1天半)本阶段学生通过查阅资料及文献(主要自学),明确任务,掌握工程设计基本方法,确定设计方案,进行设计分析,完成预设计。 3、实验阶段(7天)经教师审查通过预设计方案后,即可进行编程调试。实验由学生独立完成,教师定时指导。 4、设计总结阶段(1天)本阶段学生要认真完成课程设计报告书,整理技术资料,并尽可能写出课程设计的心得体会和改进意见。 三、完成后应上交的材料 课程设计报告书包括:设计任务及主要技术指标、设计方案及论证结果、系统的原理框图、设计程序、实验结果、实验中主要问题及故障现象的分析及设计结论等。 附实验数据、系统软硬件环境、使用说明及参考资料。 四、总评成绩 指导教师 签名日期 年 月 日 系 主 任 审核日期 年 月 日 目 录 一、实验目的………………………………………5 二、实验要求………………………………………5 三、具体内容………………………………………5 3.1先来先服务(FCFS)„„„„„„„5 3.2作业优先SJF 算法„„„„„„„„5 3.3多级队列调度算法„„„„„„„„.5 3.4时间片轮转调度法„„„„„„„„„5 3.5优先级法„„„„„„„„„„„„„6 3.6多队列反馈法„„„„„„„„„„„6 3.7轮转法„„„„„„„„„„„„„„.6 3.8最短周期优先„„„„„„„„„„„6 3.9先来先服务„„„„„„„„„„„„„7 四、实验程序………………………………………7 五、实验总结………………………………………13 设计题目:进程管理系统设计 一、实验目的: 本设计的目的是加深对进程概念及进程管理各部分内容的理解;熟悉进程管理中主要数据结构的设计及进程调度算法、进程控制机构、同步机构及通讯机构的实施。 二、实验要求: 设计一个允许n个进程并发运行的进程管理模拟系统。该系统包括有简单的进程控制、同步与通讯机构,其进程调度算法可任意选择。每个进程用一个PCB表示,其内容根据具体情况设置。各进程之间有一定的同步关系(可选)。系统在运行过程中应能显示或打印各进程的状态及有关参数的变化情况,以便观察诸进程的运行过程及系统的管理过程。 三、具体内容: 调度也称dispatcher,这是内核的主要职责之一。一个良好的任务调度算法应该主要体现在以下几个方面: 公平:保证每个进程得到合理的CPU 时间 高效:使CPU 保持忙碌状态,即总是有进程在CPU 上运行 响应时间:使交互用户的响应时间尽可能短 周转时间:使批处理用户等待输出的时间尽可能短 吞吐量:使单位时间内处理的进程尽可能多 很显然在任何操作系统中这几个目标不可能同时达到所以不同的。 操作系统会在这几个方面中做出相应的取舍从而确定自己的调度算法,常用的处理机调度算法有:先来先服务FCFS、短作业优先SJF、优先级、时间片轮转法、多级队列法、多级反馈队列法。 (1)先来先服务(FCFS) FCFS 是最简单的CPU 调度算法,即按进程到来的先后次序进行调度,这样在系统中等待时间最长的进程被优先调度,而不管其所需运行时间的长短。 (2)作业优先SJF 算法 是指当CPU 可供使用时SJF 算法把CPU 分给需要运行时间最短的进程。(3)多级队列调度算法 把就绪队列划分成几个单独的队列一般根据进程的某些特性如内存大小和进程类型永久性地把各个进程分别链入其中某一个队列中,每个队列都有自己的调度算法,此外在各个队列之间也要进行调度。通常采用固定优先级的抢占式调度,例如某系统中有5 个队列,各个队列的优先级自上而下降低,只有当前3 个队列中都为空的时候队列4 中的进程才可以运行,而当队列4 中的进程在运行时,如果队列1 中进入了一个就绪进程,则队列4 中的进程要立刻让出CPU 使用权。多级反馈队列法允许进程在各队列间移动,其基本思想是把具有不同CPU工作时间这一特性的进程区分开来,如果一个进程要使用很长的CPU 时间,则应把它移至较低级的队列中,这种方式把I/O 繁忙型和交互式进程放在较高优先级的队列中同样在低优先级队列中长时间等待的进程可以移到较高优先级队列中UNIX 系统采用的是多级反馈队列轮转法。 (4)时间片轮转调度法 当两个或两个以上任务有同样优先级,内核允许一个任务运行事先确定的一段时间叫做时间额度quantum,然后切换给另一个任务也叫做时间片调度time slicing,内核在满足以下条件时把CPU 控制权交给下一个任务就绪态的任务,当前任务已无事可做,当前任务在时间片还没结束时已经完成了。轮转法主要是为分时系统设计的,其中时间片是一个重要的参数不能取的过大或过小,通常为10 至100ms 数量级,就绪队列可以看成是一个环形队列,CPU 调度程序轮流 地把CPU 分给就绪队列中地每个进程,时间长度为一个时间片Linux 操作系统就是采用时间片轮转的调度算法。 (5)优先级法 优先级调度的基本思想是,把当前处于就绪队列中优先级最高的进程投入运行,而不管各进程的下一个CPU周期的长短和其他因素。 优先级通常用0~4095的整数(称为优先数)表示,是数大优先级高还是数小优先级高取决于规定。例如UNIX系统规定优先数越大优先级越低,优先数越小优先级越高。 优先数有静态和动态之分。静态优先数是指在进程开始运行之前便根据某种或某些因素(如估计运行时间、主存需求量、打开文件个数、所付经费多少等)算定,而且该优先数在进程的整个生命周期内一直不变。静态优先数方法虽然简单,但有可能导致某些低优先级的进程无限期地等待。尤其在高优先级的进程不断进入就绪队列的情况下,使等待CPU的低优先级进程更多,等待时间更长。动态优先数方法是按照某种原则使各进程的优先级随着时间而改变。例如随等待时间增大优先级也跟着提高、随着使用CPU时间的增长优先级跟着下降,就是一种较好的策略。等待了较长时间的进程,总会因其优先级不断地提高而被调度运行。 如果把进入就绪队列的次序作为计算进程动态优先数的主要指标,那么优先级法就变成FCFS。如果把下一个CPU周期最短作为计算进程动态优先数的主要指标,那么优先级法变成SBF,此时各进程的优先数p_pri = 1/ ti,其中ti是估算的下一个CPU周期的长度。 优先级调度也允许剥夺方式。现在的许多操作系统,例如UNIX系统V,Windows NT,OS/2等都采用优先级剥夺调度。 (6)多队列反馈法 其基本思想是,把就绪进程按优先级排成多个队列,同队列的进程具有相同的时间片。高优先级队列的时间片比低优先级队列的小。调度时先从高优先级队列中选出某一进程投入运行,当该进程的时间片到期后则转至低一级的就绪队列中。只有高优先级队列为空时才从低一级队列中调度进程。 例如考虑由3个队列组成的多级队列调度。3个队列的编号分别为0, 1, 2,如图所示。调度算法首先调度0号队列中的进程。当0号队列为空时才调度1号队列中的进程;当0号与1号队列都为空时才调度2号队列中的进程。在剥夺方式下,新进入0号队列的进程将剥夺1号或2号队列中正在执行的进程的CPU,而新进入1号队列的进程将剥夺2号队列中正在执行的进程的CPU。 其实,每个队列都可拥有各自的调度算法,也可制定不同的“转队”规则。这样看来,多队列反馈调度算法能有多种变化。 (7)轮转法 轮转法就是按一定时间片(记为q)轮番运行各个进程。如果q是一个定值,则轮转法是一种对各进程机会均等的调度方法。 轮转法本质上是剥夺的,因为一轮内,每个进程不能获得比一个时间片q更长的运行时间。正是由于这一特点,轮转法特别适用于分时操作系统。 轮转法的关键问题是如何确定q的大小。如果时间片太大以致每个进程的CPU周期都能在一个时间片内完成,则轮转法实际上脱化为FCFS。如果q太小以致CPU切换过于频繁,则会增加CPU的额外开销,降低了CPU的有效利用率。这是因为,每次CPU切换涉及到保存原运行进程的现场和恢复新运行进程的现场,这些操作一般需要10ms~100ms的时间。例如,设有一个CPU周期为10单位的进程,在q取12,6,1时的调度次数分别为0,1,9。令时间单位为1ms(1ms=1000ms),1次调度的开销为100ms,则在q=1时CPU的额外开销和有效开销之比为1:10,这是不容忽视的。 (8)最短周期优先 最短周期优先(SBF: shortest burst first)把当前就绪队列中的下一个CPU周期最短的那个进程调度运行。 (9)先来先服务 如果早就绪的进程排在就绪队列的前面,迟就绪的进程排在就绪队列的后面,那么先来先服务(FCFS: first come first service)总是把当前处于就绪队列之首的那个进程调度到运行状态。也就说,它只考虑进程进入就绪队列的先后,而不考虑它的下一个CPU周期的长短及其他因素。FCFS算法简单易行,但性能却不大好。 四、实验程序 #include “iostream.h” //define pcb typedef struct pcb { char name[10];//进程名 char state;//状态w(就绪)r(运行)f(结束)int id;//id号 int super;//优先级 int ntime;//需运行的时间 int rtime;//已运行的时间 struct pcb *next;}*pcb1; pcb1 s,w;//define two publiced linknode ,one is s(ready queue),one is w(blocked queue) //initialize two queues void init(pcb1 &r){ r=NULL;//both queues is the kind of head index } //print the information of the ready queue void print(){pcb1 p;cout<<“您现在查看的是就绪队列的信息:”;cout<<“进程号 ”<<“进程名 ”<<“优先级 ”<<“状态”<<“已运行时间 ”<<“需运行时间”< cout< id<<“ ”< name<<“ ”< super<<“ ”< state<<“ ”< rtime<<“ ”< ntime< //print the information of the blocked queue void print1(){pcb1 p;cout<<“您现在查看的是阻塞队列的信息”;cout<<“进程号 ”<<“进程名 ”<<“优先级 ”<<“状态 ”<<“已运行时间 ”<<“需运行时间”< id<<“ ”< name<<“ ”< super<<“ ”< state<<“ ”< rtime<<“ ”< ntime< //check the queue if empty int empty(pcb1 &r){ if(r==NULL)return 0;else return 1;} //check the first process of the ready queue if finshed int check(){ pcb1 p;p=s;if(p->rtime==p->ntime){ p->state='F';//if one process finshed then change ti's state cout<<“进程”< id<<“ 已经结束”< //sort process according to the super of the process and insert to the ready(blocked)queue void sort(pcb1 &r,pcb1 p){ pcb1 p1,p2;int in=0;if(r==NULL)//the queue is empty { r=p;} else { if(p->super>=r->super)//the super of the process which wait insert to the queue is highter than the super of the first process of the queue { p->next=r;r=p;} else { p1=r;p2=r->next;if(p2==NULL)//only one process in the queue { r->next=p;} else { while(in==0&&p2!=NULL)//insert to the middle of the queue { if(p->super>=p2->super){ p->next=p2;p1->next=p;in=1;} else { p1=p1->next;p2=p2->next;} } } if(in==0)//link to the last of ready queue p1->next=p;} } } //block one process and insert to block queue void block(){ if(empty(s)){ if(s->next==NULL){ sort(w,s);s=s->next;} else { pcb1 p1;p1=s;s=s->next;p1->next=NULL;sort(w,p1);} } else { cout<<“现在就绪队列已经为空,再没有进程需要阻塞”< //wake one process of block queue and insert to ready queue void wake(){ if(empty(w)){ pcb1 p1;p1=w;w=w->next;p1->next=NULL;sort(s,p1);} else { cout<<“阻塞队列已经为空,没有进程再需要唤醒”< //runing void runing(){ if(empty(s)){ pcb1 p;p=s;if(check())//check the first process of the queue if finished {//no s=s->next;p->rtime++;p->super--;p->next=NULL;sort(s,p);} else {//yes s=s->next;} } else { cout<<“就绪队列已经为空”< //creat process void input(){ pcb1 p2;p2=new pcb;cout<<“请输入 进程号、进程名、进程优先级、需要运行时间”;cin>>p2->id>>p2->name>>p2->super>>p2->ntime;p2->rtime=0;p2->state='W';p2->rtime=0;p2->next=NULL; sort(s,p2);} //main function void main(){ char ch;init(s);init(w);cout<<“*****************************进程调度模拟程序开始*******************************”< 程序运行界面如下: 五、实验总结 本次实验,我的任务是设计一个允许n个进程并发运行的进程管理模拟系统。该系统包括有简单的进程控制、同步与通讯机构,系统在运行过程中能显示各进程的状态及有关参数的变化情况,从而观察诸进程的运行过程及系统的管理过程,我是用C++写的,在我的电脑能够运行通过,虽不能尽善尽美,但也基本能实现老师的要求。 两个星期程序设计课程,虽然时间有点短,但我也收获不少,这次试验,加深了我对进程概念及进程管理的理解;比较熟悉进程管理中主要数据结构的设计及进程调度算法、进程控制机构、同步机构及通讯机构的实施。也让我认识到自己的不足,操作系统的有些知识,我知道的还不多,没有掌握好,还需要多多学学,不断提升自己的能力。 进程调度算法模拟 专业:XXXXX 学号:XXXXX 姓名:XXX 实验日期:20XX年XX月XX日 一、实验目的 通过对进程调度算法的模拟加深对进程概念和进程调度算法的理解。 二、实验要求 编写程序实现对5个进程的调度模拟,要求至少采用两种不同的调度算法分别进行模拟调度。 三、实验方法内容 1.算法设计思路 将每个进程抽象成一个控制块PCB,PCB用一个结构体描述。 构建一个进程调度类。将进程调度的各种算法分装在一个类中。类中存在三个容器,一个保存正在或未进入就绪队列的进程,一个保存就绪的进程,另一个保存已完成的进程。还有一个PCB实例。主要保存正在运行的进程。类中其他方法都是围绕这三个容器可以这个运行中的PCB展开。 主要用到的技术是STL中的vector以维护和保存进程容器、就绪容器、完成容器。 当程序启动时,用户可以选择不同的调度算法。然后用户从控制台输入各个进程的信息,这些信息保存到进程容器中。进程信息输入完毕后,就开始了进程调度,每调度一次判断就绪队列是否为空,若为空则系统时间加一个时间片。判断进程容器中是否有新的进程可以加入就绪队列。2.算法流程图 主程序的框架: 开始void FCFS();//先来先服务void SJF();//最短进程优先调度void RR();//简单时间片轮转void PD();//最高优先数优先void PCBInput();//输入进程信息选择调度算法输入进程信息将输入容器中以满足进入条件的进程调入就绪队列void ProcessQueueProcess();//查看当前时间下,有无进程加入。若有则把该进程调入就绪队列按照选择的算法开始选择就绪队列的进程开始执行void ProcessSelect();//若当前就绪队列不为空则根据选择的调度算法开始调度,否则,系统时间加一个时间片.以等待新的进程到判断就绪容器和输入容器是否为空!processScheduler.m_WaitQueue.empty()||!processScheduler.m_ProcessQueue.empt()Y打印各进程信息进行统计计算周转时间等结束void PCBDisplay();//打印当前状况下。就绪队列、完成队列、运行中的进程信息void SchedulerStatistics();//调度统计,计算周转时间等进程调度过程: 开始为空判断就绪队列是否为空if(m_WaitQueue.empty())非空让系统等待一个时间片TimePast()根据设定的调度算法从就绪队列中调入一个进程并执行(此时进程从就绪队列中删除,赋值到表示运行中的成员变量中)void FCFS();//先来先服务void SJF();//最短进程优先调度void RR();//简单时间片轮转void PD();//最高优先数优先进程运行一个时间片N是否达到该进程停止运行的条件Y选入的进程状态是否为“完成”如进程已完成,或者分得的时间片个数已到ProcessRun()Yvector m_WaitQueue;//进程就绪队列进程未完成,将进程优先数减一,并放回到就绪队列中设置进程完成时间,将该进程放入完成队列vector m_FinishQueue;//完成队列结束 3.算法中用到的数据结构 struct fcfs{ //先来先服务算法从这里开始 char name[10]; float arrivetime; float servicetime; float starttime; float finishtime; float zztime; float dqzztime; }; //定义一个结构体,里面包含的有一个进程相关的信息 4.主要的常量变量 vector m_ProcessQueue;//进程输入队列 vector m_WaitQueue;//进程就绪队列 vector m_FinishQueue;//完成队列 vector ::iterator m_iter;//迭代器 PCB m_runProcess;//运行中的进程 int m_ProcessCount;//进程数 float m_RunTime;//运行时间 int m_tagIsRun;//是否在运行标志。表示正在运行,表示没有 float m_TimeSlice;//时间片大小 int m_TimeSliceCount;//指时间片轮转中一次分到的时间片个数 char m_SchedulerAlgorithm;//调度算法 5.主要模块 void PCBInput();//输入进程信息 void PCBSort();//对进程控制块按照优先级排序(采用冒泡排序)void ProcessSelect();//若当前就绪队列不为空则根据选择的调度算法开始调度。否则,系统时间void PCBDisplay();//打印当前状况下。就绪队列、完成队列、运行中的进程信息 void ProcessRun();//进程运行一次。运行时间加个时间片。并判断进程是否达到完成条件。若是则void ProcessQueueProcess();//查看当前时间下,有无进程加入。若有则把该进程调入就绪队列 void ProcessDispatch();//进程分派,进程执行完成后决定进程该进入哪个队列(就绪、完成)void TimePast(){ m_RunTime +=m_TimeSlice;ProcessQueueProcess();}//当前系统时间加个时间void SchedulerStatistics();//调度统计,计算周转时间等 void FCFS();//先来先服务 void SJF();//最短进程优先调度 void RR();//简单时间片轮转 void PD();//最高优先数优先 加.以等待新的进程到来 ProcessStatus='f'.否则为'w';片,并检查是否有新的进程加入 四、实验代码 #include using namespace std; struct fcfs{ //先来先服务算法从这里开始 char name[10]; float arrivetime; float servicetime; float starttime; float finishtime; float zztime; float dqzztime; }; //定义一个结构体,里面包含的有一个进程相关的信息 fcfs a[100]; void input(fcfs *p,int N) { int i; cout< printf(“ 请您输入进程的名字 到达时间 服务时间:(例如: a 0 100)nn”); for(i=0;i<=N-1;i++) { printf(“ 请您输入进程%d的信息:t”,i+1); scanf(“ttt%s%f%f”,&p[i].name,&p[i].arrivetime,&p[i].servicetime); } } void Print(fcfs *p,float arrivetime,float servicetime,float starttime,float finishtime,float zztime,float dqzztime,int N) { int k; printf(“nn调用先来先服务算法以后进程运行的顺序是: ”); printf(“%s”,p[0].name); for(k=1;k { printf(“-->%s”,p[k].name); } cout< printf(“n 具体进程调度信息:n”); printf(“t进程名 到达时间 服务时间 开始时间 结束时间 周转时间 带权周转时间n”); for(k=0;k<=N-1;k++) { printf(“t%st%-.2ft %-.2ft %-.2ft %-.2ft %-.2ft %-.2fn”,p[k].name,p[k].arrivetime,p[k].servicetime,p[k].starttime,p[k].finishtime,p[k].zztime,p[k].dqzztime); } getchar(); //此处必须要有这个函数,否则就看不到显示器上面的输出,可以看到的结果只是一闪而过的一个框剪 } void sort(fcfs *p,int N)//排序 { for(int i=0;i<=N-1;i++) for(int j=0;j<=i;j++) if(p[i].arrivetime { fcfs temp; temp=p[i]; p[i]=p[j]; p[j]=temp; } } void deal(fcfs *p, float arrivetime,float servicetime,float starttime,float finishtime,float &zztime,float &dqzztime,int N) //运行阶段 { int k; for(k=0;k<=N-1;k++) { if(k==0) { p[k].starttime=p[k].arrivetime; p[k].finishtime=p[k].arrivetime+p[k].servicetime;} else { p[k].starttime=p[k-1].finishtime; p[k].finishtime=p[k-1].finishtime+p[k].servicetime;} } for(k=0;k<=N-1;k++) { p[k].zztime=p[k].finishtime-p[k].arrivetime; p[k].dqzztime=p[k].zztime/p[k].servicetime; } } void FCFS(fcfs *p,int N) { float arrivetime=0,servicetime=0,starttime=0,finishtime=0,zztime=0,dqzztime=0; sort(p,N); deal(p,arrivetime,servicetime,starttime,finishtime,zztime,dqzztime,N); Print(p,arrivetime,servicetime,starttime,finishtime,zztime,dqzztime,N); getchar(); } //先来先服务算法到此结束 struct sjf{//最短进程优先调度算法从这里开始 char name[10];float arrivetime;//到达时间 float servicetime;//运行时间 float starttime; //开始时间 float finishtime; //完成时间 };sjf a1[100]; void input(sjf *p,int N1)//进程信息输入 { int i;cout< printf(“ 请您输入进程的名字 到达时间 服务时间:(例如: a 0 100)n”); for(i=0;i<=N1-1;i++){ printf(“ 请您输入进程%d的信息:t”,i+1); scanf(“ttt%s%f%f”,&p[i].name,&p[i].arrivetime,&p[i].servicetime);} } void Print(sjf *p,float arrivetime,float servicetime,float starttime,float finishtime,int N1)//最终结果输出 { int k; printf(“nt调用最短进程优先调度算法以后进程的调度顺序为:”); printf(“%s”,p[0].name); for(k=1;k {printf(“-->%s”,p[k].name);} cout< printf(“n给个进程具体调度信息如下:n”); printf(“nt进程名t到达时间t运行时间t开始时间t完成时间n”); for(k=0;k<=N1-1;k++) { printf(“ t%st %-.2ftt %-.2ftt %-.2ftt %-.2ftn”,p[k].name,p[k].arrivetime,p[k].servicetime,p[k].starttime,p[k].finishtime); } getchar(); } void sort(sjf *p,int N1)//排序 { for(int i=0;i<=N1-1;i++) for(int j=0;j<=i;j++) if(p[i].arrivetime { sjf temp; temp=p[i]; p[i]=p[j]; p[j]=temp; } } void deal(sjf *p, float arrivetime,float servicetime,float starttime,float finishtime,int N1)//运行阶段 { int k; for(k=0;k<=N1-1;k++) { if(k==0) { p[k].starttime=p[k].arrivetime; p[k].finishtime=p[k].arrivetime+float(p[k].servicetime)/60;} else { p[k].starttime=p[k-1].finishtime; p[k].finishtime=p[k-1].finishtime+float(p[k].servicetime)/60;} } } void sjff(sjf *p,int N1){ float arrivetime=0,servicetime=0,starttime=0,finishtime=0; sort(p,N1); for(int m=0;m {if(m==0) p[m].finishtime=p[m].arrivetime+float(p[m].servicetime)/60; else p[m].finishtime=p[m-1].finishtime+float(p[m].servicetime)/60; int i=0; for(int n=m+1;n<=N1-1;n++) { if(p[n].arrivetime<=p[m].finishtime) i++; } float min=p[m+1].servicetime; int next=m+1; for(int k=m+1;k { if(p[k+1].servicetime {min=p[k+1].servicetime; next=k+1;} } sjf temp; temp=p[m+1]; p[m+1]=p[next]; p[next]=temp; } deal(p,arrivetime,servicetime,starttime,finishtime,N1); Print(p,arrivetime,servicetime,starttime,finishtime,N1); getchar();}//最短进程优先调度算法到这里结束 char menu()//用来输出相关信息的函数 { char cse1; while(1) { system(“cls”); fflush(stdin); cout< cout< cout<<“t”<<“|| <<<<<<<<<<<<欢<<<<<<<<<<< >>>>>>>>>>>>迎>>>>>>>>>>> ||”< cout<<“t”<<“|| ||”< cout<<“t”<<“||”<<“t 进程调度算法模拟”<<“tt”<<“||”< cout<<“t”<<“|| ||”< cout<<“t”<<“||”<<“tt 1.先来先服务调度算法 ”<<“tt”<<“||”< cout<<“t”<<“|| ||”< cout<<“t”<<“||”<<“tt 2.最短进程优先调度算法”<<“tt”<<“||”< cout<<“t”<<“|| ||”< cout<<“t”<<“|| <<<<<<<<<<<<<<<<<<<<<<<<<您>>>>>>>>>>>>>>>>>>>>>>>>> ||”< cout< cout< cout<<“tt 请输入您的选择(1/2):”; cse1=getchar(); if(cse1<'1'||cse1>'2') cout<<“你的输入有错!”< else break; } return cse1;} int main(int argc, char *argv[]){ while(1) { switch(menu()) { case '1': int N; cout< cout< printf(“tt<<---!!@@@先来先服务调度算法@@@!!--->>n”); cout< printf(“输入进程数目:”); scanf(“%d”,&N); input(a,N); FCFS(a,N); case '2': int N1; cout< cout< printf(“tt<<---!!@@@最短进程优先调度算法@@@!!--->>n”); cout< printf(“输入进程数目: ”); scanf(“%d”,&N1); input(a1,N1); sjf *b=a1; sjf *c=a1; sjff(b,N1); getchar(); } } system(“PAUSE”); return EXIT_SUCCESS;} 五、实验结果 1.执行结果 2.结果分析 先来先服务调度算法就是根据进程达到的时间为依据,哪一个进程先来那么该进程就会先执行;最短进程优先调度算法则是以每个进程执行所需时间长短为依据,某一个进程执行所需花的时间要短些那么该进程就先执行。以上就是本次进程调度实验的依据。 六、实验总结 通过本次实验了解到算法很重要,又更加明白算法本身可以节约时间,而且不同的函数之间在调用的时候要注意很多的问题。 自己写的:(功能感觉达到我要目的,感觉没有把操作量层次写出来,大家参考学习) #include #define max 80//假设汽车的最大容量为80 int num=0;//初始还没有启动客车上的人数为0 int sc=0;//上车的人数 int xc=0;//下车的人数 int k;//上车-下车,停站上下车后后人数变化,即净上车人数; //k=sc-xc;//净上车人数/ int dr();int cd(); int check(){ if(sc==-1||xc==-1) {cout<<“旅途愉快,汽车到达总站,再见”< exit(1); } return 0;} int dr()//司机driver的信号量 { //num=num+k;if(num<=max){ cout<<“汽车关门准备开车!”< } cout<<“司机开车!”< int c,sji;//超载人数/实际上车人数 k=sc-xc;//净上车人数/ num=num+k;//上下乘客后车上人数 if(num<=max) { cout<<“现在车上人数为:”< cout<<“坐好扶稳!”< cout<<“===================================”< cout<<“===================================”< cout<<“===================================”< } if(num>max) { c=num-max;sji=sc-c; cout<<“超载”< num=max;cout<<“已下”< } return 0;} /*主函数*/ int main(){ cout<<“=========欢迎使用司机与售票员信息量同步公交车系统=============”< cout<<“请输入上车人数!”< while(num>max) { cout<<“警告:输入上车人数后,人数已经超过限载人数,输入错误请重新输入”< cout<<“重新输入上车人数为:”< cin>>sc; num=sc; check(); } cout<<“第一次启动上车人数为”< check(); while(sc!=-1||xc!=-1) { cout<<“汽车行驶中.......!”< cout<<“请输入下车人数!”< check(); while(xc>num) { cout<<“输入下车人数超过车上人数,输入错误,请重新输入”< cout<<“重新输入下车人数为:”< cin>>xc; check(); } cout<<“请输入上车人数!”< //cout<<“上车人数为”< check();if(num>max) { cout<<“警告:输入上车人数后车里人数已经超过车的限载人数”< } //num=num+sc-xc;//上下乘客后车上人数 //cout<<“车上人数总共为”< dr(); } return 0;} 网上下载的:好像作者上传缺少311.h头文件 #include using namespace std; HANDLE Semaphore_dirver;//司机信号量的句柄 HANDLE Semaphore_condutor;//售票员信号量的句柄 int num_station=0; //司机进程 void Thread_Driver(){ while(true) { if(WaitForSingleObject(Semaphore_dirver,INFINITE)==WAIT_OBJECT_0)//相当与P(Semaphore_dirver)操作 {cout<<“汽车行驶中,请您扶稳坐好!”< Sleep(5000);//行车时间 } ReleaseSemaphore(Semaphore_condutor, 1, NULL);//相当与V(Semaphore_condutor)操作提醒售票员可以售票了 } } //售票员进程 void Thread_Conductor(){ while(true) { if(WaitForSingleObject(Semaphore_condutor,INFINITE)==WAIT_OBJECT_0)//判断车是否已停 if(!num_station) //第一站 {cout<<“没人下车”< num_station++; NUM_ON=random_num(NUM_MAX); Sleep(3000);//乘客上车时间 cout< NUM_CURRENT=NUM_ON+NUM_INITIAL;//起始站上车的人数 } else { cout<<“车辆进站,请您注意安全!开门请当心,下车请走好!下车后请勿从车前绕行!”< Sleep(3000);//乘客下车时间 NUM_DOWN=random_num(NUM_CURRENT);//下车人数 cout< NUM_CURRENT=NUM_CURRENT-NUM_DOWN;//下车后的人数 if(NUM_CURRENT { cout<<“311路无人售票车开往南门,请您按顺序投币上车!”< cout<<“上车的乘客请从后门移动,准备下车!”< Sleep(3000);//乘客上车时间 NUM_ON=random_num(NUM_MAX-NUM_CURRENT);//上车人数 cout< NUM_CURRENT=NUM_CURRENT+NUM_ON;//上车后人数 cout<<“车上有”< } else cout<<“座位已满,请等下一辆!”< } ReleaseSemaphore(Semaphore_dirver,1,NULL);//让司机开车的信号 } } /*主函数*/ int main(){ HANDLE thread[2];//定义两个线程句柄 Semaphore_dirver = CreateSemaphore(NULL,1, 1, NULL);//司机信号量 Semaphore_condutor = CreateSemaphore(NULL, 0, 1, NULL);//售票员信号量 DWORD threadID[2]; thread[0]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Thread_Driver),NULL,0,&threadID[0]);//司机进程 thread[1]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Thread_Conductor),NULL,0,&threadID[1]);//售票员进程 WaitForMultipleObjects(1, thread, TRUE, INFINITE);//等待两个线程结束 return 0;} 实 验 二 经 典 的 生 产 者 — 消 费 者 问 题 一、目的 实现对经典的生产者—消费者问题的模拟,以便更好的理解经典进程同步问题。 二、实验内容及要求 编制生产者—消费者算法,模拟一个生产者、一个消费者,共享一个缓冲池的情形。 1、实现对经典的生产者—消费者问题的模拟,以便更好的理解此经典进程同步问题。生产者- 消费者问题是典型的 PV 操作问题,假设系统中有一个比较大的缓冲池,生产者的任务是只要缓冲池未满就可以将生产出的产品放入其中,而消费者的任务是只要缓冲池未空就可以从缓冲池中拿走产 品。缓冲池被占用时,任何进程都不能访问。 2、每一个生产者都要把自己生产的产品放入缓冲池,每个消费者从缓冲池中取走产品消费。在这种情况下,生产者消费者进程同步,因为只有通过互通消息才知道是否能存入产品或者取走产品。他们之间也存在互斥,即生产者消费者必须互斥访问缓冲池,即不能有两个以上的进程同时进行。 三、生产者和消费者原理分析 在同一个进程地址空间内执行两个线程。 生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。 消费者线程从缓冲区中获得物品,然后释放缓冲区。 当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放一个空缓冲区。 当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻挡,直到新的物品被生产出来。 四、生产者与消费者功能描述: 生产者功能描述:在同一个进程地址空间内执行两个线程。生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。 消费者功能描述:消费者线程从缓冲区获得物品,然后释放缓冲区,当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来。 五、实验环境 操作系统环境: Windows 系统。编程语言: C#。 六、生产者与消费者的思路和设计 1、程序流程图 (1)生产者 开 始 生产产品 Wait empty ≤ 0 Y N Wait 缓冲区内已满,已无 可用缓冲区 N Mutex=1 Y 缓冲区正被其他 程占用 进 存入缓冲区 empty = empty-1 Signal Signal(full)结 束 (2)消费者 开 始 Wait(full)消费请求 full ≤ 0 Y N Wait 缓冲区内产品已空,不能进行消费 N Mutex=1 Y 消 费 缓冲区正被其他 程占用 进 full = full-1 Signal Signal 结 束 2、主要程序代码 // 初始化变量 private void Form1_Load(object sender, EventArgs e){ mutex = 1;// 互斥信号量 full = 0;// 缓冲池中满缓冲区的数量 empty = 5;// 缓冲池中空缓冲区的数量 count1 = 0;// 生产的产品数目 i = 0;= “1”;= “0”;= “5”;} // 消费者从缓冲区中消费一个产品 private void consumer_Click(object sender, EventArgs e){ if(full > 0){ // 消费者已进入互斥临界区 if(mutex == 1)// 申请进入临界区 { mutex = 0;// 消费者已进入互斥临界区 = “0”;= true;// 启动消费者消费缓冲区产品 } else {(“ 缓冲区被占用,请等待。。 ”, “ 信息提 ”;} } else {(“ 缓冲区为空,不能消费!”, “ 信息提示 ”,;} } // 生产者向缓冲区中存入一个产品 private void producer_Click(object sender, EventArgs e){ count1 = count1 + if(empty > 0){ 1; // // 生产一个产品 有缓冲区可放产品 if(mutex == 1){ // 申请进入临界区 mutex = 0; // 生产者已进入临界区 = “0”; ();// 启动生产者将产品放入缓冲区 } else { // 不能进入临界区 count1 = count1-1;(“ 缓冲区被占用,请等待。。 ”, “ 信息提示 ”,;} } else {(“ 缓冲区已满!”, “ 信息提示 ”,;// 无缓冲区可放产品 count1 = count1-1;} } // 生产者 private void timer1_Tick_1(object sender, EventArgs e){ if(bool1){ switch(count1){ case 1: = true;break;case 2: = true;break;case 3: = true;break;case 4: = true;break;case 5: = true;break;} = “ 生产者进程占用缓冲区,请等待。。 ”;bool1 = false;} else { switch(count1){ case 1: = false;break;case 2: = false;break;case 3: = false;break;case 4: = false;break;case 5: = false;break;} = “ 生产者进程占用缓冲区,请等待。。 ”;bool1 = true;} i = i + 1;if(i == 5) { // 循环缓冲区,首尾相接 i = 0;= false;mutex = 1;= “1”;switch(count1){ case 1: = true;break;case 2: = true;break;case 3: = true;break;case 4: = true;break;case 5: = true;break;} full = full + 1;=();empty = empty-1;=();= “ 生产结束!”;} } // 消费者 private void timer_consumer_Tick(object sender, EventArgs e){ if(bool1){ switch(count1){ case 1: = true;break;case 2: = true;break;case 3: = true;break;case 4: = true;break;case 5: = true;break;} = “ 消费者进程占用缓冲区,请等待。。 ”;bool1 =false;} else{ switch(count1){ case 1: = false;break;case 2: = false;break;case 3: = false;break;case 4: = false;break;case 5: = false;break;} = “ 消费者进程占用缓冲区,请等待。。 ”;bool1= true;} i = i + 1;if(i==5){ i = 0;= false;mutex = 1;= “1”;switch(count1){ case 1: = false;break;case 2: = false;break;case 3: = false;break;case 4: = false;break;case 5: = false;break;} count1 = count1-1;full = full-1;=();empty = empty+1;=();=“ 消费结束! ”;} 3、运行界面和运行结果 一般情况下,点一次生产者按纽,mutex 由 1 变为 0,缓冲区呈现闪烁状态(表示正在存储),此时不可以再进行缓冲区操作,否则将显示“生产者进程正在占用缓冲区,请等待”。闪烁约秒后,mutex 由 0 变为 1,闪烁停止,表示存储过程结束;点一次消费者按纽,mutex 由 1 变为 0,缓冲区呈现闪烁状态(表示正在消费),此时不可以再进行缓冲区操作,否则将显示“消费者进程正在占用缓 冲区,请等待”。闪烁约秒后,mutex 由 0 变为 1,闪烁停止,表示消费过程结束。缓冲池满后,若再点生产者按纽,会给出信息提示: “缓冲区已满!”。 缓冲池空后,若再点消费者按纽,也会给出信息提示: “缓冲区为空,不能消费!”。 在存储状态或消费状态(闪烁状态),无论是点生产者按纽还是消费者按纽都会给出“缓冲区被占用,请等待。”信息提示。 七、心得体会 本次实验是关于生产者与消费者之间互斥和同步的问题。问题的是指是 P、V 操作,实验设一个共享缓冲区,生产者和消费者互斥的使用,当一个线程使用缓冲区的时候,另一个让其等待直到前一 个线程释放缓冲区为止。 生产者与消费者是一个与现实有关的经验问题,通过此原理举一反三可以解决其他类似的问题。 通过本实验设计,我们对操作系统的 P、V 进一步的认识,深入的了解 P、V 操作的实质和其重 要性。课本的理论知识进一步阐述了现实中的实际问题。 实验中,我们小组分工合作,共同学习,虽然在实验中遇到了一些问题,但在老师和同学的细心指导和热心帮助下解决了。同时,了解到团队精神的重要性,也为以后的学习和工作打下了坚实的基础,同时积累了宝贵的经验。第二篇:操作系统进程管理系统设计实验报告
第三篇:操作系统进程调度算法模拟实验报告
第四篇:司机与售票员原创 完整代码
第五篇:操作系统实验报告经典生产者—消费者问题