操作系统实验指导书(07)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
《操作系统》
——实验指导书
编者:陈洺均
桂林电子科技大学信息科技学院
二00九年三月
实验一Windows线程的创建与撤销
一、实验目的
1.熟悉Windows系统提供的线程创建与撤销系统调用。
2.掌握Windows系统环境下线程的创建与撤销方法。
二、实验预备内容
(1)阅读Windows源码文件,加深对线程管理概念的理解;
(2)CreateThread( )调用,创建一个线程;ExitThread ( )调用,撤销当前线程;
TerminateThread( )终止线程;Sleep( )用于挂起当前正在执行的线程。
三、实验内容
正确使用CreateThread()、ExitThread ( )及Sleep( )等系统调用,进一步理解进程与线程理论。
用系统调用CreateThread( )创建一个子线程,并在子线程序中显示:Thread is Runing!。
为了能让用户清楚地看到线程的运行情况,使用Sleep( )使线程挂起5S,之后使用ExitThread (0)撤销线程。
运行结果如下图所示:
<参考程序 >
// ThreadCreate.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "ThreadCreate.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// The one and only application object
CWinApp theApp;
using namespace std;
void ThreadName1();
static HANDLE hHandle1=NULL; //用于存储线程返回句柄的变量。
DWORD dwThreadID1; //用于存储线程标识符的变量。
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
hHandle1 = CreateThread( (LPSECURITY_ATTRIBUTES) NULL,
0,
(LPTHREAD_START_ROUTINE) ThreadName1,
(LPVOID) NULL,
0, &dwThreadID1 );
代码段1 //将主线程挂起5S
CloseHandle(hHandle1);
ExitThread(0);
return nRetCode;
}
代码段2 //线程对应的函数
四、思考
(1)如何向线程对应的函数传递参数?一个参数如何传递,多个参数又如何传递?
(2)深入理解线程与进程的概念,在Windows环境下何时使用进程,何时使用线程。
实验二线程的同步
一、实验目的
1. 进一步掌握Windows系统环境下线程的创建与撤销。
2.熟悉Windows系统提供的线程同步API。
3.使用Windows系统提供的线程同步API解决实际问题。
二、实验预备内容
相关API函数介绍(见资料)
三、实验内容
完成主、子两个线程之间的同步,要求子线程先执行。
在主线程中使用系统调用CreateThread()创建一个子线程。
主线程创建子线程后进入阻塞状态,直到子线程运行完毕后唤醒主线程。
四、实验指导
在Microsoft visual C++ 6.0环境下建立一个MFC支持的控制台工程文件,编写C程序,在程序中使用CreateSemaphore(NULL,0,1,”SemaphoreName1”)创建一个名为”SemaphoreName1”的信号量,信号量的初始值为0,之后使用OpenSemaphore(SYNCHRONIZE|SEMAPHORE_MODIFY_STATE,NULL,”Semap horeName1”)打开该信号量,这里访问标志用” SYNCHRONIZE|SEMAPHORE _MODIFY_STATE”,以便之后可以使用WaitForSingleObject()等待该信号量及使用ReleaseSemaphore()释放该信号量,然后创建一个子线程,主线程创建子线程后调用WaitForSingle(hHandle1,INFINITE),这里等待时间设置为INFINITE表示要一直等待下去,直到该信号量被唤醒为止。
子线程结束,调用ReleaseSemaphore(hHandle1,1,NULL)释放信号量,使信号量的值加1。
主子线程运行情况如下图:
<参考程序 >
// Semaphore.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "Semaphore.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// The one and only application object
CWinApp theApp;
using namespace std;
static HANDLE h1; //线程句柄
static HANDLE hHandle1=NULL; //信号量句柄
void func();
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
DWORD dwThreadID1;
DWORD dRes,err;
hHandle1 =代码1 //创建一个信号量
if (hHandle1==NULL) printf("Semaphore Create Fail!\n");
else printf("Semaphore Create Success!\n");
hHandle1 =代码2 //打开信号量if (hHandle1==NULL) printf("Semaphore Open Fail!\n");
else printf("Semaphore Open Success!\n");
h1=CreateThread((LPSECURITY_ATTRIBUTES)NULL,0,
(LPTHREAD_START_ROUTINE)func,
(LPVOID)NULL,
0,&dwThreadID1); //创建子线程if (h1==NULL) printf("Thread1 create Fail!\n");
else printf("Thread1 create Success!\n");
dRes=代码3 //主线程等待子线程结束
err=GetLastError();
printf("WaitForSingleObject err=%d\n",err);
if (dRes == WAIT_TIMEOUT) printf("TIMEOUT!dRes=%d\n",dRes);
else if (dRes==WAIT_OBJECT_0)
printf("WAIT_OBJECT!dRes=%d\n",dRes);
else if (dRes==WAIT_ABANDONED)
printf("WAIT_ABANDONED!dRes=%d\n",dRes);
else printf("dRes=%d\n",dRes);
CloseHandle(h1);
CloseHandle(hHandle1);
ExitThread(0);
return nRetCode;
}
void func()
{
BOOL rc;
DWORD err;
printf(" Now In Thread !\n");
rc=代码4 //子线程唤醒主线程
err=GetLastError();
printf("ReleaseSemaphore err=%d\n",err);
if (rc==0) printf("Semaphore Release Fail!\n");
else printf("Semaphore Release Success!rc=%d\n",rc);
}
五、思考
以上程序完成了主、子两个线程执行先后顺序的同步关系,填充代码并思考以下问题:
(1)如何实现多个线程的同步?
(2)若允许子线程执行多次后主线程再执行,又如何设置信号量的初值。
实验三线程的互斥
一、实验目的
1.熟练掌握Windows系统环境下线程的创建与撤销。
2.熟悉Windows系统提供的线程互斥API。
3.使用Windows系统提供的线程互斥API解决实际问题。
二、实验预备内容
相关API函数介绍(见资料)
三、实验内容
完成两个子线程之间的互斥。
在主线程中使用系统调用CreateThread()创建两个子线程,并使用两个子线程互斥地使用全局变量Count。
正确使用临界区对象,包括初始化临界区InitializeCriticalSection()、进入临界区EnterCriticalSection()、退出临界区LeaveCriticalSection()及删除临界区DeleteCriticalSection(),进一步理解线程的互斥。
四、实验指导
在Microsoft visual C++ 6.0环境下建立一个MFC支持的控制台工程文件,编写C程序,在主线程中使用InitializeCriticalSection()初始化临界区,然后建立两个子线程,在两个子线程中使用全局变量count的前、后分别使用EnterCritical Section()进入临界区及使用LeaveCriticalSection()退出临界区,等两个子线程运行完毕,主线程使用DeleteCriticalSection()删除临界区并撤销线程。
运行结果如下图所示:
<参考程序 >
// Mutex.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "Mutex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// The one and only application object
CWinApp theApp;
using namespace std;
static int count=5;
static HANDLE h1;
static HANDLE h2;
LPCRITICAL_SECTION hCriticalSection; //定义指向临界区对象的地址指针CRITICAL_SECTION Critical; //定义临界区
void func1();
void func2();
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
DWORD dwThreadID1,dwThreadID2;
hCriticalSection=&Critical; //将指向临界区对象的地址指针指向临界区InitializeCriticalSection(hCriticalSection); //初始化临界区
h1=CreateThread((LPSECURITY_ATTRIBUTES)NULL,
0,
(LPTHREAD_START_ROUTINE)func1,
(LPVOID)NULL,
0,&dwThreadID1);
if (h1==NULL) printf("Thread1 create Fail!\n");
else printf("Thread1 create Success!\n");
h2=CreateThread((LPSECURITY_ATTRIBUTES)NULL,
0,
(LPTHREAD_START_ROUTINE)func2,
(LPVOID)NULL,
0,&dwThreadID2);
if (h2==NULL) printf("Thread2 create Fail!\n");
else printf("Thread2 create Success!\n");
_sleep(1000);
CloseHandle(h1);
CloseHandle(h2);
代码1 //删除临界区ExitThread(0);
return nRetCode;
}
void func2()
{
int r2;
代码2 //进入临界区
r2=count;
_sleep(100);
r2=r2+1;
count=r2;
printf("count in func2=%d\n",count);
代码3 //退出临界区
}
void func1()
{
int r1;
EnterCriticalSection(hCriticalSection);
r1=count;
_sleep(500);
r1=r1+1;
count=r1;
printf(“count in func1=%d\n”,count);
LeaveCriticalSection(hCriticalSection);
}
五、思考
(1)上面实验使用临界区对象(CriticalSection)实现的,请试用互斥对象(mutex)来完成。
(2)设计并完成使用前面实验完成的内容解决实际的同步与互斥问题,如生产者与消费者问题、读者与写者问题等。
实验四系统内存使用统计
4.1 实验目的
(1)了解Windows内存管理机制,理解页式存储管理技术。
(2)熟悉Windows内存管理基本数据结构。
(3)掌握Windows内存管理基本API的使用。
4.2实验内容
使用Windows系统提供的函数和数据结构显示系统存储空间的使用情况,当内存和虚拟存储空间变化时,观察系统显示变化情况。
4.3实验要求
能正确使用系统函数GlobalMemoryStatus()和数据结构MEMORYSTATUS了解系统内存和虚拟存储空间使用情况,会使用VirtualAlloc()函数和VirtualFree()函数分配和释放虚拟存储空间。
4.4实验指导
在Microsoft Visual C++6.0环境下选择Win32 Console Application建立一个控制台工程文件,由于内存分配、释放及系统存储空间使用情况函数均是Microsoft Windows操作系统的系统调用,因此在图4-1中选择An application that supports MFC。
图4-1
4.5源程序
// GetMemoryStatus.cpp : Defines the entry point for the console application. //
#include "stdafx.h"
#include "GetMemoryStatus.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
void GetMemSta(void);
/////////////////////////////////////////////////////////////////////////////
// The one and only application object
CWinApp theApp;
using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
LPVOID BaseAddr;
char *str;
GetMemSta();
printf("Now Allocate 32M Virtual Memory and 2M Physical Memory\n\n");
BaseAddr=代码1 //分配虚拟内存(NULL,1024*1024*32,MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE);
if (BaseAddr==NULL) printf("Virtual Allocate Fail\n");
str=(char *)代码2 //分配内存
GetMemSta();
printf("Now Release 32M Virtual Memory and 2M Physical Memory\n\n");
if (代码3) //释放虚拟内存
printf("Release Allocate Fail\n");
free(str); //释放内存
GetMemSta();
return nRetCode;
}
void GetMemSta(void) //统计内存的状态
{
MEMORYSTATUS MemInfo;
GlobalMemoryStatus(&MemInfo);
printf("Current Memory Status is:\n");
printf("\t Total Physical Memory
is %dMB\n",MemInfo.dwTotalPhys/(1024*1024));
printf("\t Available Physical Memory
is %dMB\n",MemInfo.dwAvailPhys/(1024*1024));
printf("\t Total Page File is %dMB\n",MemInfo.dwTotalPageFile/(1024*1024));
printf("\t Available Page File
is %dMB\n",MemInfo.dwAvailPageFile/(1024*1024));
printf("\t Total Virtual memory
is %dMB\n",MemInfo.dwTotalVirtual/(1024*1024));
printf("\t Available Virtual memory
is %dMB\n",MemInfo.dwAvailVirtual/(1024*1024));
printf("\t Memory Load is %d%% \n\n",MemInfo.dwMemoryLoad);
}
4.6实验总结
观察图4-2所示程序的运行情况,可以看出以下几点。
(1)程序开始运行时,显示的可用物理内存为106MB,可用页文件大小为399MB,可用虚拟内存为2021MB。
(2)当分别使用函数VirtualAlloc()和malloc()分配了32MB虚拟内存和2MB物理内存后,系统显示可用物理内存为104MB,可用页文件大小为365MB,可用虚拟内存为1987MB。
(3)当分别使用函数VirtualFree()和free()释放了32MB虚拟内存和2MB 物理内存后,系统的显示情况又恢复了(1)的情况。
图 4-2
4.7实验展望
从实验结果可以看出,虚拟内存的变化有些误差,请解释原因是什么。
如果
想了解更详细的虚拟内存情况,如锁定(VirtualLock)、解锁(VirtualUnlock)及保护方式(VirtualProtect)等,如何设计程序来实现?
实验五动态链接库的建立与调用
一、实验目的
(1)理解动态链接库的实现原理。
(2)掌握Windows系统动态链接库的建立方法。
(3)掌握Windows环境下动态链接库的调用方法。
二、实验预备内容
(1)动态链接库基础知识;
(2)动态链接库入口函数;
(3)动态链接库导入/导出函数;
(4)动态链接库的两种链接方式(隐式链接、显式链接);
(5)函数调用参数传递约定。
三、实验内容
(1)在Windows环境下建立一个动态链接库。
(2)使用隐式调用法调用动态链接库。
(3)使用显式调用法调用动态链接库。
四、实验要求
掌握动态链接库建立和调用方法。
在WindowsXP+Microsoft Visual C++环境下建立一个动态链接库,并分别使用隐式和显式方式将其调用,从而体会使用动态链接库的优点。
该实验完成了动态链接库的建立和调用。
函数Add()和Sub()在动态链接库文件SimpleDll.cpp中,分别完成两个整数的相加和相减。
而调用该动态链接库的程序文件是CallDll.cpp,程序运行结果如下:
<参考程序 >
// SimpleDll.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
extern "C" _declspec(dllexport) int Add(int x,int y);
extern "C" _declspec(dllexport) int Sub(int x,int y);
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
return TRUE;
}
int Add(int x,int y)
{
代码1 //加法定义
}
int Sub(int x,int y)
{
代码2 //减法定义
}
//隐式调用动态链接库的程序
// CallDll.cpp : Defines the entry point for the console application. //
#include "stdafx.h"
extern "C" _declspec(dllimport) int Add(int x,int y);
extern "C" _declspec(dllimport) int Sub(int x,int y);
int main(int argc, char* argv[])
{
int x=7;
int y=6;
int add=0;
int sub=0;
printf("Call Dll Now!\n");
add=代码3;
sub=代码4; //隐式调用动态链接库printf(" 7+6=%d ,7-6=%d\n",add,sub);
return 0;
}
//显式调用动态链接库的程序
// CallDllAddress.cpp : Defines the entry point for the console application. //
#include "stdafx.h"
#include "CallDllAddress.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// The one and only application object
CWinApp theApp;
using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int s;
int nRetCode = 0;
typedef int (*pAdd)(int x,int y);
typedef int (*pSub)(int x,int y);
HMODULE hDll;
pAdd add;
pSub sub;
hDll=LoadLibrary("SimpleDll.dll"); //加载动态链接库文件
if(hDll==NULL)
{
printf("LoadLibrary Error.......\n");
return nRetCode;
}
else printf("LoadLibrary Success.......\n");
add=代码5; //得到动态链接库中函数Add()的内部地址s=代码6; //显式调用
printf("6+2=%d\n",s);
sub=代码7; //得到动态链接库中函数Sub()的内部地址
s=代码8; //显式调用
printf("6-2=%d\n",s);
FreeLibrary(hDll); //释放动态链接库
return nRetCode;
}
四、思考
本实验介绍了在WindowsXP+Microsoft Visual C++6.0环境下建立与调用动态链接库的方法,使用动态链接库,除了可以节省内存空间、实现代码共享之外,还可以实现多种编程语言书写的程序相互调用。
同学们在完成上述实验的基础上,可以自学完成其它语言如Java书写的程序调用Visual C++建立动态链接库,从中体会多种语言编程给程序员带来的方便。
实验六采用高速缓存实现文件读/写
一、实验目的
(1)了解Windows系统文件高速缓存的概念。
(2)熟悉Windows系统文件读/写相关API。
(3)掌握采用缓冲方式实现文件读/写相关参数的设置。
二、实验内容
建立一个函数,使用该函数将源文件source.txt中的内容读出,再写到目标文件sequential.txt中去。
三、实验要求
采用高速缓存方式完成文件的读/写。
四.、实验指导
由于要采用高速缓存进行文件操作,在使用函数CreateFile()建立文件时,其dwFlagsAndAttributes应选用FILE_FLAG_SEQUENTIAL_SCAN。
该实验完成缓冲方式的文件读/写操作。
先创建两个文件,即source.txt 和sequential.txt,然后反复从文件source.txt中读出数据块,并写到文件sequential.txt中去,直到文件尾为止。
参考程序
// File_Senquential_Scan.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "File_Senquential_Scan.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
DWORD BufferSize =1024;
char buf[1024];
void FileReadWrite_Sequential_Scan(char* source,char* destination); /////////////////////////////////////////////////////////////////////////////
// The one and only application object
CWinApp theApp;
using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
printf("Call FileReadWrite_SenquentialScan!\n");
FileReadWrite_Sequential_Scan("source.txt","nobuffer.txt");
return nRetCode;
}
void FileReadWrite_Sequential_Scan(char* source,char* destination) {
HANDLE handle_src,handle_dst;
DWORD NumberOfByteRead,NumberOfByteWrite;
BOOL cycle;
char *buffer;
buffer=buf;
//建立文件
handle_src=CreateFile(source,GENERIC_READ,0,NULL,OPEN_EXISTING,FI LE_FLAG_SEQUENTIAL_SCAN,NULL);
handle_dst=CreateFile(destination,GENERIC_WRITE,NULL,NULL,CREATE_ ALWAYS,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if
(handle_src==INV ALID_HANDLE_V ALUE||handle_dst==INV ALID_HANDLE_V A LUE)
{
printf("File Create Fail!\n");
exit(1);
}
cycle=TRUE;
while (cycle)
{
NumberOfByteRead=BufferSize;
//读文件
if (代码1)
{
printf("Read File Error!%d\n",GetLastError());
exit(1);
}
if (NumberOfByteRead < BufferSize) cycle=FALSE;
//写文件
if (代码2)
{
printf("Write File Error!%d\n",GetLastError());
exit(1);
}
}
//关闭文件句柄
CloseHandle(handle_src);
CloseHandle(handle_dst);
}。