第一篇:操作系统课程设计报告——读者写者问题
操作系统课程设计
课
题:读者写者问题 姓
名:赫前进 班
级:1020552 学
号102055211 指导教师:叶瑶 提交时间:2012/12/30
(一)实验目的
1.进一步理解 “临界资源” 的概念;
2.把握在多个进程并发执行过程中对临界资源访问时的必要约束条件; 3.理解操作系统原理中 “互斥” 和 “同步” 的涵义。
(二)实验内容
利用程序设计语言编程,模拟并发执行进程的同步与互斥(要求:进程数目不少于 3 个)。
(三)、程序分析
读者写者问题的定义如下:有一个许多进程共享的数据区,这个数据区可以是一个文件或者主存的一块空间;有一些只读取这个数据区的进程(Reader)和一些只往数据区写数据的进程(Writer),此外还需要满足以下条件:(1)任意多个读进程可以同时读这个文件;(2)一次只有一个写进程可以往文件中写;
(3)如果一个写进程正在进行操作,禁止任何读进程度文件。
实验要求用信号量来实现读者写者问题的调度算法。实验提供了signal类,该类通过P()、V()两个方法实现了P、V原语的功能。实验的任务是修改Creat_Writer()添加写者进程,Creat_Reader()创建读者进程。Reader_goon()读者进程运行函数。读优先:要求指一个读者试图进行读操作时,如果这时正有其他读者在进行操作,他可直接开始读操作,而不需要等待。
读者优先的附加限制:如果一个读者申请进行读操作时已有另一读者正在进行读操作,则该读者可直接开始读操作。
写优先:一个读者试图进行读操作时,如果有其他写者在等待进行写操作或正在进行写操作,他要等待该写者完成写操作后才开始读操作。
写者优先的附加限制:如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该读者必须等到没有写者处于等待状态后才能开始读操作。
在Windows 7 环境下,创建一个控制台进程,此进程包含 n 个线程。用这 n 个线程来表示 n 个读者或写者。每个线程按相应测试数据文件(格式见下)的要求进行读写操作。用信号量机制分别实现读者优先和写者优先的读者/写者问题。运行结果显示要求:要求在每个线程创建、发出读写操作申请、开始读写操作和结束读写操作时分别显示一行提示信息,以确定所有处理都遵守相应的读写操作限制。
测试数据文件包括 n 行测试数据,分别描述创建的 n 个线程是读者还是写者,以及读写操作的开始时间和持续时间。每行测试数据包括4个字段,各个字段间用空格分隔。
Ø
第一个字段为一个正整数,表示线程序号
Ø
第二个字段表示相应线程角色,R 表示读者,W 表示写者
Ø
第三个字段为一个正数,表示读/写操作的开始时间:线程创建后,延迟相应时间(单位为秒)后发出对共享资源的读/写请求
Ø
第四个字段为一正数,表示读/写操作的持续时间:线程读写请求成功后,开始对共享资源的读/写操作,该操作持续相应时间后结束,并释放共享资源 例如: 1 R 3 5 2 W 4 5 3 R 5 2 4 R 6 5 5 W 5.1 3 读者写者问题是操作系统中经典的互斥问题:一块数据被多个读者和写者的访问,需要考虑读写互斥、写写互斥(可以同时由多个读者读取)。具体的又可以分为读者优先和写者优先两类。读者优先算法:
当新的读者到来的时候,若当前正有读者在进行读操作,则该读者无需等待前面的写操作完成,直接进行读操作。设置两个互斥信号量:
rwmutex 用于写者与其他读者/写者互斥的访问共享数据 rmutex 用于读者互斥的访问读者计数器readcount var rwmutex, rmutex : semaphore := 1,1 ; int readcount = 0;cobegin
readeri begin // i=1,2,„.P(rmutex);
Readcount++;
If(readcount == 1)P(rwmutex);
V(rmutex);
读数据;
P(rmutex);
Readcount--;
If(readcount == 0)V(rwmutex);
V(rmutex);
End
Writerj begin // j = 1,2,„.P(rwmutex);
写更新;
V(rwmutex);
End Coend 写者优先: 条件:
1)多个读者可以同时进行读
2)写者必须互斥(只允许一个写者写,也不能读者写者同时进行)
3)写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)设置三个互斥信号量:
rwmutex 用于写者与其他读者/写者互斥的访问共享数据 rmutex 用于读者互斥的访问读者计数器readcount nrmutex 用于写者等待已进入读者退出,所有读者退出前互斥写操作 var rwmutex, rmutex,nrmutex : semaphore := 1,1,1 ; int readcount = 0;cobegin
readeri begin // i=1,2,„.P(rwmutex);
P(rmutex);
Readcount++;
If(readcount == 1)P(nrmutex);//有读者进入,互斥写操作
V(rmutex);
V(rwmutex);// 及时释放读写互斥信号量,允许其它读、写进程申请资源
读数据;
P(rmutex);
Readcount--;
If(readcount == 0)V(nrmutex);//所有读者退出,允许写更新
V(rmutex);
End
Writerj begin // j = 1,2,„.P(rwmutex);// 互斥后续其它读者、写者
P(nrmutex);//如有读者正在读,等待所有读者读完
写更新;
V(nrmutex);//允许后续新的第一个读者进入后互斥写操作
V(rwmutex);//允许后续新读者及其它写者
End Coend //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /*---------函数声明---------*/ void Creat_Writer();
//添加一个写者 void Del_Writer();
//删除一个写者 void Creat_Reader();
//添加一个读者
void Reader_goon();
//读者进程运行函数 void R_Wakeup();
//唤醒等待读者 void Del_Reader();
//删除一个读者 void Show();
//显示运行状态
/*===============
class
signal
===============*/ class signal //信号量对象.{ private: int value;int queue;
//用int型数据模拟等待队列.public: signal();signal(int n);int P();//检查临界资源 int V();//释放临界资源 int Get_Value();int Get_Queue();};//////////////////////////////////////////////////////////////////// #include
#include
struct ThreadInfo
{
int num;
char type;
double start;
double time;
}thread_info[MaxThread];
HANDLE hX;
HANDLE hWsem;
HANDLE thread[MaxThread];int readcount;double totaltime;
void WRITEUNIT(int iProcess){
printf(“Thread %d begins to write.n”,iProcess);
Sleep((DWORD)(thread_info[iProcess-1].time*1000));
printf(“End of thread %d for writing.n”,iProcess);}
void READUNIT(int iProcess){
printf(“Thread %d begins to read.n”,iProcess);
Sleep((DWORD)(thread_info[iProcess-1].time*1000));
printf(“End of thread %d for reading.n”,iProcess);}
DWORD
WINAPI
reader(LPVOID
lpVoid){
int iProcess
=
*(int*)lpVoid;
Sleep((DWORD)(thread_info[iProcess-1].start*1000));
DWORD wait_for=WaitForSingleObject(hX,INFINITE);
printf(“Thread %d requres reading.n”,iProcess);
readcount++;
if(readcount==1)WaitForSingleObject(hWsem,INFINITE);
ReleaseMutex(hX);
READUNIT(iProcess);
wait_for=WaitForSingleObject(hX,INFINITE);
readcount--;
if(readcount==0)
ReleaseSemaphore(hWsem,1,0);
ReleaseMutex(hX);
return iProcess;}
DWORD
WINAPI
writer(LPVOID
lpVoid){
int iProcess
=
*(int*)lpVoid;
Sleep((DWORD)(thread_info[iProcess-1].start*1000));
printf(“Thread %d requres writing.n”,iProcess);
DWORD wait_for=WaitForSingleObject(hWsem,INFINITE);
WRITEUNIT(iProcess);
ReleaseSemaphore(hWsem,1,0);
return iProcess;}
int main(){
int threadNum;
int threadcount;
ifstream file;
hX=CreateMutex(NULL, FALSE, NULL);
hWsem=CreateSemaphore(NULL,1,1,NULL);
//???
readcount=0;
threadcount=0;
totaltime=0;
file.open(“thread.dat”,ios::in);
if(file==0)
{
printf(“File Open Error.n”);
return 0;
}
while(file>>threadNum)
{
thread_info[threadNum-1].num=threadNum;
file>>thread_info[threadNum-1].type;
file>>thread_info[threadNum-1].start;
file>>thread_info[threadNum-1].time;
totaltime+=thread_info[threadNum-1].time;
switch(thread_info[threadNum-1].type)
{
case 'W':
printf(“Creating Thread %d writing.n”,thread_info[threadNum-1].num);
thread[threadNum-1]
=
CreateThread(NULL, &thread_info[threadNum-1].num,0,0);
break;
case 'R':
printf(“Creating Thread %d reading.n”,thread_info[threadNum-1].num);
thread[threadNum-1]
=
CreateThread(NULL, &thread_info[threadNum-1].num,0,0);
break;
}
threadcount++;
}
file.close();
Sleep((DWORD)(totaltime*1000));
return 1;} ////////////////////////////////////////////////////////////////////////////////// semaphore fmutex = 1 , rdcntmutex = 1;// fmutex--> access to file;rdcntmutex--> access to readcount int readcount = 0;void reader(){
while(1)
for 0,writer, for 0,reader,{
P(rdcntmutex);
if(readcount==0)
P(fmutex);
readcount = readcount + 1;
V(rdcntmutex);
// Do read operation
P(rdcntmutex);
readcount = readcount1;
if(readcount==0)
V(fmutex);
V(rdcntmutex);
} } void writer(){
while(1)
{
P(wtcntmutex);
if(writecount==0)
P(queue);
writecount = writecount + 1;
V(wtcntmutex);
P(fmutex);
// Do write operation
V(fmutex);
P(wtcntmutex);
writecount = writecount-1;
if(writecount==0)
V(queue);
V(wtcntmutex);
} }
第二篇:操作系统读者写者实验报告
目录
一 设计概述 ……………………………………………………2
二 设计目的与内容 ……………………………………………3
三 设计分析 ……………………………………………………4
四 程序实现 ……………………………………………………5
五 程序调试 ……………………………………………………6
六 结果分析和讨论 ……………………………………………6
七 心得体会 ……………………………………………………7
八 源代码 ………………………………………………………7
一 设计概述
所谓读者写者问题,是指保证一个writer进程必须与其他进程互斥地访问共享对象的同步问题。
读者写者问题可以这样的描述,有一群写者和一群读者,写者在写同一本书,读者也在读这本书,多个读者可以同时读这本书,但是,只能有一个写者在写书,并且,读者必写者优先,也就是说,读者和写者同时提出请求时,读者优先。当读者提出请求时需要有一个互斥操作,另外,需要有一个信号量S来当前是否可操作。
信号量机制是支持多道程序的并发操作系统设计中解决资源共享时进程间的同步与互斥的重要机制,而读者写者问题则是这一机制的一个经典范例。
与记录型信号量解决读者—写者问题不同,信号量机制它增加了一个限制,即最多允许RN个读者同时读。为此,又引入了一个信号量L,并赋予初值为RN,通过执行wait(L,1,1)操作,来控制读者的数目,每当有一个读者进入时,就要执行wait(L,1,1)操作,使L的值减1。当有RN个读者进入读后,L便减为0,第RN+1 个读者要进入读时,必然会因wait(L,1,1)操作失败而堵塞。对利用信号量来解决读者—写者问题的描述如下: Var RN integer;L,mx:semaphore: =RN,1; Begin Parbegin Reader :begin Repeat Swait(L,1,1);Swait(mx,1,0);.Perform reader operation;Ssignal(L,1);Until false;End Writer :begin Repeat Swait(mx ,1,1,l,RN,0);Perform writer operation;Ssignal(mx,1);Until false;End Parend End 其中,Swait(mx,1,0)语句起着开关作用,只要无Writer进程进入些,mx=1,reader进程就都可以进入读。但是要一旦有Writer进程进入写时,其MX=0,则任何reader进程就都无法进入读。Swait(mx ,1,1,l,RN,0)语句表示仅当既无Write进程在写(mx=1),又无reader进程在读(L=RN)时,writer进程才能进入临界区写。
本设计方案就是通过利用记录型信号量对读者写者问题的解决过程进行模拟演示,形象地阐述记录型信号量机制的工作原理。
二 设计目的与内容
一 实验目的
l.用信号量来实现读者写者问题。
2.理解和运用信号量、PV原语、进程间的同步互斥关系等基本知识。
二、二 实验内容
读者写者问题的定义如下:有一个许多进程共享的数据区,这个数据区可以是一个文件或者主存的一块空间;有一些只读取这个数据区的进程(Reader)和一些只往数据区写数据的进程(Writer),此外还需要满足以下条件:
(1)任意多个读进程可以同时读这个文件;(2)一次只有一个写进程可以往文件中写;
(3)如果一个写进程正在进行操作,禁止任何读进程度文件。我们需要分两种情况实现该问题:
读优先:要求指一个读者试图进行读操作时,如果这时正有其他读者在进行操作,他可直接开始读操作,而不需要等待。
写优先:一个读者试图进行读操作时,如果有其他写者在等待进行写操作或正在进行写操作,他要等待该写者完成写操作后才开始读操作。
三设计分析
在Windows 7 环境下,创建一个包含n 个线程的控制台进程。用这n 个线程来表示n个读者或写者。每个线程按相应测试数据文件的要求,进行读写操作。请用信号量机制分别实现读者优先和写者优先的读者-写者问题。
读者-写者问题的读写操作限制:
读者-写者的读写限制(包括读者优先和写者优先)1)写-写互斥,即不能有两个写者同时进行写操作
2)读-写互斥,即不能同时有一个读者在读,同时却有一个写者在写 3)读读允许,即可以有2个以上的读者同时读
将所有的读者和所有的写者分别放进两个等待队列中,当读允许时就让读者队列释放一个或多个读者,当写允许时,释放第一个写者操作。读者写者问题的定义如下:有一个许多进程共享的数据区,这个数据区可以是一个文件或者主存的一块空间;有一些只读取这个数据区的进程(Reader)和一些只往数据区写数据的进程(Writer),此外还需要满足以下条件:1)任意多个读进程可以同时读这个文件;2)一次只有一个写进程可以往文件中写;3)如果一个写进程正在进行操作,禁止任何读进程度文件。我们需要分两种情况实现该问题:
读优先:要求指一个读者试图进行读操作时,如果这时正有其他读者在进行操作,他可直接开始读操作,而不需要等待。写优先:一个读者试图进行读操作时,如果有其他写者在等待进行写操作或正在进行写操作,他要等待该写者完成写操作后才开始读操作。
四 程序实现
程序由两部分组成:
1。读者-写者模块:包括系统调用接口,读者-写者活动描述主程序。系统接口主要功能是通过管道向父进程发送系统调用命令,并读取父进程送来的返回值。
读者-写者活动程序根据临界资源的共享,互斥原则编制,具体见源程序。2。主控模块:主控模块实现系统初始化系统调用命令接收与解释执行,系统调用功能的实现(包括信号量机制),及读者-写者活动过程记录与显示。
初始化系统环境 建立通信管道
启动读者-写者进程 接收系统调用命令 解释执行
系统初始化模块 管道建立模块 进程启动模块 命令解释模块 Wait()Signal()Wakeup()Block()
五 程序调试
测试数据文件格式: 测试数据文件包括n 行测试数据,分别描述创建的n 个线程是读者还是写者,以及读写操作的开始时间和持续时间。每行测试数据包括四个字段,各字段间用空格分隔。第一字段为一个正整数,表示线程序号。第二字段表示相应线程角色,R 表示读者是,W 表示写者。第三字段为一个正数,表示读写操作的开始时间。线程创建后,延时相应时间(单位为秒)后发出对共享资源的读写申请。第四字段为一个正数,表示读写操作的持续时间。当线程读写申请成功后,开始对共享资源的读写操作,该操作持续相应时间后结束,并释放共享资源。
六 结果分析和讨论
在读者写者同时在队列中等待申请资时,读者优先调用资源。而且如果一个读者申请进行读操作时已有另一读者正在进行读操作,则该读者可直接开始读操作,即读读允许。
进程1是R操作,在时间3时进入队列,运行时间是5,在它进入时没有进程占用资源,它既占用资源;知道它释放资源,等候的进程有3,4,5;
进程2是W操作,在时间16时进入队列,运行时间是5,在它进入时进程4占用资源,它等待资源,当4释放时占用资源;
进程3是R操作,在时间5时进入队列,运行时间是2,在它进入时进程1占用资源,它等待资源,当进程1释放资源后,由于读者优先,进程3,5同时调 运资源;
进程4是R操作,在时间6时进入队列,运行时间是5,在它进入时进程1占用资源,它等待资源,当进程1释放资源后,由于读者优先,进程3,5占用资源,它依然等待,直到进程3,5都结束;
进程5是W操作,在时间4时进入队列,运行时间是3, 在它进入时进程1占用资源,它等待资源,当进程1释放资源后,由于读者优先,进程3,5同时调运资源;
七 心得体会
这一次课程设计,让我体会很深刻。读者-写者问题经典的线程同步问题的一个模型。经过读者写者问题的编写,我对同步机构应用有了深入的了解。懂得了运用信号量实现进程间的互斥。实现了不让共享资源同时修改。用信号量上的原语操作使临界段问题的解决比较简单明了了。读者写者问题的编写,花的时间很多,也学到很多东西。了解支持多道程序的并发操作系统设计中解决资源共享时进程间的同步与互斥的信号量机制。几天的试验,虽然难度有点大,但只要自己花时间去学习,还是会攻克困难的。
总之,每一次课程设计不仅是我们学习的好机会,而且是我们锻炼实际动手能力的平台,虽然有难度的东西总会让人很抵触,比如在课设过程中有很多郁闷的时候,一个小小的错误一不小心就花去了自己一上午的时间,所以在这个过程中能够磨练人的意志与耐心,最后感谢老师的指导与监督。
八 源代码
#include
第三篇:读者-写者 操作系统实验报告 计算机操作系统
4.1实验二:读者写者问题 4.1.1实验要求
在Windows 环境下,创建一个控制台进程,此进程包含n个线程。用这n个线程来表示n个读者或写者。每个线程按相应测试数据文件(后面有介绍)的要求进行读写操作。用信号量机制分别实现读者优先和写者优先的读者-写者问题。
读者-写者问题的读写操作限制(包括读者优先和写者优先): 1)写-写互斥,即不能有两个写者同时进行写操作。
2)读-写互斥,即不能同时有一个线程在读,而另一个线程在写。3)读-读允许,即可以有一个或多个读者在读。
读者优先的附加限制:如果一个读者申请进行读操作时已有另一个读者正在进行读操作,则该读者可直接开始读操作。
写者优先的附加限制:如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该读者必须等到没有写者处于等待状态才能开始读操作。
运行结果显示要求:要求在每个线程创建、发出读写操作申请、开始读写操作和结果读写操作时分别显示一行提示信息,以确定所有处理都遵守相应的读写操作限制。4.1.2测试数据文件格式
测试数据文件包括n行测试数据,分别描述创建的n个线程是读者还是写者,以及读写操作的开始时间和持续时间。每行测试数据包括四个字段,各个字段间用空格分隔。第一字段为一个正整数,表示线程序号。第二字段表示相应线程角色,R表示读者,W表示写者。第三字段为一个正数,表示读写操作的开始时间:线程创建后,延迟相应时间(单位为秒)后发出对共享资源的读写申请。第四字段为一个正数,表示读写操作的持续时间。当线程读写申请成功后,开始对共享资源的读写操作,该操作持续相应时间后结束,并释放共享资源。
下面是一个测试数据文件的例子: 1 R 3 5 2 W 4 5 3 R 5 2 4 R 6 5 5 W 5.1 3 注意:
在创建数据文件时,由于涉及到文件格式问题,最好在记事本中手工逐个键入数据,而不要拷贝粘贴数据,否则,本示例程序运行时可能会出现不可预知的错误。4.1.3实习分析
可以将所有读者和所有写者分别存于一个读者等待队列和一个写者等待队列中,每当读允许时,就从读者队列中释放一个或多个读者线程进行读操作;每当写允许时,就从写者队列中释放一个写者进行写操作。
1.读者优先
读者优先指的是除非有写者在写文件,否则读者不需要等待。所以可以用一个整型变量read-count记录当前的读者数目,用于确定 是否需要释放正在等待的写者线程(当read-count=0时,表明所有的读者读完,需要释放写者等待队列中的一个写者)。每一个读者开始读文件时,必须修改read-count变量。因此需要一个互斥对象mutex来实现对全局变量read-count修改时的互斥。
另外,为了实现写-写互斥,需要增加一个临界区对象write。当写者发出写请求时,必须申请临界区对象的所有权。通过这种方法,也可以实现读-写互斥,当read-count=1时(即第一个读者到来时),读者线程也必须申请临界区对象的所有权。当读者拥有临界区的所有权时,写者阻塞在临界区对象write上。当写者拥有临界区的所有权时,第一个读者判断完“read-count==1”后阻塞在write上,其余的读者由于等待对read-count的判断,阻塞在mutex上。
2.写者优先
写者优先与读者优先类似。不同之处在于一旦一个写者到来,它应该尽快对文件进行写操作,如果有一个写者在等待,则新到来的读者不允许进行读操作。为此应当添加一个整型变量write-count,用于记录正在等待的写者数目,当write-count=0时,才可以释放等待的读者线程队列。
为了对全局变量write-count实现互斥,必须增加一个互斥对象mutex3。
为了实现写者优先,应当添加一个临界区对象read,当有写者在写文件或等待时,读者必须阻塞在read上。
读者线程除了要对全局变量read-count实现操作上的互斥外,还必须有一个互斥对象对阻塞read这一过程实现互斥。这两个互斥对象分别命名为mutex1和mutex2。4.1.4相关API函数说明
1.CreateThread 函数功能:
该函数创建一个在调用进程的地址空间中执行的线程。函数原型:
HANDLE CreateThread(LPSECURITY-ATTRIBUTES lpThreadAttributes, DWORD dwStackSize,LPTHREAD-START-TOUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LLPDWORD lpThreadId);参数:
·lpThreadAttributes:指向一个SECURITY-ATTRIBUTES结构,该结构决定了返回的句柄是否可被子进程继承。若lpThreadAttributes为NULL,则句柄不能被继承。在Windows NT中该结构的lpSwcurityDescriptor成员定义了新进程的安全性描述符。若lpThreadAttributes为NULL,则线程获得一个默认的安全性描述符。·dwStackSize:定义原始堆栈提交时的大小(按字节计)。系统将该值舍入为最近的页。若该值为0,或小于默认时提交的大小,默认情况是使用与调用线程同样的大小。更多的信息,请看Thread Stack Size。
·lpStartAddress:指向一个LPTHREAD-START-TOUTINE类型的应用定义的函数,该线程执行此函数。该指针还表示远程进程中线程的起始地址。该函数必须存在于远程进程中。
·lpParameter:定义一个传递给该进程的32位值。
·dwCreationFlags:定义控制进程创建的附加标志。若定义CREATE-SUSPENDED标志,线程创建时处于挂起状态,并且直到ResumeThread函数调用时才能运行。若该值为0,则该线程在创建后立即执行。
·lpThreadId:指向一个32位值,它接收该线程的标识符。返回值:
若函数调用成功,返回值为新线程的句柄;若函数调用失败,返回值为NULL。备注:
新线程的句柄创建时设为THREAD-ALL-ACCESS访问权限。若未提供安全性描述符,则该句柄可被任何要求一个线程对象句柄的函数所使用。若提供了安全性描述符,则以后使用该句柄时,将在授权访问以前执行访问检查。若访问检查被拒绝访问,则请求进程不能使用该句柄获得对该线程的访问。线程从lpStartAddress参数定义的函数处开始执行。若该函数返回,系统将默认地认为以调用ExitThread函数的方法终止该线程。使用GetExitCodeThread 函数来获得线程的返回值。
线程创建时拥有THREAD-PRIORITY-NORMAL优先权。使用GetThreadPriority和SetThreadPriority函数可以获得和设置线程的优先权值。
一个线程终止时,该线程对象被设为发信号状态,以满足在该对象上等待的所有进程。一个线程对象始终存在于系统中,直到该线程终止,且它所有的句柄都已通过调用CloseHandle函数关闭。
2.ExitThread 函数功能:
该函数结束一个线程。函数原型:
VOID ExitThread(DWORD dwExitcode); 参数:
·dwExitcode:定义调用线程的退出代码。使用GetExitcodeThread函数来检测一个线程的退出代码。返回值:无。备注:
调用ExitThread函数,是结束一个线程的较好的方法。调用该函数后(或者直接地调用,或者从一个线程过程返回),当前线程的堆栈取消分配,线程终止。若调用该函数时,该线程为进程的最后一个线程,则该线程的进程也被终止。
线程对象的状态变为发信号状态,以释放所有正在等待该线程终止的其他线程。线程的终止状态从STILL-ACTIVATE变为dwExitcode参数的值。
线程结合时不必从操作系统中移去该线程对象。当线程的最后一个句柄关闭时,该线程对象被删除。
3.SLEEP 函数功能:
该函数对于指定的时间间隔挂起当前的执行线程。函数原型:
VOID SLEEP(DWORD dwMilliseconds); 参数:
·dwMilliseconds:定义挂起执行线程的时间,以毫秒(ms)为单位。取值为0时,该线程将如余下的时间片交给处于就绪状态的同一优先级的其他线程。若没有处于就绪状态的同一优先级的其他线程,则函数立即返回,该线程继续执行。若取值为INFINITE则造成无限延迟。返回值:
该函数没有返回值。备注:
一个线程可以在调用该函数时将睡眠时间设为0ms,以将剩余的时间片交出。4.CreateMutex 函数功能:
该函数创建有名或者无名的互斥对象。函数原型:
HANDLE CreateMutex(LPSECURITY-ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner,LPCTSTR lpName);参数:
·lpMutexAttributes:指向SECURITY-ATTRIBUTES结构的指针,该结构决定子进程是否能继承返回句柄。如果lpMutexAttributes为NULL,那么句柄不能被继承。
在Windows NT中该结构的lpSwcurityDescriptor成员指定新互斥对象的安全性描述符。若lpThreadAttributes为NULL,那么互斥对象获得默认的安全性描述符。·bInitialOwner:指定互斥对象的初始所属身份。如果该值为TRUE,并且调用者创建互斥对象,那么调用线程获得互斥对象所属身份。否则,调用线程不能获得互斥对象所属身份。判断调用者是否创建互斥对象请参阅返回值部分。
·lpName:指向以NULL结尾的字符串,该字符串指定了互斥对象名。该名字的长度大于MAX-PATH且可以包含除反斜线()路径分隔符以外的任何字符。名字是区分大小写的。
如果lpName与已存在的有名互斥对象相匹配,那么该函数要求用MUTEX-ALL-ACCESS权限访问已存在的对象。在这种情况下,由于参数bInitialOwner已被创建进程所设置,该参数被忽略。如果参数lpMutexAttributes不为NULL,它决定句柄是否解除继承,但是其安全描述符成员被忽略。
如果lpName为NULL,那么创建的互斥对象无名。
如果lpName与已存在的事件、信号量、可等待定时器、作业或者文件映射对象的名字相匹配,那么函数调用失败,并且GetLastError函数返回ERROR-INVALID-HANDLE,其原因是这些对象共享相同的名字空间。
返回值:
如果函数调用成功,返回值为互斥对象句柄;如果函数调用之前,有名互斥对象已存在,那么函数给已存在的对象返回一个句柄,并且函数GetLastError返回ERROR-ALREADY-EXISTS,否则,调用者创建互斥对象。
如果函数调用失败,则返回值为NULL。若想获得更多错误信息,请调用GetLastError函数。
备注:
由函数CreateMutex返回的句柄有MUTEX-ALL-ACCESS权限可以去访问新的互斥对象,并且可用在请求互斥对象句柄的任何函数中。
调用进程中的任何线程可以可以在调用等待函数时指定互斥对象句柄。当指定对象的状态为信号态时,返回单对象等待函数。当任何一个或者所有的互斥对象都为信号态时,返回多对象等待函数指令。等待函数返回后,等待的线程被释放,继续向下执行。
当一个互斥对象不被任何线程拥有时,处于信号态。创建该对象的线程可以使用bInitialOwner标志来请求立即获得对该互斥对象的所有权。否则,线程必须使用等待函数来请求所有权。当互斥对象处于信号态,等待的线程获得对该对象的所有权时,此互斥对象的状态被设置为非信号态,等待函数返回。任意时刻,仅有一个线程能拥有该互斥对象,线程可以使用ReleaseMutex函数来释放对这个互斥对象的所有权。
若线程已经拥有了一个互斥对象,那么它可以重复调用等待函数而不会发生阻塞,一般情况下,用户不会重复等待同一个互斥对象,这种机制防止了线程因等待它已经拥有的互斥对象而发生死锁。然而,线程必须为每一次等待调用一次ReleaseMutex函数来释放该互斥对象。
两个或多个互斥进程可以调用CreateMutex来创建同名的互斥对象,第一个进程实际创建互斥对象,以后的进程打开已存在的互斥对象的句柄。这使得多个进程可以得到同一个互斥对象的句柄,从而减轻了用户的负担,使用户不必判断创建进程是否为第一个启动的进程。使用这种技术时,应该把bInitialOwner标志设为FALSE;否则很难确定开始时哪一个进程拥有该互斥对象。
由于多进程能够拥有相同互斥对象的句柄,通过使用这个对象,可使多进程同步。以下为共享对象机制:
·如果CreateMutex中的lpMutexAttributes参数允许继承,由CreateProcess函数创建的子进程可以继承父进程的互斥对象句柄。
·一个进程可以在调用DuplicateHandle函数时指定互斥对象句柄来创建一个可以被其他进程使用的双重句柄。一个进程在调用OpenMutex或CreateMutex函数时能指定互斥对象名。
·使用CloseHandle函数关闭句柄,进程时系统自动关闭句柄。当最后一个句柄被关闭时,互斥对象被销毁。
5.ReleaseMutex 函数功能:
该函数放弃指定互斥对象的所有权。函数原型:
BOOL ReleaseMutex(HANDLE hMutex); 参数:
·hMutex:互斥对象句柄。为CreateMutex或OpenMutex函数的返回值。返回值:
如果函数调用成功,那么返回值是非零值;如果函数调用失败,那么返回值是零值。若想获得更多错误信息,请调用GetLastError函数。
备注:
如果调用线程不拥有互斥对象,ReleaseMutex函数失败。一个线程通过调用等待函数拥有互斥对象。创建该互斥对象的线程也拥有互斥对象,而不需要调用等待函数。当互斥对象的所有者线程不再需要互斥对象时,它可以调用ReleaseMutex函数。
当一个线程拥有一个互斥对象后,它可以用该互斥对象多次调用等待函数而不会阻塞。这防止一个线程等待一个它拥有的互斥对象时出现死锁。不过,为了释放所有权,该线程必须为每一个等待操作调用一次ReleaseMutex函数。
6.WaitForSingleObject 函数功能:
当下列情况之一发生时该函数返回:(1)指定对象处于信号态;(2)超时。函数原型:
DWORD waitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);参数:
·hHandle:等待对象句柄。若想了解指定句柄的对象类型列表,参阅下面备注部分。
在Windows NT中,句柄必须有SYNCHRONIZE访问权限。若想获得更多的信息,请查看Standard Access Rights。
·dwMilliseconds:指定以毫秒为单位的超时间隔。如果超时,即使对象的状态是非信号态的并且没有完成,函数也返回。如果dwMilliseconds是0,函数测试对象的状态并立即返回;如果dwMilliseconds是INFINITE,函数从不超时。返回值:
如果函数调用成功,返回值表明引起函数返回的事件。可能值如下:
·WAIT-ABANDONED:指定对象是互斥对象,在线程被终止前,线程没有释放互斥对象。互斥对象的所属关系被授予调用线程,并且该互斥对象被置为非信号态。·WAIT-OBJECT-0:指定对象的状态被置为信号态。
·WAIT-TIMEOUT:超时,并且对象的状态为非信号态。
如果函数调用失败,返回值是WAIT-FAILED。若想获得更多错误信息,请调用GetLastError函数。
备注:
waitForSingleObjects函数决定等待条件是否被满足。如果等待条件并没有被满足,调用线程进入一个高效的等待状态,当等待满足条件时占用非常少的处理机时间。
在运行前,一个等待函数修改同步对象类型的状态。修改仅发生在引起函数返回的对象身上。例如,信号的计数减1。
WaitForSingleObjects函数能等待的对象包括:Change notification(改变通告);Consoleinput(控制台输入);Event(事件);Job(作业);Mutex(互斥对象);Process(进程);Semaphore(信号量);Thread(线程);Waitable timer(可等待定时器)。
当使用等待函数或代码直接或间接创建窗口时,一定要小心。如果一个线程创建了任何窗口,它必须处理进程消息。消息广播被发送到系统的所有窗口。一个线程用没有超时的等待函数也许会引起系统死锁。间接创建窗口的两个例子是DDE和COM CoInitialize。因此,如果用户有一个创建窗口的线程,用MsgWaitForMultipleObjects或MsgWaitForMultipleObjectsEx函数,而不要用SignalObjectAndWait函数。
7.WaitForMultipleObjects 函数功能:
WaitForMultipleObjects函数当下列条件之一满足时返回:(1)任意一个或全部指定对象处于信号态;(2)超时间隔以过。
函数原型:
DWORD WaitForMultipleObjects(DWORD nCount,CONST HANDLE *lpHandles, BOOL fWaitAll,DWORD dwMilliseconds);参数:
·nCount:指定由lpHandles所指向的数组中的句柄对象数目MAXIMUM-WAIT-OBJECTS。
·lpHandles:指向对象句柄数组的指针。该数组可以包含不同类型对象的句柄。在Windows NT中,句柄必须有SYNCHRONIZE访问权限。若想获得更多的信息,请查看Standard Access Rights。
·fWaitall:指定等待类型。如果为TRUE,当lpHandles指向的数组里的全部对象为信号态时,函数返回。如果为FALSE,当由lpHandles指向的数组里的任一对象为信号态时,函数返回。对于后者,返回值指出引起函数返回的对象。
·dwMilliseconds:指定以毫秒为单位的超时间隔。如果超时,即使bWaitAll参数指定的条件没有满足,函数也返回。如果dwMilliseconds是0,函数测试对象的状态并立即返回;如果dwMilliseconds是INFINITE,函数从不超时。
返回值:
如果函数调用成功,返回值表明引起函数返回的事件。可能值如下:
·WAIT-OBJECT-0到WAIT-OBJECT-0+nCount-1:如果bWaitAll为TRUE,那么返回值表明所有指定对象的状态为信号态。如果bWaitAll为FALSE,那么返回值减去WAIT-OBJECT-0表明引起函数返回的对象的pHandles数组索引。如果多于一个对象变为信号态,则返回的是数组索引最小的信号态对象索引。
·WAIT-ABANDONED-0到·WAIT-ABANDONED-0+ nCount-1:如果bWaitAll为TRUE,那么返回值表明所有指定对象的状态为信号态,并且至少一个对象是已放弃的互斥对象。如果bWaitAll为FALSE,那么返回值减去WAIT-OBJECT-0表明引起函数返回的放弃互斥对象的pHandles数组索引。
·WAIT-TIMEOUT:超时并且由参数bWaitAll指定的条件没有满足。
如果函数调用失败,返回值是WAIT-FAILED。若想获得更多错误信息,请调用GetLastError函数。
8.CreateSemapore 函数功能:
该函数是创建一个有名或者无名信号对象。函数原型:
HANDLE CreateSwmaphore(LPSECURITY-ATTRIBUTES lpAttributes,LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName);参数:
·lpAttributes:安全属性。如果是NULL就表示要使用默认属性。·lInitialCount:Semapore的初值。必须大于或等于0,并且小于或等于MaximumCount。·lMaximumCount:Semapore的最大值。这也就是在同一时间内能够锁住Semapore之线程的最多个数。
·lpName:Semapore的名称(一个字符串)。任何线程(或进程)都可以根据这一名称引用到这个Semaphore。这个值可以是NULL,意思是产生一个没有名字的Semaphore。
返回值:
如果成功就传回一个handle,否则传回NULL。不论哪一种情况,GetLastError都会传回一个合理的结果。如果指定的Semaphore名称已经存在,则该函数还是成功的,GetLastError会传回ERROR_ALREADY_EXISTS。
9.ReleaseSemaphore 函数功能:
该函数将指定信号对象的计数增加一个指定的数量。函数原型:
BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount,LPLONG lpPreviousCount);参数:
·hSemaphore:Semaphore的handle。
·lReleaseCount:Semaphore现值的增额。该值不可以是负值或0。·lpPreviousCount:借此返回Semaphore原来的值。返回值:
如果成功,则返回TRUE。否则返回FALSE。失败时可调用GetLastError获得原因。备注:
无论ReleaseSemaphore对于Semaphore所造成的当前值怎样增加,都绝对不会超过CreateSemaphore时所指定的ImaximumCount。
请记住,lpPreviousCount所传回来的是一个瞬间值。你不可以把lReleaseCount加上* lpPreviousCount,就当做是Semaphore的当前值,因为其他线程可能已经改变了Semaphore的值。
与mutex不同的是,调用ReleaseSemaphore的那个线程,并不一定就是调用WaitXxx 的那个线程。任何线程都可以在任何时候调用ReleaseSemaphore,解除被任何线程锁定的Semaphore。10.InitializeCriticalSection 函数功能:
该函数初始化临界区对象。函数原型:
VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 参数:
·lpCriticalSection:指向临界区对象的指针。备注:
单进程的所有线程可以使用互斥同步机制的临界区对象。但是,不能保证线程获得临界区所有权的顺序,系统将对所有线程公平处理。
进程负责分配临界区对象使用的存储空间,这可以通过声明CRITICAL_SECTION类型的变量来完成。在使用临界区之前,该进程的一些线程必须使用InitializeCriticalSection或InitializeCriticalSectionAndSectiom函数来初始化该临界区对象。
11.EnterCriticalSection 函数功能:该函数是等待指定临界区对象的所有权。当调用线程被赋予所有权时,该函数返回。
函数原型:
VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 参数:
·lpCriticalSection:指向临界区对象的指针。12.LeaveCriticalSection 函数功能:该函数释放指定临界区对象的所有权。函数原型:
VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 参数:
·lpCriticalSection:指向临界区对象的指针。4.1.5参考源代码
下面的程序已经在Windows 2000/XP上实现。用VC6.0创建源文件,将输入文件命名为thread.dat并放在与源文件相同的文件夹内,编译运行即可(本节中的参考源代码仅供参考)。
#include “windows.h” #include
#define READER 'R'
// 读者 #define WRITER 'W'
// 写者
#define INTE_PER_SEC 1000
// 每秒时钟中断数目。#define MAX_THREAD_NUM 64
// 最大线程数目 #define MAX_FILE_NUM 32
// 最大数据文件数目 #define MAX_STR_LEN 32
// 字符串长度
int readcount=0;
// 读者数目 int writecount=0;
// 写者数目
CRITICAL_SECTION RP_Write;
//临界区 CRITICAL_SECTION cs_Write;CRITICAL_SECTION cs_Read;struct ThreadInfo { int
serial;
// 线程序号
char entity;
//线程类别(判断读者线程还是写者线程)double delay;double persist;};
/////////////////////////////////////////////////////////////////////////// // 读者优先--读者线程 //p:读者线程信息
void RP_ReaderThread(void* p){
//互斥变量
HANDLE h_Mutex;h_Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,“mutex_for_readcount”);
DWORD wait_for_mutex;
//等待互斥变量所有权 DWORD m_delay;
// 延迟时间
DWORD m_persist;
// 读文件持续时间 int m_serial;
//线程序号 //从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);Sleep(m_delay);
//延迟等待
printf(“Reader thread %d sents the reading require.n”,m_serial);
// 等待互斥信号,保证对readcount的访问、修改互斥 wait_for_mutex=WaitForSingleObject(h_Mutex,-1);//读者数目增加 readcount++;if(readcount==1){
//第一个读者,等待资源
EnterCriticalSection(&RP_Write);} ReleaseMutex(h_Mutex);
//释放互斥信号
//读文件
printf(“Reader thread %d begins to read file.n”,m_serial);Sleep(m_persist);
// 退出线程
printf(“Reader thread %d finished reading file.n”,m_serial);//等待互斥信号,保证对readcount的访问、修改互斥 wait_for_mutex=WaitForSingleObject(h_Mutex,-1);//读者数目减少 readcount--;if(readcount==0){
//如果所有读者读完,唤醒写者
LeaveCriticalSection(&RP_Write);} ReleaseMutex(h_Mutex);
//释放互斥信号 }
/////////////////////////////////////////////////////////////////////////// // 读者优先--写者线程 //p:写者线程信息
void RP_WriterThread(void* p){ DWORD m_delay;
// 延迟时间
DWORD m_persist;
// 写文件持续时间 int m_serial;
//线程序号 //从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);Sleep(m_delay);
//延迟等待
printf(“Writer thread %d sents the writing require.n”,m_serial);// 等待资源
EnterCriticalSection(&RP_Write);
//写文件 printf(“Writer thread %d begins to Write to the file.n”,m_serial);Sleep(m_persist);
// 退出线程
printf(“Writer thread %d finished Writing to the file.n”,m_serial);//释放资源
LeaveCriticalSection(&RP_Write);}
/////////////////////////////////////////////////////////////////////////// // 读者优先处理函数 //file:文件名
void ReaderPriority(char* file){ DWORD n_thread=0;
//线程数目 DWORD thread_ID;
//线程ID DWORD wait_for_all;
//等待所有线程结束 //互斥对象
HANDLE h_Mutex;h_Mutex=CreateMutex(NULL,FALSE,“mutex_for_readcount”);
//线程对象的数组
HANDLE h_Thread[MAX_THREAD_NUM];ThreadInfo thread_info[MAX_THREAD_NUM];
readcount=0;
// 初始化 readcount
InitializeCriticalSection(&RP_Write);
//初始化临界区 ifstream inFile;inFile.open(file);
//打开文件 printf(“Reader Priority:nn”);while(inFile){
//读入每一个读者、写者的信息
inFile>>thread_info[n_thread].serial;
inFile>>thread_info[n_thread].entity;
inFile>>thread_info[n_thread].delay;
inFile>>thread_info[n_thread++].persist;
inFile.get();} for(int i=0;i<(int)(n_thread);i++){
if(thread_info[i].entity==READER || thread_info[i].entity=='R')
{
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_ReaderThread),&thread_info[i],0,&thread_ID);//创建读者线程
} else{
//创建写者线程
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_WriterThread),&thread_info[i],0,&thread_ID);
} }
//等待所有线程结束
wait_for_all=WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1);printf(“All reader and writer have finished operating.n”);}
/////////////////////////////////////////////////////////////////////////// // 写者优先--读者线程 //p:读者线程信息
void WP_ReaderThread(void* p){
//互斥变量
HANDLE h_Mutex1;h_Mutex1=OpenMutex(MUTEX_ALL_ACCESS,FALSE,“mutex1”);HANDLE h_Mutex2;h_Mutex2=OpenMutex(MUTEX_ALL_ACCESS,FALSE,“mutex2”);
DWORD wait_for_mutex1;
//等待互斥变量所有权 DWORD wait_for_mutex2;DWORD m_delay;
// 延迟时间
DWORD m_persist;
// 读文件持续时间 int m_serial;
//线程序号 //从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);Sleep(m_delay);
//延迟等待
printf(“Reader thread %d sents the reading require.n”,m_serial);wait_for_mutex1= WaitForSingleObject(h_Mutex1,-1);//进入读者临界区
EnterCriticalSection(&cs_Read);// 阻塞互斥对象mutex2,保证对readcount的访问、修改互斥 wait_for_mutex2= WaitForSingleObject(h_Mutex2,-1);//修改读者数目 readcount++;if(readcount==1){
//如果是第一个读者,等待写者写完
EnterCriticalSection(&cs_Write);} ReleaseMutex(h_Mutex2);
//释放互斥信号mutex2 // 让其他读者进入临界区
LeaveCriticalSection(&cs_Write);ReleaseMutex(h_Mutex1);//读文件
printf(“Reader thread %d begins to read file.n”,m_serial);Sleep(m_persist);// 退出线程
printf(“Reader thread %d finished reading file.n”,m_serial);// 阻塞互斥对象mutex2,保证对readcount的访问、修改互斥 wait_for_mutex2= WaitForSingleObject(h_Mutex2,-1);readcount--;if(readcount==0){
// 最后一个读者,唤醒写者
LeaveCriticalSection(&cs_Write);} ReleaseMutex(h_Mutex2);
//释放互斥信号 } /////////////////////////////////////////////////////////////////////////// // 写者优先--写者线程 //p:写者线程信息
void WP_WriterThread(void* p){ DWORD m_delay;
// 延迟时间
DWORD m_persist;
// 写文件持续时间 int m_serial;
//线程序号 DWORD wait_for_mutex3;//互斥对象
HANDLE h_Mutex3;h_Mutex3= OpenMutex(MUTEX_ALL_ACCESS,FALSE,“mutex3”);
//从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);Sleep(m_delay);
//延迟等待 printf(“Writer thread %d sents the writing require.n”,m_serial);
// 阻塞互斥对象mutex3,保证对writecount的访问、修改互斥 wait_for_mutex3= WaitForSingleObject(h_Mutex3,-1);writecount++;
//修改读者数目 if(writecount==1){
//第一个写者,等待读者读完
EnterCriticalSection(&cs_Read);} ReleaseMutex(h_Mutex3);
//进入写者临界区
EnterCriticalSection(&cs_Write);//写文件
printf(“Writer thread %d begins to Write to the file.n”,m_serial);Sleep(m_persist);
// 退出线程
printf(“Writer thread %d finishing Writing to the file.n”,m_serial);
//离开临界区
LeaveCriticalSection(&cs_Write);
// 阻塞互斥对象mutex3,保证对writecount的访问、修改互斥 wait_for_mutex3= WaitForSingleObject(h_Mutex3,-1);writecount--;
//修改读者数目 if(writecount==0){
//写者写完,读者可以读
LeaveCriticalSection(&cs_Read);} ReleaseMutex(h_Mutex3);}
/////////////////////////////////////////////////////////////////////////// // 写者优先处理函数 //file:文件名
void WriterPriority(char* file){ DWORD n_thread=0;
//线程数目 DWORD thread_ID;
//线程ID DWORD wait_for_all;
//等待所有线程结束
//互斥对象
HANDLE h_Mutex1;h_Mutex1=CreateMutex(NULL,FALSE,“mutex1”);HANDLE h_Mutex2;h_Mutex2=CreateMutex(NULL,FALSE,“mutex2”);HANDLE h_Mutex3;h_Mutex3=CreateMutex(NULL,FALSE,“mutex3”);
//线程对象
HANDLE h_Thread[MAX_THREAD_NUM];ThreadInfo thread_info[MAX_THREAD_NUM];
readcount=0;
// 初始化 readcount writecount=0;
// 初始化writecount InitializeCriticalSection(&cs_Write);
//初始化临界区 InitializeCriticalSection(&cs_Read);ifstream inFile;inFile.open(file);
//打开文件 printf(“Writer Priority:nn”);while(inFile){
//读入每一个读者、写者的信息
inFile>>thread_info[n_thread].serial;
inFile>>thread_info[n_thread].entity;
inFile>>thread_info[n_thread].delay;
inFile>>thread_info[n_thread++].persist;
inFile.get();} for(int i=0;i<(int)(n_thread);i++){
if(thread_info[i].entity==READER || thread_info[i].entity=='R')
{
//创建读者线程
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_WriterThread),&thread_info[i],0,&thread_ID);
} else{
//创建写者线程
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(WP_WriterThread),&thread_info[i],0,&thread_ID);
} }
//等待所有线程结束
wait_for_all=WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1);printf(“All reader and writer have finished operating.n”);} /////////////////////////////////////////////////////////////////////////////// //主函数
int main(int argc,char* argv[]){ char ch;
while(true){
//打印提示信息
printf(“************************************************n”);
printf(“
1:Reader Priorityn”);
printf(“
2:Writer Priorityn”);
printf(“
3:Exit Priorityn”);
printf(“************************************************n”);
printf(“Enter your choice(1,2 or 3): ”);
//如果输入信息不正确,继续输入
do{
ch=(char)_getch();
}while(ch!= '1' &&ch!= '2' && ch!= '3');
system(“cls”);
//选择3,返回
if(ch=='3')
return 0;
//选择1,读者优先
else if(ch=='1')
ReaderPriority(“thread.dat”);
//选择2,写者优先
else if(ch=='2')
WriterPriority(“thread.dat”);
//结束
printf(“nPress Any Key To Continue: ”);
_getch();
system(“cls”);} return 0;}
说明:
在Win32 API中,互斥对象Mutex与P、V中的互斥信号量有类似的地方,但也有不同:在P、V操作中的互斥信号量可以有一个任意大小的初值,但互斥对象Mutex没有,它可以被看成是初值为1的互斥信号量。而且一个线程在取得Mutex的所有权之后,即使不调用ReleaseMutex函数,在线程结束时,线程也会自动释放Mutex的所有权。
临界区对象CriticalSection则与P、V操作中初值为1的互斥信号量语意相同。它在线程结束时会将CriticalSection的所有权传递给它的同类型线程。这样就可以满足在一个线程中申请所有权,在另一个线程释放所有权的要求。在读者优先中的write互斥信号量以及写者优先中的read和write互斥信号量就应该用CriticalSection实现而不应该用Mutex。
用WaitForSingleSignal函数可以获得一个Mutex的所有权,类似于P操作,而ReleaseMutex函数可以释放一个Mutex的所有权,类似于V操作。
用EnterCriticalSection函数可以进入一个CriticalSection,类似于P操作,而LeaveCriticalSection函数离开一个CriticalSection,类似于V操作。
备注:
ReaderPriority和WritePriority函数最后都有
wait_for_all=WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1);是因为主函数要等待所有的线程都结束之后才退出。因为不知道有多少线程,所以源文件最初有:
#define MAX_THREAD_NUM 64 //最大线程数目
即线程最多不能超过MAX_THREAD_NUM个。线程对象的数组大小为MAX_THREAD_NUM。如果创建的线程没有那么多,空间会有浪费,但是可以达到牺牲空间来节省时间的目的。
有的书上还有其他的处理方法。一种是在主函数的最后加上Sleep(1000),即通过主函数睡眠的方法等待其他线程结束,这当然不是一种很好的方法,因为睡眠等待的时间没法控制。另一种方法是增加循环变量threadCount(线程的个数),每个线程结束的就会执行语句
threadCount--;主函数的最后测试:
while(threadcount>0);但是这种方式会让主函数循环等待,浪费了CPU资源。
相比之下,考虑到运行效率,还是实例中给出的方法比较好写些。
第四篇:操作系统课程设计报告
课程设计报告
题 目: 模拟请求页式管理
课程名称: 计算机操作系统 学 院: 信息工程学院
专 业: 计算机科学与技术
班 级: 14计本(1)学生姓名: * * * 学 号: 201403031** 指导教师: * * 成 绩:
开课时间: 2016-2017 学年 一 学期
模拟请求页式管理
第1章 需求分析
1.1设计要求
请求页式管理是一种常用的虚拟存储管理技术。本设计通过请求页式存储管理中页面置换算法模拟设计,了解虚拟存储技术的特点,掌握请求页式管理的页面置换算法。本实验要求用Vc++或其他高级语言编写和调试。
编写程序实现:
(1)先进先出页面置换算法(FIFO)(2)最近最久未使用页面置换算法(LRU)最佳置换页面置换算法(OPT)设计一个虚拟存储区和内存工作区,编程序演示以上三种算法的具体实现过程,并计算访问命中率。
1.2解决方案
首先确定实现语言使用c#实现图形化界面,后确定要实现哪些功能,比如算法选择,页面添加,模拟控制。然后确定输出结构以便于程序的测试和验证。将基本框架建立后再进行编程。编程前进行算法结构分析最后编程实现。
1.3算法实现原理
1、先进先出置换算法(FIFO):
发生缺页中断时按照页面进入内存顺序总是淘汰最先进入内存的页面。
2、最近最久未使用置换算法(LRU):
发生缺页中断时总是淘汰存在内存中最长时间未被使用的页面。
3、最佳置换算法(OPT):
发生缺页中断时若一个或几个页面将来将不会被调用则按先进先出原则淘汰页面,若将来都有调用则比较调用时刻选择最远时刻页面淘汰。
4、缺页率:缺页次数占页面调用次数的百分比。
第2章 概要设计
2.1数据设计
常变量:调用页面最大数量(MaxN),内存最大页面数(MaxM)待调用页面数组:page_dd[MaxN]存放等待调用的页面号
页面数组专用指针 page_p,用于指向page_dd数组中正需调入内存的页号 内存块数组:Memery[MaxM],存放内存当前存放的页号 缺页计数器:count,记录缺页次数
内存块状态数组:M1[MaxN],M2[MaxN],M3[MaxN],记录每次页面调用结束后内存各块的状态
缺页记录数组s[MaxN],用于记录页面调用时是否产生缺页中断,初始化为是
2.2函数设计
1、页面添加函数:void btnAdd_Click(object sender, EventArgs e)用于实现通过点击按钮实现数据输入。
2、内存初始化函数:init(int[] a, int[] b,int []m1,int[]m2,int[]m3)参数有页面数组、内存数组、状态数组,采用先进先出算法对内存先进行装满 服务于先进先出页面置换函数和最佳置换函数。
3、输出函数:void display(int[]a,int[]m1,int[]m2,int[]m3,char[]c)用于输出模拟结果,参数有页面数组,内存数组,状态数组,缺页记录数组。再模拟之后调用。
4、模拟控制函数:void btnmo_Click(object sender, EventArgs e)用于实现通过单击模拟按钮,根据用户所选算法进行模拟并显示结果。
5、先进先出算法模拟函数:
void FIFO(int[] a, int[] b,int[]m1,int[]m2,int[]m3,char[] s)用于实现先进先出算法模拟,参数有页面数组,内存数组、内存状态记录数组,缺页记录数组。在模拟函数中调用。
6、最近最久未使用算法模拟函数:
void LRU(int[] a, int[] b, int[] m1, int[] m2, int[] m3, char[] s)用于 3 实现最近最久未使用算法模拟,参数有页面数组,内存数组,内存状态记录数组,缺页记录数组。在模拟函数中被调用。
7、最近最久未使用函数辅助函数:void LUR_I(int[] a,int e)用于对最近最久未使用算法中所用辅助数组(记录页面存在时长)进行调整,参数有辅助数组及需调整的数据下标。在最近最久未使用函数中调用。
8、最佳置换算法模拟函数:
void OPT(int[] a, int[] b, int[] m1, int[] m2, int[] m3, char[] s)用于模拟最佳置换算法。参数有页面数组,内存数组,内存状态记录数组,缺页记录数组。在模拟函数中被调用。
9、最佳置换算法辅助函数:void OPT_F(int[] a, int e)用于对最佳置换算法中的辅助数组进行调整。参数有辅助数组,需调整数据下标。在最佳置换算法中被调用。
10、重置函数:void btncz_Click(object sender, EventArgs e)用于重新选择算法进行新的模拟。
2.3主要算法设计
1、初始化函数算法:
第一步:将第一个页面调入内存,调整最佳置换算法辅助数组,缺页计数器加一,保存内存数组状态。
第二步:调用下一个页面并判断内存中是否有本页面有转第三步,无转第四步。第三步:更改缺页数组对应下标值,记录当前内存状态,调整最佳置换算法辅助数组,页面指针指向下一页。
第四步:将页面调入内存,调整最佳置换算法辅助函数,缺页计数器加一,保存内存数组状态。若内存尚不满转第一步。具体见图1初始化算法流程图。
开始页面调入内存缺页计数器加一记录内存状态调用下一页否否内存是否有该页面是记录内存状态修改缺页数组内存已满是结束
图1 初始化算法流程图
2、先进先出页面置换算法:
第一步:检查内存中是否已有需调用页面,有则转第二步,无则转第三步。第二步:记录当前内存状态,修改缺页数组对应下标值。
第三步:内存中无需要调用的页面,进行出队操作,然后进行入队操作,记录内存块状态,缺页计数器加一。
第四步:若页面数组未被调用结束转第一步。具体见图2先进先出算法流程图。
开始页面调入内存该页在内存中是否已存在是否否先出队操作后入队操作记录内存状态修改缺页数组值记录内存状态缺页计数器加一页面调用结束是结束
图2 先进先出算法流程图
3、最近最久未使用置换算法:
第一步:将页面调入内存,记录内存状态,缺页计数器加一,调整辅助数组,页面指针加一。
第二步:检查内存中是否已有所需页面,有转第三步,无转第一步。
第三步:修改缺页数组对应下标记录,记录内存状态,调整辅助数组,页面指针加一。第四步:内存是否已满,无则转第一步,是则转第五步。
第五步:检查内存中是否有所需页面,有则记录当前内存状态,修改缺页数组对应下标值。无则转第六步。
第六步:检查辅助数组找出最大值并记录其下标,置换内存中对应下标的数据,调整辅助数组,缺页计数器加一。
第七步:页面是否调用结束未结束则转第五步。具体见图3最近最久未使用算法流程图。
开始调入页面至内存记录内存状态计数器加一否调整辅助数组调用下一页内存中是否已有该页否内存已满是通过辅助数组确定淘汰页面是修改缺页数组记录内存状态调整辅助数组否页面置换记录内存状态计数器加一调用结束是结束
图3 最近最久未使用算法
4、最佳置换算法:
第一步:检查内存中是否已有所需页面,有则记录内存状态,修改缺页数组对应下标数值。无则转第二步。
第二步:判断内存中各页面的未来调用情况,记录是否还有调用,若有则记录调用时刻。
第三步:分析调用情况,内存中页面都在将来不会被调用转第四步,有一个被调用转第五步,有两个被调用转第六步,全被调用转第七步。
第四步:查找辅助数组找到内存中存在时间最长的页面进行置换,修改内存状态,缺页计数器加一,修改辅助数组。
第五步:查找到不会被调用的页面,并根据辅助数组选择最早进入内存的页面将其置换。修改内存状态,缺页计数器加一,修改辅助数组。
第六步:查找辅助数组找到将来不需要在调用的页面将其置换,修改辅助数组,记录内存状态,缺页计数器加一。
第七步:查找辅助数组,找寻最晚被调用的页面,将其置换。记录内存状态,修改辅助数组,缺页计数器加一。
第八步:页面是否调用完成,否则转第一步。具体见图4最佳置换算法流程图
开始调入页面记录内存状态计数器加一更新辅助函数是页面已存在否向后检查内存当前页面调用情况所有页面都不会再度调用否是一个页面会调用否否是两个页面会调用是否查找辅助数组得到最先进入页面通过辅助数组得到不会再调用的页面通过辅助数组获取最晚调用的页面通过辅助数组得到另外两个页面中最先进入的页面置换页面记录内存状态计数器加一更新辅助函数页面调用结束是结束
图4 最佳置换算法流程图 2.4界面设计
采用c# 设计windows窗体应用程序,使用下拉列表框选择算法,通过按钮添加待调用的页面。通过文本控件显示模拟结果。显示样式:第一行:算法名称;
第二行:调用页面顺序;
第三行至第五行显示内存在每调用一次页面后的状态;
第六行:是否缺页;
最后一行显示缺页率;
第3章 详细设计与实现
3.1函数设计
1、添加按钮功能实现代码
主要功能:实现单击一次添加一个调用页面,并给出相应的提示,如正在输入的是第几次调度页面,在输入为空时能够弹出对话框提示用户,在输入完成时为避免数组越界应在输入完成时隐藏;输入过程中始终保证时输入焦点。private void btnAdd_Click(object sender, EventArgs e){ if(txtAdd.Text!= “")//输入不为空才能继续输入 { page_dd[i_add] = Convert.ToInt32(txtAdd.Text);/*将输入值赋值给页面数组*/ txtShow.Text += txtAdd.Text + ” “;/*显示供用户查阅*/ i_add++;txtAdd.Clear();/*清空*/ if(i_add == MaxN)//输入结束时 { txtAdd.ReadOnly = true;//不允许继续输入 btnAdd.Hide();//按钮隐藏 return;} txtAdd.Focus();//设置为输入焦点
label2.Text = ”第“ +(i_add + 1)+ ”次调度页面:“;/*提示用户正在输入的是第几次调度页面*/ } /*输入为空则弹出对话框提示用户输入为空*/ else { MessageBox.Show(”请输入调用页面!“, ”输入为空“, MessageBoxButtons.OK, MessageBoxIcon.Warning);txtAdd.Focus();} }
2、初始化函数
主要功能:将内存一先进先出方式填满,并记录每个页面进入时间,服务于先进先出页面置换算法和最佳置换算法。
void init(int[] a, int[] b,int []m1,int[]m2,int[]m3){ /*内存未满时循环*/ for(int i = 0;i < MaxM&&page_p //调整辅助数组将刚进入内存的页面的对应时间 OPT_F(O_Q ,i); count++;//缺页计数器加一 m1[page_p] = b[0];//保存内存状态 m2[page_p] = b[1];m3[page_p] = b[2];page_p++;//调用下一页面 //检查内存中是否原先就有需要的页面; for(int j = 0;j <= i&&page_p s[page_p] = 'F';//缺页数组对应数据更改 m1[page_p] = b[0];//记录内存状态 m2[page_p] = b[1];m3[page_p] = b[2];OPT_F(O_Q,-1);//调整最佳置换算法辅助函数 page_p++;//调用下一页 j =-1;//重新开始寻找 } } } } 3、先进先出页面置换函数 主要功能:根据先进先出算法要求在产生缺页中断时采用先进先出方式确定淘汰页面,并在每次页面调用时记录下内存状态,缺页次数;采用循环队列使得每次出队的一定是最先进入内存的。 private void FIFO(int[] a, int[] b,int[]m1,int[]m2,int[]m3,char[] s){ int Fpage_p = page_p;int front, rear;//定义队列对手和对尾指针并初始化 front = 0;rear = MaxM1;int sa;for(;Fpage_p < MaxN;Fpage_p++){ sa = 0;for(int i = 0;i < MaxM;i++)//检查内存中是否已有要调用的页面。 { if(b[i] == a[Fpage_p]){ m1[Fpage_p] = b[0];m2[Fpage_p] = b[1];m3[Fpage_p] = b[2];s[Fpage_p] = 'F';sa = 1;break;} } if(sa == 0){ front =(front + 1)% MaxM; rear =(rear + 1)% MaxM;b[rear] = a[Fpage_p];m1[Fpage_p] = b[0];m2[Fpage_p] = b[1];m3[Fpage_p] = b[2];count++;} else continue;} } /*最近最久未使用算法辅助数组调整函数*/ private void LUR_I(int[] a,int e){ int temp;temp = a[e];a[e] = 1;for(int i = 0;i < MaxM;i++){ if(a[i] < temp && i!=e)a[i]++;} } /*最佳置换算法辅助数组调整函数*/ private void OPT_F(int[] a, int e){ if(e!=-1){ a[e] = 0;for(int i = 0;i < MaxM;i++){ if(i!= e)a[i]++;} } else for(int i = 0;i < MaxM;i++){ a[i]++;} } /*最近最久未使用算法*/ private void LRU(int[] a, int[] b, int[] m1, int[] m2, int[] m3, char[] s){ int[] L_Q = new int[MaxM]{3,3,3};int sa;for(int i = 0;i < MaxM && page_p < MaxN;i++){ b[i] = a[page_p];//调入内存 count++;m1[page_p] = b[0];//保存内存状态 m2[page_p] = b[1];m3[page_p] = b[2];LUR_I(L_Q, i);page_p++;for(int j = 0;j <= i && page_p < MaxN;j++){ if(b[j] == a[page_p]){ s[page_p] = 'F';m1[page_p] = b[0];m2[page_p] = b[1];m3[page_p] = b[2];LUR_I(L_Q, j);page_p++;j =-1;} } } for(;page_p < MaxN;page_p++){ sa = 0;for(int i = 0;i < MaxM;i++)//用的页面。{ if(b[i] == a[page_p]){ m1[page_p] = b[0];m2[page_p] = b[1];m3[page_p] = b[2];s[page_p] = 'F';LUR_I(L_Q, i);sa = 1;break;} } if(sa == 0){ 检查内存中是否已有要调40 for(int i = 0;i < MaxM;i++){ if(L_Q[i] == 3){ b[i] = a[page_p];m1[page_p] = b[0];m2[page_p] = b[1];m3[page_p] = b[2];LUR_I(L_Q, i);break;} } count++;} else continue;} } /*最佳置换算法*/ private void OPT(int[] a, int[] b, int[] m1, int[] m2, int[] m3, char[] s){ int sa;int O_p;int Ocount;int[] OPT_I=new int [MaxM ]{-1 ,-1 ,-1 };int[] OPT_J=new int [MaxM]{MaxN ,MaxN ,MaxN };for(;page_p < MaxN;page_p++){ for(int i = 0;i < MaxM;i++){ OPT_I[i] =-1;//刷新状态数组 OPT_J[i] = MaxN;} sa = 0;for(int i = 0;i < MaxM;i++)//检查内存中是否已有要调用的页面。 { if(b[i] == a[page_p]){ m1[page_p] = b[0];m2[page_p] = b[1];m3[page_p] = b[2];OPT_F(O_Q,-1); s[page_p] = 'F';sa = 1;break;} } if(sa == 0)//缺页 { Ocount = 0;for(int i = 0;i < MaxM;i++){ O_p = page_p + 1;for(;O_p < MaxN;O_p++){ if(b[i] == a[O_p]){ Ocount++;OPT_I[i] = 1;OPT_J[i] = O_p;break;} } } switch(Ocount){ case 0://全部页面以后都不会再度调用 int temp = 0;for(int i = 0;i < MaxM;i++){ if(O_Q[i] > O_Q[temp])temp = i;} b[temp] = a[page_p];m1[page_p] = b[0];m2[page_p] = b[1];m3[page_p] = b[2];OPT_F(O_Q ,temp);count++;break;case 1://有一个页面将在以后调用 temp = 0;for(int i = 0;i < MaxM;i++){ if(OPT_I[i]!= 1 && O_Q[i] > O_Q[temp])temp = i; } b[temp] = a[page_p];m1[page_p] = b[0];m2[page_p] = b[1];m3[page_p] = b[2];OPT_F(O_Q ,temp);count++;break;case 2: for(int i = 0;i < MaxM;i++){ if(OPT_I[i] ==-1){ b[i] = a[page_p];m1[page_p] = b[0];m2[page_p] = b[1];m3[page_p] = b[2];OPT_F(O_Q, i);count++;} } break;case 3: int p = 0;for(int i = 0;i < MaxM;i++){ if(OPT_J[i] >OPT_J[p])p = i;} b[p] = a[page_p];m1[page_p] = b[0];m2[page_p] = b[1];m3[page_p] = b[2];OPT_F(O_Q, p);count++;break;} } } } /*重置函数*/ private void btncz_Click(object sender, EventArgs e){ comboBox1.SelectedIndex = 0; txtAdd.Text = ”“;page_p = 0;i_add = 0;count = 0;//txtShow.Text = ”";for(int i = 0;i < MaxM;i++)Memery[i] =-1;for(int i = 0;i < MaxN;i++)s[i] = 'T';} } } 操 作 系 统 课 程 设 计 实 验 报 告 学院:计算机科学与技术学院 班级:计112 学号:1113022032 姓名: 一、实验名称: 用C++实现驱动调度算法、页面替换算法、银行家算法、处理器调度算法 二、实验要求: 书写实验报告,包括的内容有: (1)实验题目 (2)程序中使用的数据结构及主要文字说明 (3)带有注释的源程序 (4)执行程序说明,表明各进程控制快的初始状态,以及各算法的运行状态 (5)通过实验后的收获与体会及对实验的改进意见和见解 二、实验目的: 通过自己编程来实现各类操作系统算法,进一步理解操作系统的概念及含义,提高对操作系统的认识,同时提高自己的动手实践能力。加强我们对各类算法的理解。 三、实验内容: 1、实现页面替换算法 (1)FIFO 先进先出页面替换算法 (2)LRU最近最少使用页面替换算法 (3)LFU最少使用频率页面替换算法 2、银行家算法 3、实现驱动调度算法 (1)先来先服务算法 (2)电梯算法 (3)扫描算法 4、实现处理器调度 (1)先进先出处理器调度 (2)时间片轮转法 (3)优先级调度 四、实验原理: 1、页面替换算法 先进先出页面置换算法:该算法总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面加以淘汰。将已调入内存的页面按先后次序链接成一个队列,将最先调入的页面与新页面进行置换 最近最久未使用置换算法:该算法是利用“最近的过去”作为“最近的将来”,将最近最久未使用的页面加以淘汰。将已调入内存的页面按先后顺序链接成一个队列,为每一个页面增加一个访问字段,用来记录一个页面自上次被访问以来所经历的是时间t,当需淘汰一个页面时,选择现有页面中其t值最大,即最近最久未使用的页面加以淘汰 2、银行家算法 先对用户提出的请求进行合法性检查,即检查请求的是不大于需要的,是否不大于可利用的。若请求合法,则进行试分配。最后对试分配后的状态调用安全性检查算法进行安全性检查。若安全,则分配,否则,不分配,恢复原来状态,拒绝申请。 3、驱动调度算法 先进先出算法(FIFO):总是严格按时间顺序对磁盘请求予以处理。算法实现简单、易于理解并且相对公平,不会发生进程饿死现象。但该算法可能会移动的柱面数较多并且会 经常更换移动方向,效率有待提高 电梯调度算法:总是将一个方向上的请求全部处理完后,才改变方向继续处理其他请求。 扫描算法(scan algorithm):总是从最外向最内(或最内向最外)进行扫描,然后在从最内向最外(或最外向最内)扫描。该算法与电梯调度算法的区别是电梯调度在没有最外或最内的请求时不会移动到最外或最内柱面。 4、处理器调度算法 先进先出处理器调度:按照作业进入系统后备工作队列的先后次序来挑选作业,先进入系统的作业将优先被挑选进入主存,创建用户进程,分配所需资源,然后移入就绪队列。 时间片轮转法调度算法:调度次序每次把CPU分配给就绪队列进程/线程使用规 定的时间间隔,就绪队列中每个进程/线程轮流的运行一个时间片,当时间片耗尽时,就强迫当前运行进程/线程让出处理器,转而排列到就绪队列尾部,等候下一轮调度。 优先级调度:根据确定的优先级来选取进程/线程,总是选择就绪队列中的优先 级最高者投入运行,即优先级越高,先被调用。 五、数据结构设计 对操作系统的各类算法设计数据结构如下: 页面替换算法:void FIFO();void LRU();void LFU(); 银行家算法:void Init()初始化算法 void Bank()银行家算法 bool Safe()安全性算法 驱动调度算法: struct MagneticHead//磁头构成{ int site; int count; bool direct; }; struct Range//磁盘磁道范围 { int mStart; int mEnd; }; struct RequestList//请求序列 { int site; bool state; }; struct Data//基本数据集合{ MagneticHead magneticHead; RequestList *requestList; int *executeList; Range range; int length; }; 处理器调度: typedef struct pcb//时间片轮转法 { char pname[N]; int runtime; int arrivetime; char state; struct pcb*next; }PCB; typedef struct PCB1//先进先出服务 { char ID[3];//进程号 char name[10];//进程名 char state;//运行状态 floatarrivetime;//到达时间 floatstarttime;//进程开始时间 floatfinishtime;//进程结束时间 floatservicetime;//服务时间 float turnaroundtime;//周转时间 float weightedturnaroundtime;//带权周转时间 struct PCB1 *next;//指向下个进程 }pcb1; struct pcb2 {优先级调度 char name[10]; char state; int super; int ntime; int rtime; struct pcb2* link; }*ready=NULL,*d; typedef struct pcb2 PCB2; 六、课程设计总结 在本次课程设计中,就是讲平时所做的实验结合起来,实现操作系统的各类算法,了解操作系统的运行原理以及其基本概念,更好的将操作系统的原理很好的展现出来。同时,在本次实验中遇到了很多问题,需要我们仔细的检查和修改。其次,实验中为了能更好的体现各类算法的运行情况,需要做一个清晰的界面,以能清楚地看出运行结果。第五篇:操作系统课程设计报告