进程同步.docx
《进程同步.docx》由会员分享,可在线阅读,更多相关《进程同步.docx(15页珍藏版)》请在冰豆网上搜索。
进程同步
操作系统课程设计
进程同步
专业
学生姓名
班级
学号
任课教师
完成日期
目录
一、设计概述1
二、设计目的2
三、设计要求2
四、设计内容3
(1)题目选择3
(2)设计难点3
(3)设计思路4
(4)设计流程图4
五、源程序5
(1)数据库Database.java5
(2)数据库服务器DatabaseServer.java7
(3)读者reader.java8
(4)写者writer.java9
六、执行及运行结果9
七、总结10
一、设计概述
所谓读者写者问题,是指保证一个writer进程必须与其他进程互斥地访问共享资源的同步问题。
读者写者问题可以这样表述,有一群写者和一群读者,写者在写同一本书,读者也在读这本书,多个读者可以同时读这本书,但是只能有一个写者在写书,并且读者优先,也就是说,读者和写者同时提出请求时,读者优先。
当读者提出请求时需要有一个互斥操作,另外需要有一个信号量mutex来当前是否可操作。
信号量机制是支持多道程序的并发操作系统设计中解决资源共享时进程间的同步与互斥的重要机制,而读者写者问题则是这一机制的一个经典范例。
与记录型信号量解决读者-写者问题不同,信号量机制它增加了一个限制,即最多允许n个读者同时读,为此引入了一个信号量Count,并赋予初值为0,通过执行Count++操作,来操作读者的数目。
每当有一个读者进入时,就要执行。
Count++操作,使Count的值加1,读者离开时Count--;当且仅当Count==0时,V(Wmutex),才能进入写操作,对利用信号量来解决读者-写者问题的表述如下:
Wmutex表示读写的互斥信号量,初值:
Wmutex=1;
公共变量Count表示“正在读”的进程数,初值:
Count=0;
Rmutex:
表示对Count的互斥操作,初值:
Rmutex=1;
Main()
{
intWmutex=1;
IntRmutex=1;
intCount=0;
cobegin
read1();
read2();
read3();
......
write1();
write2();
write3();
......
coend
}
readn()
{
P(Rmutex);
Count++;
If(Count==1)P(Wmutex);
V(Rmutex);
读
P(Rmutex);
Count--;
if(Count==0)V(Wmutex);
V(Rmutex);
}
writen()
{
P(Wmutex);
写
V(Wmutex);
}
二、设计目的
理解临界区和进程互斥的概念,掌握用信号量和PV操作实现进程互斥的方法。
三、设计要求
在linux环境下编写一个控制台应用程序,该程序运行时能创建N个线程,其中既有读者线程又有写者线程,它们按照事先设计好的测试数据进行读写操作。
用信号量和PV操作实现读者/写者问题。
四、设计内容
(1)题目选择
进程同步
(2)设计难点
在计算机操作系统中,PV操作是进程管理中的难点。
首先应弄清PV操作的含义:
PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下:
P(S):
①将信号量S的值减1,即S=S-1;
②如果S>=0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。
V(S):
①将信号量S的值加1,即S=S+1;
②如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。
PV操作的意义:
我们用信号量及PV操作来实现进程的同步和互斥。
PV操作属于进程的低级通信。
什么是信号量?
信号量的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。
信号量的值与相应资源的使用情况有关。
当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。
注意,信号量的值仅能由PV操作来改变。
一般来说,信号量S>=0时,S表示可用资源的数量。
执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。
而执行一个V操作意味着释放一个单位资源,因此S的值加1;若S=<0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。
利用信号量和PV操作实现进程互斥的一般模型是:
进程P1进程P2……进程Pn
………………
P(S);P(S);P(S);
临界区;临界区;临界区;
V(S);V(S);V(S);
……………………
其中信号量S用于互斥,初值为1。
使用PV操作实现进程互斥时应该注意的是:
(1)每个程序中用户实现互斥的P、V操作必须成对出现,先做P操作,进临界区,后做V操作,出临界区。
若有多个分支,要认真检查其成对性。
(2)P、V操作应分别紧靠临界区的头尾部,临界区的代码应尽可能短,不能有死循环。
(3)互斥信号量的初值一般为1。
(3)设计思路
将所有的读者和所有的写者分别放进两个等待队列中,当读允许时就让读者队列释放一个或多个读者,当写允许时,释放第一个写者操作。
读者优先:
如果没有写者正在操作,则读者不需要等待,用一个整型变量readcount记录当前的读者数目,用于确定是否释放写者线程,(当readcount=0时,说明所有的读者都已经读完,释放一个写者线程),每个读者开始读之前都要修改readcount,为了互斥的实现对readcount的修改,需要一个互斥对象Rmutex来实现互斥。
另外,为了实现写-写互斥,需要一个临界区对象write,当写者发出写的请求时,必须先得到临界区对象的所有权。
通过这种方法,可以实现读写互斥,当readcount=1时,(即第一个读者的到来时,),读者线程也必须申请临界区对象的所有权.
当读者拥有临界区的所有权,写者都阻塞在临界区对象write上。
当写者拥有临界区对象所有权时,第一个判断完readcount==1后,其余的读者由于等待对readcount的判断,阻塞在Rmutex上。
写者优先:
写者优先和读者优先有相同之处,不同的地方在:
一旦有一个写者到来时,应该尽快让写者进行写,如果有一个写者在等待,则新到的读者操作不能读操作,为此添加一个整型变量writecount,记录写者的数目,当writecount=0时才可以释放读者进行读操作!
为了实现对全局变量writecount的互斥访问,设置了一个互斥对象mutex3。
为了实现写者优先,设置一个临界区对象read,当有写者在写或等待时,读者必须阻塞在临界区对象read上。
读者除了要一个全局变量readcount实现操作上的互斥外,还需要一个互斥对象对阻塞在read这一个过程实现互斥,这两个互斥对象分别为mutex1和mutex2。
(4)设计流程图
五、源程序
(1)数据库Database.java
publicclassDatabase{
privatestaticfinalintNAP_TIME=5;
privateintreaderCount;
privateintwriterCount;
privatebooleandbReading;
privatebooleandbWriting;
publicDatabase(){
super();
readerCount=0;
writerCount=0;
dbReading=false;
dbWriting=false;
//TODOAuto-generatedconstructorstub
}
publicstaticvoidnapping(){
intsleepTime=(int)(NAP_TIME*Math.random());
try{
Thread.sleep(sleepTime*1000);
}catch(Exceptione){
e.printStackTrace();
}
}
publicsynchronizedintstartReading(){
while(writerCount>0){
try{
System.out.println("readeriswaiting");
wait();
}catch(Exceptione){
System.out.println(e.toString());
e.printStackTrace();
}
}
if(readerCount==1){
dbReading=true;
}
returnreaderCount;
}
publicsynchronizedintendReading(){
--readerCount;
if(readerCount==0){
dbReading=false;
}
notifyAll();
System.out.println("onereaderisdonereading.Count="+readerCount);
returnreaderCount;
}
publicsynchronizedvoidstartWriting(){
while(dbReading==true||dbWriting==true){
try{
System.out.println("Writeriswaiting");
wait();
}catch(Exceptione){
System.out.println(e.toString());
}
}
dbWriting=true;
}
publicsynchronizedvoidendWriting(){
--writerCount;
dbWriting=false;
System.out.println("onewriterisdonewriting.Count="+writerCount);
notifyAll();
}
}
(2)数据库服务器DatabaseServer.java
publicclassDatabaseServer{
publicDatabaseServer(){
super();//TODOAuto-generatedconstructorstub
}
publicstaticvoidmain(String[]args){
Databasedb=newDatabase();
Readerr1=newReader(1,db);
Readerr2=newReader(2,db);
Readerr3=newReader(3,db);
Readerr4=newReader(4,db);
Writerw1=newWriter(1,db);
Writerw2=newWriter(2,db);
r1.start();
r2.start();
r3.start();
w1.start();
r4.start();
w2.start();
}
}
(3)读者reader.java
publicclassReaderextendsThread{
privateDatabaseserver;
privateintreaderNum;
publicReader(intr,Databasedb){
super();
readerNum=r;
server=db;//TODOAuto-generatedconstructorstub
}
publicvoidrun(){
intc;
while(true){
System.out.println("reader"+readerNum+"issleeping");
Database.napping();
System.out.println("reader"+readerNum+"wantstoread");
c=server.startRead();
System.out.println("reader"+readerNum+"isreading.Count="+c);
Database.napping();
c=server.endReading();
System.out.println("Itisreader"+readerNum+"whohasdonereadingaccordingtocount="+c);
}
}
}
(4)写者writer.java
publicclassWriterextendsThread{
privateDatabaseserver;
privateintwriterNum;
publicWriter(intw,Databasedb){
super();
writerNum=w;
server=db;//TODOAuto-generatedconstructorstub
}
publicvoidrun(){
while(true){
System.out.println("Writer"+writerNum+"issleeping");
Database.napping();
System.out.println("Writer"+writerNum+"wantstowrite");
server.startWriting();
System.out.println("Writer"+writerNum+"iswriting");
Database.napping();
server.endWriting();
System.out.println("ItisWriter"+writerNum+"whohasdonewriting.");
}
}
六、执行及运行结果
(1)进入文件目录cddym/
(2)列出当前目录下的文件夹及文件ls
(3)运行javaDatabase及javaDatabaseServer
七、总结
这次的课程设计做的并不顺利,而其中的种种遭遇更是让我反省良久。
一路坚持下来,其中的艰辛也许只有经历过才能真正体会。
不过,经过一番实践后,当看到自己亲手做的东西就那么真实的摆在眼前,曾经的心血与付出终于有了回报,那份激动与喜悦的心情又岂是三言两语说得清楚的!
“如人饮水,冷暖自知”,现在我是真的体会到了。
先说实践。
关于实践,前人曾留有十二字箴言:
“实践是检验真理的唯一标准”,经过这次应用软件设计,我所理解的实践已远不只此。
人说“爱过才知情深,醉过方知酒浓”,我以为,只有实践才会出真知,没有实践,任何理论、见解都是苍白无力的。
眼之所见、心之所想大多数时候并不就是手之所为。
在动手尝试之前,我可以算是一个眼高手低的人。
课程设计的题目下来了,我做的是“进程同步”,本以为这是最简单的,基本上可以不费多大心力即轻松搞定。
因此开始的几天里也没怎么刻意着手这件事情。
事实却是,等到我真正做了才发现随着问题的不断出现和为此而查阅许多相关的资料,花了那么多的时间与精力,做的过程还是如此的困难重重,并且做出来的东西也并非尽善尽美。
方知许多事情并非都如人所想,不实践、不参与是无论如何也不会明白的。
实践之重要正在于此!
这段小小的经历使我感触很深,也教会我在以后的学习与工作中不要再眼高手低,任何事情都需亲自尝试后再做定断。
牢记“眼之所见、心之所想非手之所为也!
”
接下来再说着手前的准备。
三国时诸葛亮草船借箭有赖“万事俱备,只欠东风”,而我的设计能顺利进行也必须有充足的准备作为后盾。
只可惜在一开始的时候由于并不很重视因此也未意识到这一点,导致做的过程中停停找找、找找停停,严重影响了设计进度和效率,并且这种临阵磨枪式的做法也使得准备很不充分,往往是急需要用的东西找也找不到。
这样一来,自然会遇到重重困难。
我想,如果在动手之前已经做好了充足准备,必然会少遇到很多麻烦,也不会一度出现举步维艰的情况。
当然了,这次还只是一个小小的设计,如果换成是某个大型系统的设计岂不是无法想象?
所以这次经历也算是给了我一个教训:
千万不要打无准备的仗!
早准备方保无虞。
设计的过程行将结束,一个个挑灯夜战、激烈讨论的夜晚也已过去。
回顾整个过程,更清楚的认识到知识的欠缺,而自己所学的各种知识只能算是皮毛,还有更多的东西需要我去研究,去掌握。
尽管如此,我也不会退缩、停滞不前,因为通过这次设计实践,认识到了利用ubuntu的强大。
这才是最重要的。
相信经过此次课程设计,日后将会有所改进。
总的来说,我觉得这次设计实践收获颇丰,于今后的学业、步入社会后参加工作乃至做人做事都是一笔不小的财富!
通过这次课程设计,我懂得了实践的重要性、团队合作精神的可贵以及做事前的充足准备与做事过程中的坚持和细心谨慎对于高质高效地完成一项工作的特殊意义。
任何事情都有一个循序渐进的过程,知难而进、勇往直前,只有这样才有可能领略险峰的无限风光。
治学、做人又何尝不是如此呢?