步骤1:在工具栏单击“新建”按钮,编写代码保存为3-2.cpp。 程序功能:利用互斥信号量保护共享资源
参考类与函数:windows.h、iostream、class CCountUpDown、 WaitForCompletion()、DoCount()、ReleaseMutex()、
步骤2:单击“Build”菜单中的“Compile 3-2.cpp”命令,再单击“是”按钮确认,系统对3-2.cpp进行编译。
步骤3:编译完成后,单击“Build”菜单中的“Build 3-2.exe”命令,建立3-2.exe可执行文件。
操作能否正常进行,如果不行,原因是什么?
答:可以正常运行。
步骤4:在工具栏单击“Execute program”按钮,执行3-2.exe程序。
请描述运行结果(如果运行不成功,则可能的原因是什么?):
. . . .
程序创建两个线程来访问共享值,创建互斥体用于访问数值,程序中用的信号量是m_nAccess,实现从50到0的递减。
3.5 实验总结
答: 本次实验中主要的操作是线程的同步,创建进程,终止进程,利用信号量创建、打开、使用、清除,并利用互斥信号量保证两个进程互斥的访问共享数据。
当一个任务想对临界区访问时,为了防止别的任务也对该临界区操作,它需要利用互斥体来保护同时访问的共享资源。
CreateMutex()A函数可创建互斥信号量,ReleaseMutex()允许另一线程获得互斥体。利用CreateEvent()函数创建事。
实验4 进程的同步与通信 Windows 2000线程间的通信
(实验估计时间:100分钟)
4.1 背景知识
Windows 2000 提供的线程间通信类内核对象允许同一进程或跨进程的线程之间互相发送信息,包括文件、文件映射、邮件位和命名管道等,其中最常用的是文件和文件映射,这类对象允许一个线程很容易地向同一进程或其他进程中的另一线程发送信息。 1. 文件对象
文件对象是人们所熟悉的永久存储的传统元素。将一个文件看作是内核对象可使开发人员获得比标准C++文件操作更为强大的功能。 内核允许开发人员在系统设备或网络上创建代表永久存储数据块的文件对象。这些文件对象是对永久存储数据的低级访问者;用C++运行库或其他方法打开的所有文件最终都变成对CreateFile()API的调用。
CreateFile()函数分配一个内核对象来代表一个永久的文件,当在磁盘上创建一个新文件或当打开一个已经存在的文件时,就调用这个API,其参数总结见表4-1
表4-1 CreateFile()API参数
参数名 LPCTSTR lpFileName DWORD dwDesiredAccess DWORD dwShareMode lpSecurityAttributes DWORD dwCreationDisposition DWORD dwFlagsAndAttributes HANDLE hTemplateFile 使用目的 要打开或创建的文件名。 所要求的文件访问权;一个包括GENERIC_READ或GENERIC_WRITE的屏蔽。 指定与其他进程共享的文件类型(如果有的话) 性 在文件系统的级别上所采取的操作的类型。如新文件的创建或打开一个已有的文件 文件系统的属性,如只读、隐藏等。还可以是文件对象的属性、如可缓存写入等。 指向另一文件对象的句柄,常用于为新创建的文件提供属性。 LPSECURITY_ATTRIBUTES 当被文件系统支持时与备份文件对象有关的安全
通常可以使用ReadFile()和WriteFile()API在永久存储和应用程序间通过文件对象来移动数据,因为创建调用将对象的大多数复杂性封装起来了,这两个函数只是简单地利用指向要交换数据的文件对象的句柄(即指向内存内的数据缓存区的指针),然后计数移动数据的字节数。除此之外,这两个函数还执行重叠式的输入和输出,由于不会“堵塞”主线程,可用来传送大量的数据。
Createfile()函数除了可访问标准的永久文件外,还可访问控制台输入和输出,以及从命名的管道来的数据。
GetFileType() API指明要处理的关键文件句柄的结构。除此之外,内核还提供了GetFileInformationByHandle()、GetFileSize()和GetFileTime()API用于获得关键数据的详细情况。其它用于在文件中改变数据的工具函数包括Lockfile()、SetFilePoint()和SetEndOfFile() API。 除了这些基于句柄的API之外,内核还提供了大量的工具,用于按文件名对文件直接操作。文件对象用完之后,应该用CloseHandle()API加以清除。 2. 文件映射对象
比使用ReadFile()和WriteFile()API通过文件对象来读取和写入数据更为简单的是,Windows 2000还提供了一种在文件中处理数据的方法,名为内存映射文件,也称为文件映射。文件映射对象是在虚拟内存中分配的永久或临时文件对象区域(如果可能的话、可大到整个文件),可将其看作是二进制的数据块,使用这类对象,可获得直接在内存中访问文件内容的能力。
文件映射对象提供了强大的扫描文件中数据的能力,而不必移动文件指针,对于多线程的读写操作来说,这一点特别有用,因为每个线程都可能想要把读取指针移动到不同的位置——为了防止这种情况,就需要使用某种线程同步机制保护文件。
在CreateFileMapping()API中,一个新的文件映射对象需要有一个永久的文件对象(由CreateFile()所创建)。该函数使用标准的安全性和命名参数,还有用于允许操作(如只读)的保护标志以及大映射的最大容量,随后可根据来自OpenFileMapping()API的其他线程或进程使用该映射——这与事件和互斥信号量的打开进程是非常类似的。
内存映射文件对象的另一个强大的应用是可请求系统创建一个运行映射的临时文件。该临时文件提供一个临时的区域,用于线程或进程互相发送大量数据,而不必创建或保护磁盘上的文件。利用向创建函数中发送INVALID_HANDLE_VALUE来代替真正的文件句柄,就可创建这一临时的内存映射文件;指令内核使用系统页式文件建立支持映射的最大容量的临时数据区。
为了利用文件映射对象,进程必须将对文件的查看映射到它的内
存空间中,也就是说,应该将文件映射对象想像为进程的第一步,在这一步中,当查看实际上允许访问的数据时,附加有共享数据的安全性和命名方式。为了获得指向内存区域的指针需要调用MapViewOfFile()API,此调用使用文件映射对象的句柄作为其主要参数。此外还有所需的访问等级(如读-写)和开始查看时文件内的偏移和要查看的容量。该函数返回一个指向进程内的内存的指针,此指针有多种编程方面的应用(但不超过访问权限)。
当结束文件映射查看时,必须用接收到的指针调用UnmapView()OfFile() API,然后再根据映射对象调用CloseHandle()API,从而将其清除。
4.2 实验目的
在本实验中,通过对文件和文件映射对象的了解,来加深对Windows 2000线程同步的理解。
1)回顾系统进程、线程的有关概念,加深对Windows 2000线程间通信的理解。
2)了解文件和文件映射对象。
3)通过实验程序,了解线程如何通过文件对象发送数据。 4)了解在进程中如何使用文件对象。
5) 通过分析实验程序,了解线程如何通过文件映射对象发送数据。
6)了解在进程中如何使用文件映射对象。
4.3 工具/准备工作
在开始本实验之前,请回顾教科书的相关内容。 您需要做以下准备:
1)一台运行Windows 2000 Professional 操作系统的计算机。 2)计算机中需安装Visual C++ 6.0 专业版或企业版。
4.4 实验内容与步骤
1. 文件对象
了解线程如何通过文件对象在永久存储介质上互相发送数据。激活并启动一个线程,接着一个线程创建进程。每个线程从指定的文件中读取数据,数据的增加是以创建时发送给它的数量进行的,然后将新数值写回文件。