第一篇:读者-写者 操作系统实验报告 计算机操作系统
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资源。
相比之下,考虑到运行效率,还是实例中给出的方法比较好写些。
第二篇:操作系统读者写者实验报告
目录
一 设计概述 ……………………………………………………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
第三篇:操作系统课程设计报告——读者写者问题
操作系统课程设计
课
题:读者写者问题 姓
名:赫前进 班
级: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);
} }
第四篇:操作系统实验报告
实验二
进程调度
1.目的和要求
通过这次实验,理解进程调度的过程,进一步掌握进程状态的转变、进程调度的策略,进一步体会多道程序并发执行的特点,并分析具体的调度算法的特点,掌握对系统性能的评价方法。
2.实验内容
阅读教材《计算机操作系统》第二章和第三章,掌握进程管理及调度相关概念和原理。
编写程序模拟实现进程的轮转法调度过程,模拟程序只对PCB进行相应的调度模拟操作,不需要实际程序。假设初始状态为:有n个进程处于就绪状态,有m个进程处于阻塞状态。采用轮转法进程调度算法进行调度(调度过程中,假设处于执行状态的进程不会阻塞),且每过t个时间片系统释放资源,唤醒处于阻塞队列队首的进程。
程序要求如下:
1)输出系统中进程的调度次序; 2)计算CPU利用率。
3.实验环境
Windows操作系统、VC++6.0 C语言
4设计思想:
(1)
程序中进程可用PCB表示,其类型描述如下:
struct PCB_type
{
int pid;
//进程名 int
state;
//进程状态
2——表示“执行”状态
1——表示“就绪”状态
0——表示“阻塞”状态
int cpu_time;//运行需要的CPU时间(需运行的时间片个数)
} 用PCB来模拟进程;
(2)设置两个队列,将处于“就绪”状态的进程PCB挂在队列ready中;将处于“阻塞”状态的进程PCB挂在队列blocked中。队列类型描述如下:
struct QueueNode{
struct PCB_type
PCB;
Struct QueueNode *next;} 并设全程量:
struct QueueNode *ready_head=NULL,//ready队列队首指针
*ready_tail=NULL , //ready队列队尾指针
*blocked_head=NULL,//blocked队列队首指针 *blocked_tail=NULL;//blocked队列队尾指针(3)设计子程序:
start_state();
读入假设的数据,设置系统初始状态,即初始化就绪队列和阻塞队列。
dispath();
模拟调度,当就绪队列的队首进程运行一个时间片后,放到就绪队列末尾,每次都是队首进程进行调度,一个进程运行结束就从就绪队列中删除,当到t个时间片后,唤醒阻塞队列队首进程。
calculate();
就绪进程运行一次,usecpu加1,当就绪队列为空时unusecpu加1,CPU利用率为use_cpu/(use_cpu+unuse_cpu)。
5源代码:
#include
struct PCB_type {
int pid;
//进程名
int
state;
//进程状态
//2--表示“执行”状态
//1--表示“就绪”状态
//0--表示“阻塞”状态
int cpu_time;//运行需要的CPU时间(需运行的时间片个数)};struct QueueNode{
struct PCB_type
PCB;
struct QueueNode *next;};struct QueueNode *ready_head=NULL,//ready队列队首指针
*ready_tail=NULL,//ready队列队尾指针
*block_head=NULL,//blocked队列队首指针
*block_tail=NULL;
//blocked队列队尾指针
int use_cpu,unuse_cpu;
void start_state()//读入假设的数据,设置系统初始状态 {
int n,m;
int i;
struct QueueNode *p,*q;
printf(“输入就绪节点个数n:”);
scanf(“%d”,&n);
printf(“输入阻塞节点个数m:”);
scanf(“%d”,&m);
p=(struct QueueNode *)malloc(sizeof(struct QueueNode));
p->next =NULL;
ready_head=ready_tail=p;
for(i=0;i { p=(struct QueueNode *)malloc(sizeof(struct QueueNode)); p->next =NULL; p->PCB.state=1; printf(“输入就绪进程%d的pid和cpu_time:”,i+1); scanf(“%d%d”,&p->PCB.pid,&p->PCB.cpu_time); ready_tail->next=p; ready_tail=p; } q=(struct QueueNode *)malloc(sizeof(struct QueueNode)); q->next =NULL; block_head=block_tail=q; for(i=0;i { q=(struct QueueNode *)malloc(sizeof(struct QueueNode)); q->next=NULL; q->PCB.state=0; printf(“输入阻塞进程%d的pid和cpu_time:”,i+1); scanf(“%d%d”,&q->PCB.pid,&q->PCB.cpu_time); block_tail->next=q; block_tail=q; } printf(“n处于就绪状态的进程有:n”); p=ready_head->next; i=1; while(p) {printf(“进程%d的pid和cpu_time:%5d%5d%5dn“,i,p->PCB.pid,p->PCB.state,p->PCB.cpu_time); p=p->next; i++; } } void dispath() //模拟调度 { int x=0,t; use_cpu=0; unuse_cpu=0; printf(”输入t:“); scanf(”%d“,&t); printf(”开始调度n“); while(ready_head!=ready_tail||block_head!=block_tail) { struct QueueNode *p,*q; if(ready_head!=ready_tail) { p=ready_head->next; ready_head->next=p->next; p->next=NULL; if(ready_head->next==NULL) { ready_tail=ready_head; } p->PCB.state=2; printf(”进程%d调度t“,p->PCB.pid); state和 use_cpu++; x++; p->PCB.cpu_time--; if(p->PCB.cpu_time) { ready_tail->next=p; ready_tail=p; } else { printf(”进程%d完成t“,p->PCB.pid); free(p); } } else { unuse_cpu++; x++; printf(”空闲一个时间片t“); } if(x==t&&block_head!=block_tail) { q=block_head->next; block_head->next=q->next; q->next=NULL; if(block_head->next==NULL) { block_tail=block_head; } ready_tail->next=q; ready_tail=q; x=0; } } } void calculate() //计算CPU利用率 { printf(”ncpu的利用率%.2fn“,(float)use_cpu/(use_cpu+unuse_cpu)); } void main(){start_state(); dispath(); calculate();} 6运行结果: 7实验总结: 实验帮我复习了数据结构和C语言,且巩固课本知识,知道了如何定义结构体,如何在链接队列中增删节点。模拟进程调度帮我们巩固了进程三状态之间的变迁。懂得调式的重要性。总之,我们明白了理论联系实际。多看书,多上机。 实验三 可变分区存储管理 1.目的和要求 通过这次实验,加深对内存管理的认识,进一步掌握内存的分配、回收算法的思想。 2.实验内容 阅读教材《计算机操作系统》第四章,掌握存储器管理相关概念和原理。编写程序模拟实现内存的动态分区法存储管理。内存空闲区使用自由链管理,采用最坏适应算法从自由链中寻找空闲区进行分配,内存回收时假定不做与相邻空闲区的合并。 假定系统的内存共640K,初始状态为操作系统本身占用64K。在t1时间之后,有作业A、B、C、D分别请求8K、16K、64K、124K的内存空间;在t2时间之后,作业C完成;在t3时间之后,作业E请求50K的内存空间;在t4时间之后,作业D完成。要求编程序分别输出t1、t2、t3、t4时刻内存的空闲区的状态。 3.实验环境 Windows操作系统、VC++6.0 C语言 4.设计思想 模拟内存分配和回收,要设置两个链队列,一个空闲区链和一个占用区链,空闲区链节点有起始地址,大小和指向下一节点的指针等数据域,占用区链节点有起始地址,大小,作业名和指向下一节点的指针等数据域,本实验用最坏适应算法,每次作业申请内存都是从空闲链队头节点分配,如果相等,就删除空闲头结点,如果小于申请的,就不分配,否则就划分内存给作业,剩下的内存大小,重新插入空闲链队,按从大到小,接着把作业占用的内存放到占用区链节点的末尾。每次作业运行完,就要回收其占用的内存大小,把作业节点按从大到小插入到空闲链队中。5.源代码: #include struct freelinkNode *next;};struct busylinkNode{ char name; int len;int address;struct busylinkNode *next;};struct freelinkNode *free_head=NULL; //自由链队列(带头结点)队首指针 struct busylinkNode *busy_head=NULL; //占用区队列队(带头结点)首指针 struct busylinkNode *busy_tail=NULL; //占用区队列队尾指针 void start(void)/* 设置系统初始状态*/ { struct freelinkNode *p; struct busylinkNode *q; free_head=(struct freelinkNode*)malloc(sizeof(struct freelinkNode)); free_head->next=NULL;// 创建自由链头结点 busy_head=busy_tail=(struct busylinkNode*)malloc(sizeof(struct busylinkNode)); busy_head->next=NULL;// 创建占用链头结点 p=(struct freelinkNode *)malloc(sizeof(struct freelinkNode)); p->address=64; p->len=640-64;//OS占用了64K p->next=NULL; free_head->next=p; q=(struct busylinkNode *)malloc(sizeof(struct busylinkNode)); q->name='S';/* S表示操作系统占用 */ q->len=64;q->address=0;q->next=NULL; busy_head->next=q;busy_tail=q;} void requireMemo(char name, int require)/*模拟内存分配*/ { freelinkNode *w,*u,*v;busylinkNode *p;if(free_head->next->len>=require){ p=(struct busylinkNode*)malloc(sizeof(struct busylinkNode)); p->name=name; p->address=free_head->next->address; p->len=require; p->next=NULL; busy_tail->next=p; busy_tail=p;} else printf(”Can't allocate“); w=free_head->next; free_head->next=w->next; if(w->len==require) { free(w);} else { w->address=w->address+require; w->len=w->len-require;} u=free_head; v=free_head->next; while((v!=NULL)&&(v->len>w->len)){ u=v; v=v->next;} u->next=w; w->next=v;} void freeMemo(char name)/* 模拟内存回收*/ { int len; int address;busylinkNode *q,*p;freelinkNode *w,*u,*v;q=busy_head; p=busy_head->next; while((p!=NULL)&&(p->name!=name)) { q=p; p=p->next;} if(p==NULL){ printf(”%c is not exist“,name);} else { if(p==busy_tail) { busy_tail=q; } else { q->next=p->next; len=p->len; address=p->address; free(p); w=(struct freelinkNode*)malloc(sizeof(struct freelinkNode)); w->len=len; w->address=address; u=free_head; v=free_head->next; while((v!=NULL)&&(v->len>len)) { u=v;v=v->next; } u->next=w; w->next=v; } } } void past(int time)/* 模拟系统过了time 时间*/ { printf(”过了时间%d后:n“,time);} void printlink()/* 输出内存空闲情况(自由链的结点)*/ { freelinkNode *p; printf(”内存的空闲情况为:n“); p=(struct freelinkNode *)malloc(sizeof(struct freelinkNode)); p=free_head->next; while(p!=NULL) { printf(”内存的起始地址和内存的大小%5dt%5d:n",p->address,p->len); p=p->next; } } void main(){ int t1=1,t2=2,t3=3,t4=4; start(); past(t1); requireMemo('A',8); requireMemo('B',16); requireMemo('C',64); requireMemo('D',124); printlink(); past(t2); freeMemo('C'); printlink(); past(t3); requireMemo('E',50); printlink(); past(t4); freeMemo('D'); printlink();} 6.运行结果: 7.实验总结: 巩固编程能力,和调式能力,复习课本知识,明白理论联系实际的重要性,动手能力非常重要,多看书,多独立思考,品味痛苦的过程,享受成功的喜悦。 操作系统实验报告 院系:数计学院 班级:大类6班 学号:100511624 姓名:明章辉 指导教师:徐军利 许昌学院 《操作系统》实验报告书 学号:姓名:闫金科班级:成绩: 5006140057 14物联网工程 2016年02月实验一 Linux的安装与配置 一、实验目的 1.熟悉Linux系统的基本概念,比如Linux发行版、宏内核、微内核等。2.掌握Linux系统的安装和配置过程,初步掌握Linux系统的启动和退出方法。3.熟悉Linux系统的文件系统结构,了解Linux常用文件夹的作用。 二、实验内容 1.从网络上下载VMware软件和两个不同Linux发行版镜像文件。2.安装VMware虚拟机软件。 3.在VMware中利用第一个镜像文件完成第一个Linux的安装,期间完成网络信息、用户信息、文件系统和硬盘分区等配置。 4.在VMware中利用第二个镜像文件完成第二个Linux的安装,并通过LILO或者GRUB解决两个操作系统选择启动的问题。 5.启动Linux系统,打开文件浏览器查看Linux系统的文件结构,并列举出Linux常用目录的作用。 三、实验过程及结果 1、启动VMware,点击新建Linux虚拟机,如图所示: 2、点击下一步,选择经典型,点击下一步在选择客户机页面选择Linux,版本选择Red Hat Enterprise Linux 5,如图所示: 3、点击下一步创建虚拟机名称以及所要安装的位置,如图所示: 4、点击下一步,磁盘容量填一个合适大小,此处选择默认值大小10GB,如图所示: 5、点击完成,点击编辑虚拟机设置,选择硬件选项中的CD-ROM(IDE...)选项,在右侧连接中选择“使用ISO镜像(I)”选项,点击“浏览”,找到Linux的镜像文件,如图所示: 6点击确定按钮后,点击启动虚拟机按钮,来到Linux的安装界面,如图所示: 7、到此页面之后,等待自动检测安装,如图所示: 8、等到出现如图所示页面后点击“skip”按钮,跳过检测,直接进入安装设置界面,如图所示: 9、安装设计界面如图所示: 10、点击Next按钮进入设置语言界面,设置语言为“简体中文”,如图所示: 11、点击Nest按钮进入系统键盘设置按钮,设置系统键盘为“美国英语式”,如图所示: 12、点击下一步按钮,弹出“安装号码”对话框,选择跳过输入安装号码,如图所示: 13、按照提示,一直点击下一步按钮,如图所示: 14、到设置最后一步,点击下一步按钮进入开始安装Red Hat Enterprise Linux Sever界面,如图所示: 15、安装完成后,进入欢迎界面,按照提示点击前进按钮知道进入Linux桌面,如图所示: 16、安装成功的Linux系统桌面如图所示,桌面包含五个图标,分别为:计算机、jk’s Home、回收站、RHEL/5.3 i386DVD。 四、实验总结 通过安装虚拟机等操作让我认识到Linux这系统一些基本特点,本次试验学会了安装虚拟机并且使用虚拟机安装操作系统,掌握了红帽Linux系统的安装和配置过程,以及对镜像ISO文件的使用,有别于我们机器上使用的系统,通过虚拟机这个软件还可以在已有系统的基础上使用其他操作系统。安装过程中一定要注意选择版本的时候要选择Red Hat Enterprise Linux 5版本,否则安装不能成功。自己动手成功的安装了Linux系统,自己对Linux的学习产生更大的兴趣。 实验二 Linux操作系统的运行模式 一、实验目的 1.熟悉Linux系统终端工作环境的使用,了解Linux命令的格式,使用学会利用常用的Linux命令来完成系统的管理和维护。 2.了解X-Windows的特点,熟悉Linux图形用户接口的使用,掌握GNOME桌面环境的基本操作。 3.了解和掌握在Linux环境下安装软件包的方法,如QQ for Linux等用软件的安装方法。 二、实验内容 1.启动Linux系统打开虚拟终端界面,使用Linux的在线帮助指令man或help获得ls、uname、date、cal、mkdir、cp等Linux命令的帮助手册,了解这些命令的具体使用方法。同时,也可以通过执行“命令名 –help”来显示该命令的帮助信息,如“ls –help”,试用这些命令。 2.通过uname命令的执行,查看并给出相关系统信息:操作系统的名称、系统域名、系统CPU名称等。 3.在主目录下创建一个名为myetc的子目录,将/etc目录下与网络相关的文件和子目录拷贝到该目录,并将这些文件的执行权限设置为可执行。 4.在主目录/home下创建目录program、music 和temp,然后在program下建立目录java和C,列出完成该过程的所有命令。 5.在图形界面环境中,查看GNOME桌面的面板和桌面,设置GNOME,包括屏幕保护程序、更改背景和指定关联程序等。6.实现对光盘的加载和访问,然后卸载。 三、实验过程及结果 1、打开终端,输入 【ls –help】来查看【ls】指令的使用方法,同理查看uname、date、cal、mkdir、cp的使用方法。 2、在终端中输入【uname –a】显示操作系统名系统cpu名和系统域名 3、重启系统,用【root】用户名进入系统,以获得权限。在终端中输入【mkdir myetc】,在主目录下创建【myrtc】的目录,【ls】查看是否创建。输入【cd..】返回至【/】文件,输入【cp –r etc root/myetc】讲etc中内容复制到myetc中,进入myetc文件【ls】查看。输入 【chmod u+x etc】赋予文件可执行的权限,输入【ll】查看。 4、在home下,输入【mkdir {program,music,temp}】,可在home下创立这三个目录,输入【ls】查看。在program下输入【mkdir{java,C}】,可创立java和C两个目录,【ls】查看。 5、在桌面上方选择【系统】-【首选项】,即可设置屏幕保护程序和更改背景和指定关联程序 5、在桌面上可见看到有CD光盘,双击浏览,右键【弹出】即卸载。 四、实验总结和体会 Linux的指令系统是学习Linux操作系统很重要的一部分,指令系统相当于在Windows操作系统下的doc,可以省去图形化界面。通过这次的实验让我了解了Linux的强大功能,了解到Linux有许多方便快捷的设置基本配置的方法,这使我更喜欢上Linux的使用。在使用指令的过程中,有时候对文件的操作需要一定的权限,这时需要在登陆时用户名使用【root】,而不是我们在安装时使用的用户名,这样就获得了管理员权限,可以对一些系统文件进行操作。 实验三 Linux应用软件与系统管理 一、实验目的 1.了解OpenOffice.Org集成办公软件,掌握利用OpenOffice.Org的套件来完成文档和图片的处理。 2.了解Linux网络管理的知识,熟悉Linux网络配置的方法,掌握在Linux环境下配置Web服务器和ftp服务的方法。 二、实验内容 1.配置Linux系统的网络环境,安装FTP和Web服务器,并配置相关的属性,利用FTP实现WINDOWS和Linux之间的数据交换。 2.利用FTP程序上传自己的照片到FTP服务器,利用OpenOffice的文字处理工具OpenOffice Writer制作一份表格形式的个人简历。个人简历中至少包含学号、姓名、性别、专业、照片和学习经历等内容,并保存为网页格式(html格式)。3.将个人简历网页设置为WEB服务器的首页,然后在客户端利用浏览器访问WEB服务器,查看效果。 4.通过读取proc文件系统,获取系统各种信息(如主机名、系统启动时间、运行时间、版本号、所有进程信息、CPU使用率等),并以比较容易的方式显示。 三、实验过程及结果 1.配置网络环境:在(服务.cmd).里面进行以下操作:在服务里选择3按回车 完成后,可在本地连接看到VMware已连接上网络 在虚拟机设置中设置以太网网络连接方式为 网关地址填虚拟机的网管,IP地址设为虚拟机的一个子网: 四、总结: 在linux系统下,make是我们经常用到的编译命令,所以关于make代码和他的操作指令一定要记清楚。所以,熟练掌握了make和makefile工具之后,源码安装软件就变的像windows下安装软件一样简单。 实验四 进程控制与管理 一、实验目的 1.掌握GCC编译器的用法,学会利用GCC编辑器来编辑C语言程序,学会利用GDB调试器来调试C语言程序。 2.理解进程和程序的区别和联系,3.掌握在Linux环境下观察进程运行情况和CPU工作情况的命令。4.了解fork()系统调用,掌握利用fork()创建进程的方法。 5.了解Linux系统其他与进程相关的系统调用,如exec、wait和exit等。6.了解Linux常用的进程通信机制。 二、实验内容 1.利用Linux的进程管理命令ps、top来监视和跟踪进程,体会进程和程序的关系。2.利用Linux的文字编辑器编写文件复制的C语言程序,并用gcc编译该程序,然后运行该程序。 3.编写一段程序,使用系统调用fork()创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示'a',子进程分别显示字符'b'和字符'c'。试观察记录屏幕上的显示结果,并分析原因。 4.修改上述程序,每一个进程循环显示一句话。子进程显示'daughter „'及'son „„',父进程显示 'parent „„',观察结果,分析原因。5.用fork()创建一个进程,再调用exec()用新的程序替换该子进程的内容。 三、实验过程及结果 1、利用Linux的进程管理命令ps、top来监视和跟踪进程,体会进程和程序的关系。<1>从用户身份切换到ROOT身份 <2>输入命令 ps 查看进程 <2>输入命令 top 跟踪进程 2、利用Linux的文字编辑器编写一个计算机100个自然数和的C语言程序,并用gcc编译该程序,然后运行该程序。 <1>创建一个.C文件 并进入进行编辑 <2>用GCC 进行编译,再查看文件,发现产生执行文件 a.out <3>执行这个可执行文件得到结果5050 1、编写一段程序,使用系统调用fork()创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示'a',子进程分别显示字符'b'和字符'c'。试观察记录屏幕上的显示结果,并分析原因。 <1>穿件一个.C文件 并进行编写程序代码 <2>反复执行2次该程序 <3>可以看出两次执行的结果 a b c 出现的顺序不同,原因是,3个进程的输出次序是随机的,并不会按规定的顺序出现,所以会出现上述结果。 4、修改上述程序,每一个进程循环显示一句话。子进程显示'daughter „'及'son „„',父进程显示 'parent „„',观察结果,分析原因。<1>重新修改代码 <3>执行这段程序 <4>原分析: 因和之前一样,可以看出执行的结果 3个单词出现的顺序不同,原因是,3个进程的输出次序是随机的,并不会按规定的顺序出现,所以会出现上述结果。 5、用fork()创建一个进程,再调用exec()用新的程序替换该子进程的内容。<1> 编写代码 <2> 执行的结果 结果表明 execl 替代了son的内容 四、实验总结和体会 这个实验考察的是进程之间存在很多可能性以及对编辑器的使用。本次实验学习了在linux环境下用gcc编译器运行c语言程序,在linux环境下编写程序用到了vi编辑器,知道了该编辑器也需要各种命令来操作。编写C语言程序时用到了fork()函数,再调用execl()用新的程序替换该子进程的内容。 实验五 进程调度模拟程序的设计与实现 一、实验目的 1.了解进程调度的概念,掌握常用进程调度算法的原理。2.掌握Linux程序设计编辑、编译和调试的技巧。 二、实验内容 1.编写程序实现进程调度调度算法先来先服务、优先级高优先和时间片轮转调度算法。(编程语言不限) 2.输入数据,输出运行结果。 三、实验过程及结果 1先来先服务 #i nclude struct { int id; float ArriveTime;float RequestTime;float StartTime;float EndTime;float RunTime;float DQRunTime;int Status;}arrayTask[4];GetTask(){ int i;float a; for(i=0;i<4;i++){arrayTask[i].id=i+1;printf(“input the number”); printf(“input the the ArriveTime of arrayTask[%d]:”,i);scanf(“%f”,&a); arrayTask[i].ArriveTime=a; printf(“input the RequestTime of arrayTask[%d]:”,i);scanf(“%f”,&a); arrayTask[i].RequestTime=a;arrayTask[i].StartTime=0;arrayTask[i].EndTime=0;arrayTask[i].RunTime=0;arrayTask[i].Status=0; } } int fcfs() { int i,j,w=0; for(i=0;i<4;i++) { if(arrayTask[i].Status==0) { t=arrayTask[i].ArriveTime; w=1; } if(w==1) break; } for(i=0;i<4;i++) { if(arrayTask[i].ArriveTime t=arrayTask[i].ArriveTime; } for(i=0;i<4;i++) { if(arrayTask[i].ArriveTime==t) return i; } } int sjf(){ int i,x=0,a=0,b=0;float g; for(i=0;i<4;i++){ if(arrayTask[i].Status==1){g=arrayTask[i].EndTime;x=1;} } if(x==0){ t=arrayTask[0].ArriveTime; for(i=0;i<4;i++){ if(arrayTask[i].ArriveTime t=arrayTask[i].ArriveTime;a=i;} } return a;} else { for(i=0;i<4;i++){ if(arrayTask[i].EndTime>g)g=arrayTask[i].EndTime;} for(i=0;i<4;i++){ if(arrayTask[i].Status==0&& arrayTask[i].ArriveTime<=g){ t=arrayTask[i].RequestTime;a=i;b=1;} /*判断有没有进程在前个进程完成前到达*/ } if(b!=0)/*有进程到达则按SJF*/ { for(i=0;i<4;i++){ if(arrayTask[i].Status==0&&arrayTask[i].ArriveTime<=g&&arrayTask[i].RequestTime return a;} else{ /*否则按FCFS*/ for(i=0;i<4;i++) {if(arrayTask[i].Status==0)t=arrayTask[i].ArriveTime;} for(i=0;i<4;i++){ if(arrayTask[i].Status==0&&arrayTask[i].ArriveTime return a;} } } new(int s)/*定义执行进程后相关数据的修改*/ { int i,g=0;for(i=0;i<4;i++){ if(arrayTask[i].Status==0)continue;else { g=1;break;} } if(g==0)/*当处理的是第一个未执行的进程时执行*/ { arrayTask[s].StartTime=arrayTask[s].ArriveTime; arrayTask[s].EndTime=arrayTask[s].RequestTime+arrayTask[s].ArriveTime;arrayTask[s].RunTime=arrayTask[s].RequestTime;arrayTask[s].Status=1;g=2;} if(g==1)/*当处理的不是第一个未执行的进程时执行*/ { arrayTask[s].Status=1;for(i=0;i<4;i++){ if(arrayTask[i].Status==1)d=arrayTask[i].EndTime;} for(i=0;i<4;i++)/*查找最后执行的进程的完成时间*/ { if(arrayTask[i].EndTime>d&&arrayTask[i].Status==1)d=arrayTask[i].EndTime;} if(arrayTask[s].ArriveTime arrayTask[s].StartTime=arrayTask[s].ArriveTime; arrayTask[s].EndTime=arrayTask[s].StartTime+arrayTask[s].RequestTime;arrayTask[s].RunTime=arrayTask[s].EndTime-arrayTask[s].ArriveTime;} arrayTask[s].DQRunTime=arrayTask[s].RunTime/arrayTask[s].RequestTime;} Printresult(int j)/*定义打印函数*/ { printf(“%dt”,arrayTask[j].id); printf(“%5.2ft”,arrayTask[j].ArriveTime);printf(“%5.2ft”,arrayTask[j].RequestTime);printf(“%5.2ft”,arrayTask[j].StartTime);printf(“%5.2ft”,arrayTask[j].EndTime);printf(“%5.2ft”,arrayTask[j].RunTime);printf(“%5.2fn”,arrayTask[j].DQRunTime);} main(){ int i,b,k,a,c=0;int d[4];clrscr(); printf(“t F.FCFS n”);printf(“t S.SFJ n”);printf(“t Q.EXIT n”);for(i=0;;i++){ if(c)break; printf(“please input the number a:n”);scanf(“%d”,&a);switch(a){ case Q: c=1;break; case F:printf(“please input the different-ArriveTime of arrayTasksn”);GetTask(); printf(“*****************************the result of fcfsn”);printf(“NumbertArrivetServertStarttFinishtTurnovetTake power turnover timen”); for(b=0;b<4;b++)/*调用两个函数改变结构体数的值*/ { k=fcfs();d[b]=k;new(k);} for(b=0;b<4;b++) Printresult(d[b]);/*调用打印函数打出结果*/ continue; case S: printf(“please input the different-RequestTime of array Tasksn”);GetTask(); printf(“******************************the result of sjfn”);printf(“NumbertArrivetRequesttStarttEndtRuntDQRun timen”);for(b=0;b<4;b++){ k=sjf();d[b]=k;new(k);} for(b=0;b<4;b++)Printresult(d[b]);continue; default:printf(“the number Error.please input another number!n”);} } } 四、实验总结和体会 通过做本实验,让我对进程或作业先来先服务、高优先权、按时间片轮转调度算法以及进程调度的概念和算法,有了更深入的认识!理解进程的状态及变化,动态显示每个进程的当前状态及进程的调度情况。进程调度是处理机管理的核心内容。优先级高优先是根据作业的优先级,总是选择优先级最高者进入队列。轮转调度算法是调度程序每次把CPU分配给就绪队列首进程/线程使用规定的时间间隔,就绪队列中都路保留巡行一个时间片。第五篇:操作系统实验报告