数学与信息技术学院1南京晓庄学院《操作系统》实验报告指导老师:专业班级:学号:姓名:完成日期:数学与信息技术学院2数学与信息技术学院一、实验概述1.实验目的深入了解掌握进程的同步、互斥机制,认识理解其调度过程,并用于解决实际生产者/消费者问题。使用高级编程语言现“生产者——消费者”进程同步问题。2.实验目标(1)对课本知识加以巩固,在实践中提高动手能力;(2)完整的完成此次实验,并有所创新与突破;(3)独立思考,充分利用网络资源与图书资源;(4)取得一个被老师认可的高分评价;二、实验工具1.操作系统:windows2003serverenterprise2.开发环境:VisualStudio2010三、实验过程1、实验具体题目,分析题目:本实验要求利用PV操作实现解决生产者——消费者问题中的同步问题。此问题描述的是一群生产者进程在生产产品并将这些产品提供给消费者进程去消费,在两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将它所生产的产品放入一个缓冲区,消费者进程可从缓冲区中取走产品去消费,但它们之间必须保持同步,即不允许消费者进程到一个空缓冲区去取产品,也不允许生产者进程向一个已装满且尚未取出的缓冲区中投放产品,并且生产者消费者互斥使用缓冲区。2、算法流程:数学与信息技术学院33、算法实现主要用到生产者函数RP-ProceducerThread(void*p)来实现缓冲区产品数量的增加,用RP-ConsumerThread(void*p)来实现缓冲区产品的减少。并用到了CreateThread函数来创建生产者消费者线程,利用线程的句柄以及创建线程是立刻运行的特点来进行生产消费操作。至于PV算法的实现是利用buffer_empty和buffer_full来进行控制,buffer_empty的值可以看做资源量,只有空的数值大于0才可以进行生产,buffer_full的数值与buffer_empty的值有对应的关系,利用buffer_full来控制消费的进行。最后,在缓冲区操作临界资源PC_Buffer来说利用EnterCriticalSection(&PC_Buffer);//等待共享资源LeaveCriticalSection(&PC_Buffer);//释放共享资源来进行缓冲区操作的控制。总之当程序读入测试文件中的数据时,便根据读入的字符时C还是P创造相应的进程,在分配EMPTY或者FULL资源,然后等待共享资源PC_Buffer,得到后便进行操作。最后读入所有的数据,完成所有进程的操作。4、需要解决的问题:利用生产者进程进行生产,同时消费者进程也能进行消费,但是必须满足同步的条件才可以允许,否则将提示缓冲区满无法进行生产或者缓冲区空无法进行消费的错误,故程序应该具有判断的功能。若结束当前的生产者——消费者进程,将会提示此次进程中生产消费者分别生产了和消费的产品数目,并统计缓冲数学与信息技术学院4区中剩余的产品数目,最后才结束。5、关键代码分析:1、做出如下定义CRITICAL_SECTIONPC_Buffer;//临界区HANDLEh_semaphore_empty,h_semaphore_full;//信号量对象structBuffer{charbuf[BUFFER_LEN];}Buf[MAX_THREAD_NUM];intin=0;//生产者生产指针intout=0;//消费者消费指针SYSTEMTIMEtimeSys;CRITICAL_SECTIONPC_Buffer;//临界区HANDLEh_semaphore_empty,h_semaphore_full;//信号量对象structThreadInfo{intserial;//线程序号charentity;//线程类别(P-生产者线程,C-消费者线程)doubledelay;//生产产品/延续时间doublepersist;//线程把产品加入到缓冲区/持续时间};2①CreateThread函数创建一个在调用进程的地址空间中执行的线程。此函数为API函数,用于创建生产者消费者进程。②信号量对象(semaphore)信号量对象实现了Dijkstra定义中的通用信号量语义。信号量对象就是资源信号量,初始值的取值在0到指定最大值之间,用于限制并发访问的线程数,数学与信息技术学院5也可用于进程、线程间的同步。它的相关API包括:CreateSemaphore、OpenSemaphore和ReleaseSemaphore。(1)CreateSemapore函数是创建一个有名或者无名信号量对象,在输人参数中指定最大值和初值,返回对象句柄。格式:HANDLECreateSemaphore(LPSECURITY_ATTRIBUTESlpAttributes,LONGlInitialCount,LONGlMaximumCount,LPCTSTRlpName);1pAttributes:安全属性。如果是NULL就表示要使用默认属性。1InitialCount:Semaphore的初值。必须≥0,并且≤MaximumCount。lMaximumCount:Semaphore的最大值。这也就是在同一时间内能够锁住Semaphore之线程的最多个数。1pName:Semaphore的名称(一个字符串)。任何线程(或进程)都可以根据这一名称引用到这个Semaphore。这个值可以是NULL,意思是产生一个没有名字的Semaphore。返回值:如果成功就传回一个handle,否则传回NULL③等待操作Windows2000为对象提供了两个统一的等待操作函数WaitForSingleObject和WaitForMultipleObjiects,等待函数被同步对象用于实现各种Dijkstra定义的P操作。等待的对象包括:Changenotification(改变通告);Consoleinput(控制台输入);Event(事件);Job(作业);Mutex(互斥对象);Process(进程);Semaphore(信号量);Thread(线程);Waitabletimer(可等待定时器)。函数决定等待条件是否被满足。如果等待条件并没有被满足,调用线程进入一个高效的等待状态,当等待满足条件时占用非常少的处理器时间。在运行前,一个等待函数修改同步对象类型的状态。修改仅发生在引起函数返回的对象身上。例如,信号的计数减1。一个线程通过调用等待函数拥有对象。创建该对象的线程也拥有对象,而不需要调用等待函数。(1)WaitForSingleObject函数可在指定的时间内等待指定对象为可用状态当下列情况之一发生时该函数返回:(1)指定对象处于信号态;(2)超时。格式:DWORDWaitForSingleObject(HANDLEhHandle,DWORDdwMilliseconds);hHandle:等待对象句柄。dwMilliseconds:指定以毫秒为单位的超时间隔。如果超时,即使对象的状态是非信号态的并且没有完成,函数也返回。如果它是0,函数测试对象的状态数学与信息技术学院6并立刻返回;如果它是INFINITE(定义为0xFFFFFFFF或-1),函数从不超时。返回值:如果函数调用成功,返回值表明引起函数返回的事件。可能值如下:WAIT_ABANDONED:指定对象是互斥对象,在线程被终止前,线程没有释放互斥对象。互斥对象的所属关系被授予调用线程,并且该互斥对象被置为非信号态。WAITOBJECT_0:指定对象的状态被置为信号态。WAIT_TIMEOUT:超时,并且对象的状态为非信号态。如果函数调用失败,返回值是WAIT_FAILED。(2)WaitForMultipleObjects函数可在指定的时间内等待多个对象为可用状态。格式:DWORDWaitForMultipleObjects(DWORDnCount,CONSTHANDLE*lpHandles,BOOLbWaitAll,DWORDdwMilliseconds);nCount规定了可引起函数阻塞的一组对象的句柄数目。lpHandles指向存放一组句柄的数组。bWaitAll规定了是否函数应该等待一组对象都发送出有信号通知(bWaitAll=TRUE),或者只是等待一个对象(bWaitAll=FLASE)。dwMilliseconds,它同在WaitForSingleObiect一样。④临界区对象临界区对象只能用于在同一个进程内使用的临界区,同一个进程内各线程对它的访问是互斥进行的,临界区对象的运行速度比互斥对象快。把变量说明为CRITICAL_SECTION类型,就可作临界区使用。相关的API包括InitializeCriticalSection、EnterCriticalSection、TryEnterCriticalSection、LeaveCriticalSection和DeleteCriticalSection。(1)InitializeCriticalSection函数初始化临界区对象。格式:VOIDInitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);lpCriticalSection:指向临界区对象的指针。(2)EnterCriticalSection函数是等待指定临界区对象的所有权。当调用线程被数学与信息技术学院7赋予所有权时,该函数返回。格式:VOIDEnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);lpCriticalSection:指向临界区对象的指针。(3)LeaveCriticalSection函数释放指定临界区对象的所有权格式:VOIDLeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);1pCriticalSection:指向临界区对象的指针。(4)DeleteCriticalSection释放与临界区对象相关的所有系统资源。(5)临界区对象的应用例进程通过声明CRITICAL_SECTION类型的变量来完成分配临界区对象使用的存储空间,使用InitializeCriticalSection来初始化该临界区对象。线程在进入临界区之前使用EnterCriticalSection等待占用临界区的使用权,线程在退出临界区之后马上使用LeaveCriticalSection释放临界区的使用权。利用临界区对象实现一个进程的各线程互斥的程序如下:……CRITICAL_SECTIONPC_Buffer;structThreadInfo{……};voidRP_ProceducerThread(void*p){……EnterCriticalSection(&PC_Buffer);//等待共享资源……(临界区)//生产者把产品加入到缓冲区LeaveCriticalSection(&PC_Buffer);//释放共享资源……}intmain(intargc,char*argv[])数学与信息技术学院8{HANDLEh_Thread;DWORDthread_ID;ThreadInfothread_info[MAX_THREAD_NUM];InitializeCriticalSection(&PC_Buffer);……h_Thread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_ProceducerThread),&thread_info[i],0,&thread_ID);……}⑤生产者消费者函数voidRP_ProceducerThread(void*p){DWORDm_delay;//生产产品延续时间DWORDm_persist;//把产品加入到缓冲区持续时间intm_serial;//线程序号DWORDwait_for_semaphore_empty;//等待资源信号量