死循环
在程序中加入跟踪语句,或调试运行程序,同时参考MSDN中的帮助文件CreateProcess()的使用方法,理解父子进程如何传递参数。给出程序执行过程的大概描述:
产生的应用程序的名称 (本EXE文件), 告诉其行为像一个子进程的标志,不继承句柄,使用新的控制台,新的环境,启动信息,返回的进程信息。
步骤4:填空
CreateProcess() 函数有__8个核心参数?本实验程序中设置的各个参数的值是: a. szFilename; b. szCmdLine,; c. NULL; d. NULL; e. FALSE;
f. CREATE_NEW_CONSOLE; g. NULL; h. NULL。
步骤5:按源程序中注释中的提示,修改源程序2-2,编译执行。运行结果:
步骤6:参考MSDN中的帮助文件CreateMutex()、OpenMutex()、ReleaseMutex()和WaitForSingleObject()的使用方法,理解父子进程如何利用互斥体进行同步的。给出父子进程同步过程的一个大概描述: 首先,进程创建一个互斥体,打开互斥体,如遇到互斥,则进行处理,处理完后,释放互斥体,下面便是进程等待下一个要处理的项目。
11
3、 实验结论
每个进程都从调用CreateProcess() API函数开始,该函数的任务是在对象管理器子系统内初始化进程对象。每一进程都以调用ExitProcess() 或TerminateProcess() API函数终止。通常应用程序的框架负责调用 ExitProcess() 函数。进程都是有始有终,其中有中断,还有处理进程间互斥的函数,已达到进程的完成后自然终止。
实验三 进程同步的经典算法
背景知识
Windows 2000提供的常用对象可分成三类:核心应用服务、线程同步和线程间通讯。其中,开发人员可以使用线程同步对象来协调线程和进程的工作,以使其共享信息并执行任务。此类对象包括互锁数据、临界段、事件、互斥体和信号等。
多线程编程中关键的一步是保护所有的共享资源,工具主要有互锁函数、临界段和互斥体等;另一个实质性部分是协调线程使其完成应用程序的任务,为此,可利用内核中的事件对象和信号。
在进程内或进程间实现线程同步的最方便的方法是使用事件对象,这一组内核对象允许一个线程对其受信状态进行直接控制 (见表3-1) 。
而互斥体则是另一个可命名且安全的内核对象,其主要目的是引导对共享资源的访问。拥有单一访问资源的线程创建互斥体,所有想要访问该资源的线程应该在实际执行操作之前获得互斥体,而在访问结束时立即释放互斥体,以允许下一个等待线程获得互斥体,然后接着进行下去。
与事件对象类似,互斥体容易创建、打开、使用并清除。利用CreateMutex() API可创建互斥体,创建时还可以指定一个初始的拥有权标志,通过使用这个标志,只有当线程完成了资源的所有的初始化工作时,才允许创建线程释放互斥体。
表3-1 用于管理事件对象的API
API名称 描述 12
在内核中创建一个新的事件对象。此函数允许CreateEvent(有安全性设置、手工还是自动重置的标志以及) 初始时已接受还是未接受信号状态的标志 创建对已经存在的事件对象的引用。此API函OpenEvent() 数需要名称、继承标志和所需的访问级别 SetEvent() 将手工重置事件转化为已接受信号状态 ResetEvent() 将手工重置事件转化为非接受信号状态 将自动重置事件对象转化为已接受信号状态。PulseEvent() 当系统释放所有的等待它的线程时此种转化立即发生
为了获得互斥体,首先,想要访问调用的线程可使用OpenMutex()
API来获得指向对象的句柄;然后,线程将这个句柄提供给一个等待函数。当内核将互斥体对象发送给等待线程时,就表明该线程获得了互斥体的拥有权。当线程获得拥有权时,线程控制了对共享资源的访问——必须设法尽快地放弃互斥体。放弃共享资源时需要在该对象上调用ReleaseMute() API。然后系统负责将互斥体拥有权传递给下一个等待着的线程 (由到达时间决定顺序) 。 1、实验目的
1) 回顾系统进程、线程的有关概念,加深对Windows 2000线程的理解。
2) 了解互斥体对象,通过对生产者消费者等进程间同步与互斥经典算法的实现,加深对P、V原语以及利用P、V原语进行进程间同步与互斥操作的理解。
2、实验内容和步骤
(1). 生产者消费者问题
步骤1:创建一个“Win32 Consol Application”工程,然后拷贝清单3-1中的程序,编译成可执行文件。
步骤2:在“命令提示符”窗口运行步骤1中生成的可执行文件。
13
运行结果:
范例:E:\\课程\\os课\\os实验\\程序\\os11\\debug>os31 (假设编译生成的可执行文件是os31.exe)
Producing 1 ... Succeed
Appending a product ... Succeed 0: 1 <-- 消费 1: 0 <-- 生产
Producing 2 ... Succeed
Appending a product ... Succeed 0: 1 <-- 生产 <-- 消费 1: 2
Taking a product ... Succeed 0: 0 <-- 生产 1: 2 <-- 消费
Consuming 1 ... Succeed
Taking a product ... Succeed 0: 0 <-- 生产 <-- 消费 1: 0
Consuming 2 ... Succeed
Producing 3 ... Succeed
Appending a product ... Succeed 0: 3 <-- 消费 1: 0 <-- 生产
Producing 30 ... Succeed
Appending a product ... Succeed 0: 29 <-- 生产 <-- 消费 1: 30
Taking a product ... Succeed 0: 0 <-- 生产 1: 30 <-- 消费
Consuming 29 ... Succeed Taking a product ... Succeed 0: 0 <-- 生产 <-- 消费
14
1: 0
Consuming 30 ... Succeed
步骤3:仔细阅读源程序,找出创建线程的WINDOWS API函数,回答下列问题:线程的第一个执行函数是什么(从哪里开始执行)?它位于创建线程的API函数的第几个参数中?
第一个执行函数是:DWORD WINAPI Producer(LPVOID lpPara) Produce();
它位于创建线程的API函数的第3个参数中。
步骤4:修改清单3-1中的程序,调整生产者线程和消费者线程的个数,使得消费者数目大与生产者,看看结果有何不同。运行结果: Producing 1 ... Succeed Appending a product ... Succeed 0: 1 <-- 消费 1: 0 <-- 生产
Producing 2 ... Succeed Appending a product ... Succeed 0: 1 <-- 生产 <-- 消费 1: 2
Taking a product ... Succeed 0: 0 <-- 生产 1: 2 <-- 消费
Consuming 1 ... Succeed Taking a product ... Succeed 0: 0 <-- 生产 <-- 消费 1: 0
Consuming 2 ... Succeed
15