当前位置:首页 > 《计算机操作系统》实验指导书
nValue += nAdd;
// 写回永久存储介质
:: SetFilePointer(hFile, 0, NULL, FILE_BEGIN) ; :: WriteFile( hFile, // 要写入的文件 reinterpret_cast
NULL) ; // 无重叠I/O if (dwXfer == sizeof(nValue) )
{
std :: cout << “write: ”<< nValue << std :: endl; }
:: CloseHandle(hFile) ;
hFile = INVALID_HANDLE_VALUE; }
return(0) ; }
void main() {
// 创建100个线程从文件中进行读写
for (int nTotal = 100; nTotal > 0; --nTotal)
{
// 启动线程
HANDLE hThread = :: CreateThread(
NULL, // 缺省的安全性 0, // 缺省的堆栈 ThreadProc, // 线程函数 reinterpret_cast
NULL) ; // 忽略线程id // 等待线程完成
:: WaitForSingleObject(hThread, INFINITE) ;
:: Sleep(500) ; // 放慢显示速度,方便观察 // 释放指向线程的句柄 :: CloseHandle(hThread) ;
hThread = INVALID_ HANDLE_VALUE;
} }
运行结果 (如果运行不成功,则可能的原因是什么?) :
____________________________________________________________________ ________________________________________________________________________ 阅读和分析程序,请回答问题:
1) 程序中启动了多少个单独的读写线程?
____________________________________________________________________ 2) 使用了哪个系统API函数来创建线程例程?
____________________________________________________________________ 3) 文件的读和写操作分别使用了哪个API函数?
____________________________________________________________________ ________________________________________________________________________
每次运行进程时,都可看到程序中的每个线程从前面的线程中读取数据并将数据增加,文件中的数值连续增加。这个示例是很简单的通讯机制。可将这一示例用作编写自己的文件读/写代码的模板。
请注意程序中写入之前文件指针的重置。重置文件指针是必要的,因为该指针在读取结束时将处于前四个字节之后,同一指针还要用于向文件写入数据。如果函数向该处写入新数值,则下次进程运行时,只能读到原来的数值。那么:
4) 在程序中,重置文件指针使用了哪一个函数?
____________________________________________________________________ 5) 从输出结果,对照分析程序,可以看出程序运行的流程吗?请简单描述:
____________________________________________________________________ ________________________________________________________________________
2、演示使用映射文件的内存交换数据的线程
# include
static HANDLE g_hMutexMapping = INVALID_HANDLE_VALUE; // 增加共享内存中的数值的简单线程
static DWORD WINAPI ThreadProc(LPVOID lpParam) {
// 将参数看作句柄
HANDLE hMapping = reinterpret_cast
// 等待对文件的访问
:: WaitForSingleObject(g_hMutexMapping, INFINITE) ; // 映射视图
LPVOID pFile = :: MapViewOfFile( hMapping, // 保存文件的对象 FILE_MAP_ALL_ACCESS, // 获得读写权限 0, // 在文件的开头处 (高32位) 开始 0, // ... (低32位) 0) ; // 映射整个文件 if (pFile != NULL) {
// 将数据看作长整数
LONG * pnData = reinterpret_cast
std :: cout << “thread: ” << :: GetCurrentThreadId()
<< “value: ”<< (* pnData) << std :: endl;
// 释放文件视图
:: UnmapViewOfFile(pFile) ; pFile = NULL; }
// 释放对文件的访问权
:: ReleaseMutex(g_hMutexMapping) ; return(0) ; }
// 创建共享数据空间
HANDLE MakeSharedFile() {
// 创建文件映射对象
HANDLE hMapping = :: CreateFileMapping( INVALID_HANDLE_VALUE, // 使用页式文件临时文件 NULL, // 缺省的安全性 PAGE_READWRITRE, // 可读写权 0, // 最大容量 (高32位) sizeof(LONG) , // ... (低32位) NULL) ; // 匿名的 if (hMapping != INVALID_HANDLE_VALUE) {
// 在文件映射上创建视图
LPVOID pData = :: MapViewOfFile( hMapping, // 保存文件的对象 FILE_MAP_ALL_ACCESS, // 获得读写权 0, // 在文件的开头处(高32位)开始 0, // ... (低32位) 0 ) ; // 映射整个文件 if (pData != NULL) {
:: ZeroMemory(pData, sizeof(LONG) ) ;
}
// 关闭文件视图
:: UnmapViewOfFile(pData) ; }
return (hMapping) ; }
void main() {
// 创建数据文件
HANDLE hMapping = :: MakeSharedFile() ; // 创建仲裁的互斥体
g_hMutexMapping = :: CreateMutex(NULL, FALSE, NULL) ; // 根据文件创建100个线程来读写
for (int nTotal = 100; nTotal > 0 ; - - nTotal) {
// 启动线程
HANDLE hThread = :: CreateThread( NULL, // 缺省的安全性 0, // 缺省堆栈 ThreadProc, // 线程函数 reinterpret_cast
std :: cout << “all threads created, waiting...”<< std :: endl; :: WaitForSingleObject(hThread, INFINITE) ; }
:: Sleep(500) ; // 放慢显示速度,方便观察 // 释放指向线程的句柄 :: CloseHandle(hThread) ;
hThread = INVALID_HANDLE_VALUE; }
// 关闭对象
:: CloseHandle(hMapping) ;
hMapping = INVALID_HANDLE_VALUE; :: CloseHandle(g_hMutexMapping) ;
g_hMutexMapping = INVALID_HANDLE_VALUE;
}
阅读和分析程序,请回答:
1) 程序中用来创建一个文件映射对象的系统API函数是哪个?
____________________________________________________________________ 2) 在文件映射上创建和关闭文件视图分别使用了哪一个系统函数?
a. __________________________________________________________________ b. __________________________________________________________________
3) 运行时,程序首先通过 ( ) 函数创建一个小型的文件映射对象 ( ) ,接着,使用系统API函数 ( ) 再创建一个保护其应用的互斥体 ( ) 。然后,应用程序创建100个线程,每个都允许进行同样的进程,即:通过互斥体获得访问权,这个操作是由语句:____________________________________________________________________
实现的。再通过函数 ( ) 操作将视图映射到文件,将高32位看作有符号整数,将该数值增加 (即命令:______________________ ) ,再将新数值显示在控制台上。每个线程清除文件的视图并在退出之前释放互斥体释放互斥体的语句是_______________________________________________________。当线程完成时,应用程序关闭并退出。
4) 将程序中的语句 :: Sleep(500) ; 删除 (例如在语句前面加上“//”) 后,重新编译运行,结果有变化吗?为什么?
____________________________________________________________________ ________________________________________________________________________
四、实验总结
请总结一下本次实验的收获、教训和感受,结合课本内容谈一下你对进程间控制的理解。
实验三 存储管理
一、实验目的
通过实验了解Windows 2000内存的使用,学习如何在应用程序中管理内存,体会Windows应用程序内存的简单性和自我防护能力。学习检查虚拟内存空间或对其进行操作;了解Windows 2000的内存结构和虚拟内存的管理,进而了解进程堆和Windows为使用内存而提供的一些扩展功能。
二、实验环境
硬件环境:计算机一台,局域网环境;
软件环境:Windows 2000 Professional,Visual C++ 6.0专业版或企业版。
三、实验内容和步骤
在Windows 2000环境下,4GB的虚拟地址空间被划分成两个部分:低端2GB提供给进程使用,高端2GB提供给系统使用。这意味着用户的应用程序代码,包括DLL以及进程使用的各种数据等,都装在用户进程地址空间内 (低端2GB) 。
1. 虚拟内存的检测
检测进程的虚拟地址空间
// 工程vmwalker
# include
# pragma comment(lib, \
// 以可读方式对用户显示保护的辅助方法。
// 保护标记表示允许应用程序对内存进行访问的类型 // 以及操作系统强制访问的类型
inline bool TestSet(DWORD dwTarget, DWORD dwMask) {
return ((dwTarget & dwMask) == dwMask) ; }
# define SHOWMASK(dwTarget, type) \\ if (TestSet(dwTarget, PAGE_##type) ) \\ {std :: cout << \
void ShowProtection(DWORD dwTarget) {
SHOWMASK(dwTarget, READONLY) ; SHOWMASK(dwTarget, GUARD) ; SHOWMASK(dwTarget, NOCACHE) ; SHOWMASK(dwTarget, READWRITE) ; SHOWMASK(dwTarget, WRITECOPY) ; SHOWMASK(dwTarget, EXECUTE) ;
SHOWMASK(dwTarget, EXECUTE_READ) ;
SHOWMASK(dwTarget, EXECUTE_READWRITE) ; SHOWMASK(dwTarget, EXECUTE_WRITECOPY) ; SHOWMASK(dwTarget, NOACCESS) ; }
共分享92篇相关文档