文档库 最新最全的文档下载
当前位置:文档库 › 《操作系统》实验指导书

《操作系统》实验指导书

计算机科学与技术、信息管理和信息系统专业《操作系统》实验指导书

《操作系统》实验指导书实验一 Linux系统的安装

一、实验目的

1、深入认识磁盘分区

2、掌握Linux安装的基本过程

3、掌握多系统共存的系统安装方法

二、实验任务

在现有系统安装Redhat Linux系统,注意不要破坏现有系统。

三、实验指导

参考《Linux上机实践教程》第一章内容。

实验二 Linux系统的基本使用

一、实验目的

1、熟悉linux系统的启动、登入和退出

2、熟悉linux系统文件和目录的基本使用

3、熟悉其它常用命令及虚拟终端的使用

4、体会linux系统作为分时系统的特点

二、实验任务

启动、登入和退出linux系统

练习使用文件和目录操作的基本命令

使用它常用命令及虚拟终端

练习使用Vi编辑器

三、实验指导

参考《Linux上机实践教程》第二、三章内容。

实验三 windows 2000中进程的创建和控制

一、实验目的

1、加深对进程概念的理解,明确进程和程序的区别

2、进一步认识进程并发执行的实质

3、掌握windows 2000中进程创建和控制的编程方法

二、实验任务

创建一个windows窗口程序,含有4个菜单项,分别用来创建和撤消记事本进程和计算器进程。若相应进程已经创建了,再选择创建进程菜单,则弹出对话框提示进程已经创建;若进程已经撤消了,再选择撤消进程菜单,则弹出对话框提示进程已经撤消。注意考虑从主程序外部启动和关闭进程的情况。

三、实验指导

1、Win32的进程的概念

进程是应用程序的运行实例,由私有虚拟地址空间、代码、数据和其他操作系统资源(如进程创建的文件、管道、同步对象等)组成。一个应用程序可以有一个或多个进程,一个进程可以有一个或多个线程,其中一个是主线程。进程要完成什么事情的话必须至少拥有一个线程,由线程来负责执行包含在地址空间的代码。

2、Win32的进程的创建

Windows所创建的每个进程都从调用CreateProcess() API函数开始,该函数的任务是在对象管理器子系统内初始化进程对象。CreateProcess() 调用的核心参数是可执行文件运行时的文件名及其命令行。下表详细地列出了每个参数的类型和名称。

可以指定第一个参数,即应用程序的名称,其中包括相对于当前进程的当前目录的全路径或者利用搜索方法找到的路径;lpCommandLine参数允许调用者向新应用程序发送数据;接下来的三个参数与进程和它的主线程以及返回的指向该对象的句柄的安全性有关。

然后是标志参数,用以在dwCreationFlags参数中指明系统应该给予新进程什么行为。经常使用的标志是CREATE_SUSPNDED,告诉主线程立刻暂停。当准备好时,应该使用ResumeThread() API来启动进程。另一个常用的标志是CREATE_NEW_CONSOLE,告诉新进程启动自己的控制台窗口,而不是利用父窗口。

这一参数还允许设置进程的优先级,用以向系统指明,相对于系统中所有其他的活动进程来说,给此进程多少CPU时间。

接着是CreateProcess() 函数调用所需要的三个通常使用缺省值的参数。第一个参数是lpEnvironment参数,指明为新进程提供的环境;第二个参数是lpCurrentDirectory,可用于向主创进程发送与缺省目录不同的新进程使用的特殊的当前目录;第三个参数是

STARTUPINFO数据结构所必需的,用于在必要时指明新应用程序的主窗口的外观。

CreateProcess() 的最后一个参数是用于新进程对象及其主线程的句柄和ID的返回值缓冲区。以PROCESS_INFORMA TION结构中返回的句柄调用CloseHandle() API函数是重要的,因为如果不将这些句柄关闭的话,有可能危及主创进程终止之前的任何未释放的资源。

3、终止进程

所有进程都是以调用ExitProcess() 或者TerminateProcess() 函数结束的。但最好使用前者而不要使用后者,因为进程是在完成了它的所有的关闭“职责”之后以正常的终止方式来调用前者的。而外部进程通常调用后者即突然的进行,由于关闭时的途径不太正常,有可能引起错误的行为。

TerminateProcess() API函数只要打开带有PROCESS_TERMINATE访问权的进程对象,就可以终止进程,并向系统返回指定的代码。这是一种“野蛮”的终止进程的方式,但是有时却是需要的。

4、如何判断一个进程是否启动或终止?

如果一个进程的启动或终止完全由主进程控制,只需引入一个标志进行判断即可。

如果外部可以控制进程的启动或终止,则情况要复杂一些。通常由两种方法,一是根据进程的程序名称,查询系统所有进程,以确定该程序是否已经运行。另一种方法是根据进程的窗口标题获得窗口句柄,再根据窗口句柄获得相应的程序名称,以确定该程序是否已经运行。以下是相关的API函数,可查阅MSDN:

HANDLE WINAPI CreateToolhelp32Snapshot(

DWORD dwFlags,

DWORD th32ProcessID );

BOOL WINAPI Process32First(

HANDLE hSnapshot,

LPPROCESSENTRY32 lppe );

BOOL WINAPI Process32Next(

HANDLE hSnapshot,

LPPROCESSENTRY32 lppe );

HWND FindWindow(

LPCTSTR lpClassName, // pointer to class name

LPCTSTR lpWindowName // pointer to window name);

UINT WINAPI GetWindowModuleFileName(

HWND hwnd,

LPTSTR lpszFileName,

UINT cchFileNameMax);

5、程序实例

本程序显示了创建子进程的基本框架。该程序只是再一次地启动自身,显示它的系统进程ID和它在进程列表中的位置。

# include

# include

# include

// 创建传递过来的进程的克隆过程并赋于其ID值

void StartClone(int nCloneID)

{

// 提取用于当前可执行文件的文件名

TCHAR szFilename[MAX_PA TH] ;

:: GetModuleFileName(NULL, szFilename, MAX_PA TH) ;

// 格式化用于子进程的命令行并通知其EXE文件名和克隆ID

TCHAR szCmdLine[MAX_PA TH] ;

:: sprintf(szCmdLine, “\”%s\” %d”, szFilename, nCloneID) ;

// 用于子进程的STARTUPINFO结构

STARTUPINFO si;

:: ZeroMemory(reinterpret_cast (&si) , sizeof(si) ) ;

si.cb = sizeof(si) ; // 必须是本结构的大小

// 返回的用于子进程的进程信息

PROCESS_INFORMA TION pi;

// 利用同样的可执行文件和命令行创建进程,并赋于其子进程的性质BOOL bCreateOK = :: CreateProcess(

szFilename, // 产生这个EXE的应用程序的名称szCmdLine, // 告诉其行为像一个子进程的标志

NULL, // 缺省的进程安全性

NULL, // 缺省的线程安全性

FALSE, // 不继承句柄

CREA TE_NEW_CONSOLE, // 使用新的控制台

NULL, // 新的环境

NULL, // 当前目录

&si, // 启动信息

&pi) ; // 返回的进程信息

// 对子进程释放引用

if (bCreateOK)

{

:: CloseHandle(pi.hProcess) ;

:: CloseHandle(pi.hThread) ;

}

}

int main(int argc, char* argv[] )

{

// 确定进程在列表中的位置

int nClone(0) ;

if (argc > 1)

{

// 从第二个参数中提取克隆ID

:: sscanf(argv[1] , “%d” , &nClone) ;

}

// 显示进程位置

std :: cout << “Process ID: “ << :: GetCurrentProcessId()

<< “, Clone ID: “ << nClone

<< std :: endl;

// 检查是否有创建子进程的需要

const int c_nCloneMax = 25;

if (nClone < C_nCloneMax)

{

// 发送新进程的命令行和克隆号

StartClone(++nClone) ;

}

// 在终止之前暂停一下(l/2秒)

:: Sleep(500) ;

return 0;

}

实验四 windows 2000中线程的创建和同步控制

一、实验目的

1、加深对线程概念的理解,明确进程和线程的区别

2、掌握windows 2000中线程创建和同步控制的编程方法

二、实验任务

在windows2000的环境下,创建一个控制台进程,此进程创建两个并发线程,一个是读线程,另一个是写线程。这两个线程共享一个数组A,写线程对数组分别进行10次写操作,每次写操作对A的每个元素赋一个相同的值;读线程对数组分别进行10次读操作,每次读操作输出A中所有元素的值。写出相应代码,并分析运行结果。修改代码,使每次读写互斥,即每次对数组的写操作结束后才能进行写操作,反之亦然。

三、实验指导

线程是操作系统分时调度分配CPU时间的基本实体。一个线程可以执行程序的任意部分的代码,即使这部分代码被另一个线程并发地执行;一个进程的所有线程共享它的虚拟地址空间、全局变量和操作系统资源。之所以有线程这个概念,是因为以线程而不是进程为调度对象效率更高:由于创建新进程必须加载代码,而线程要执行的代码已经被映射到进程的地址空间,所以创建、执行线程的速度比进程更快。一个进程的所有线程共享进程的地址空间和全局变量,所以简化了线程之间的通讯。

1、线程的创建

使用CreateThread函数创建线程,CreateThread的原型如下:

HANDLE CreateThread(

LPSECURITY_ATTRIBUTES lpThreadAttributes,

DWORD dwStackSize,

LPTHREAD_START_ROUTINE lpStartAddress,

LPVOID lpParameter,

DWORD dwCreationFlags, // creation flags

LPDWORD lpThreadId

);

其中:

lpThreadAttributes 表示创建线程的安全属性,NT下有用。

dwStackSize 指定线程栈的尺寸,如果为0则与进程主线程栈相同。

lpStartAddress 指定线程开始运行的地址。

lpParameter 表示传递给线程的32位的参数。

dwCreateFlages 表示是否创建后挂起线程(取值CREATE_SUSPEND),挂起后调用ResumeThread继续执行。

lpThreadId 用来存放返回的线程ID。

线程的优先级别

进程的每个优先级类包含了五个线程的优先级水平。在进程的优先级类确定之后,可以改变线程的优先级水平。用SetPriorityClass设置进程优先级类,用SetThreadPriority 设置线程优先级水平。

Normal 级的线程可以被除了Idle级以外的任意线程抢占。

2、线程的终止

以下情况终止一个线程:

?调用了ExitThread函数;

?线程函数返回:主线程返回导致ExitProcess被调用,其他线程返回导致ExitThread

被调用;

?调用ExitProcess导致进程的所有线程终止;

?调用TerminateThread终止一个线程;

?调用TerminateProcess终止一个进程时,导致其所有线程的终止。

3、线程同步

同步可以保证在一个时间内只有一个线程对某个资源(如操作系统资源等共享资源)有控制权。共享资源包括全局变量、公共数据成员或者句柄等。同步还可以使得有关联交互作用的代码按一定的顺序执行。

Win32 提供了一组对象用来实现多线程的同步。这些对象有两种状态:获得信号(Signaled)或者没有或则信号(Not signaled)。线程通过Win32 API提供的同步等待函数(Wait functions)来使用同步对象。一个同步对象在同步等待函数调用时被指定,调用同步函数地线程被阻塞(blocked),直到同步对象获得信号。被阻塞的线程不占用CPU 时间。

同步对象主要有:Critical_section(临界区),Event(事件),Mutex(互斥对象),Semaphores(信号量)。下面,解释怎么使用这些同步对象。

?临界区对象:

首先,定义一个临界区对象cs:

CRITICAL_SECTION cs;

然后,初始化该对象。初始化时把对象设置为NOT_SINGALED,表示允许线程使用资源:

InitializeCriticalSection(&cs);

如果一段程序代码需要对某个资源进行同步保护,则这是一段临界区代码。在进入该临界区代码前调用EnterCriticalSection函数,这样,其他线程都不能执行该段代码,若它们试图执行就会被阻塞。完成临界区的执行之后,调用LeaveCriticalSection函数,其他的线程就可以继续执行该段代码。如果该函数不被调用,则其他线程将无限期的等待。

?事件对象

首先,调用CreateEvent函数创建一个事件对象,该函数返回一个事件句柄。然后,可以设置(SetEvent)或者复位(ResetEvent)一个事件对象,也可以发一个事件脉冲(PlusEvent),即设置一个事件对象,然后复位它。复位有两种形式:自动复位和人工复位。在创建事件对象时指定复位形式。

自动复位:当对象获得信号后,就释放下一个可用线程(优先级别最高的线程;如果优先级别相同,则等待队列中的第一个线程被释放)。

人工复位:当对象获得信号后,就释放所有可利用线程。

最后,使用CloseHandle销毁创建的事件对象。

?互斥对象

首先,调用CreateMutex创建互斥对象;然后,调用等待函数,可以的话利用关键资源;最后,调用RealseMutex释放互斥对象。

互斥对象可以在进程间使用,但临界区对象只能用于同一进程的线程之间。

?信号量对象

在Win32中,信号量的数值变为0时给以信号。在有多个资源需要管理时可以使用信

号量对象。

首先,调用CreateSemaphore创建一个信号量;然后,调用等待函数,如果允许的话,则利用关键资源;最后,调用RealeaseSemaphore释放信号量对象。

此外,还有其他句柄可以用来同步线程:

●文件句柄(FILE HANDLES)

●命名管道句柄(NAMED PIPE HANDELS)

●控制台输入缓冲区句柄(CONSOLE INPUT BUFFER HANDLES)

●通讯设备句柄(COMMUNICTION DEVICE HANDLES)

●进程句柄(PROCESS HANDLES)

●线程句柄(THREAD HANDLES)

例如,当一个进程或线程结束时,进程或线程句柄获得信号,等待该进程或者线程结束的线程被释放。

等待函数:

Win32 提供了一组等待函数用来让一个线程阻塞自己的执行。等待函数常用的是:

●等待单个对象的(FOR SINGLE OBJECT):

WaitForSingleObject

函数参数包括同步对象的句柄和等待时间等。

在以下情况下等待函数返回:

同步对象获得信号时返回;

等待时间达到了返回:如果等待时间不限制(Infinite),则只有同步对象获得信号才返回;如果等待时间为0,则在测试了同步对象的状态之后马上返回。

●等待多个对象的(FOR MULTIPLE OBJECTS)

WaitForMultipleObjects

函数参数包括同步对象的句柄,等待时间,是等待一个还是多个同步对象等等。

在以下情况下等待函数返回:

一个或全部同步对象获得信号时返回(在参数中指定是等待一个或多个同步对象);

等待时间达到了返回:如果等待时间不限制(Infinite),则只有同步对象获得信号才返回;如果等待时间为0,则在测试了同步对象的状态之后马上返回。

4、程序实例

下列程序创建了5个线程,每个线程分别打印一行数字。对该程序运行多次,查看运行结果是否有不同。

#define WIN32_LEAN_AND_MEAN

#include

#include

#include

DWORD WINAPI ThreadFunc(LPVOID);

int main()

{

HANDLE hThrd;

DWORD threadId;

int i;

for (i=0; i<5; i++)

{

hThrd = CreateThread(NULL,

0,

ThreadFunc,

(LPVOID)i,

0,

&threadId );

if (hThrd)

{

printf("Thread launched %d\n", i);

CloseHandle(hThrd);

}

}

// Wait for the threads to complete.

Sleep(2000);

return EXIT_SUCCESS;

}

DWORD WINAPI ThreadFunc(LPVOID n)

{

int i;

for (i=0;i<10;i++)

printf("%d%d%d%d%d%d%d%d\n",n,n,n,n,n,n,n,n);

return 0;

}

实验五动态分区分配算法的模拟

一、实验目的

1、加深操作系统内存管理过程的理解

2、掌握内存分配算法的基本应用

二、实验任务

请同学们用C/C++实现一个完整的(可变)动态分区管理器,包括分配,回收,分区碎片整理等。希望同学们实现如下功能:

?初始化功能:内存状态设置为初始状态。

?分配功能:要求至少使用两种算法,用户可以选择使用。

?回收功能:

?空闲块的合并:即紧凑功能,用以消除碎片。当做碎片整理时,需要跟踪分配的空间,

修改其引用以保证引用的正确性。

?显示当前内存的使用状态,可以使用表格或图形。

三、实验指导

1.基本思想

动态分区是指系统不预先划分固定分区,而是在装入程序的时候划分内存区域,使得为程序分配的分区大小恰好等于该程序的需求量,且分区的个数是动态的。显然动态分区有较大的灵活性,较之固定分区能获得好的内存利用率。

2.数据结构

动态分区管理可以用两种数据结构实现,一种是已分配区表和空闲区表,也就是用预先定义好的系统空间来存放空间分配信息。

另一种也是最常用的就是空闲链表,由于对分区的操作是动态的,所以很难估计数据结构所占用的空间,而且空闲区表会占用宝贵的系统空间,所以提出了空闲链表的概念。其特点是用于管理分区的信息动态生成并和该分区在物理地址上相邻。这样由于可以简单用两个空闲块之间的距离定位已分配空间,不仅节约了系统空间,而且不必维持已分配空间的信息。

本实验是要做一个模拟程序,来模拟动态分区算法的分配和回收过程,并不是真正的去分配和回收内存。基本的模拟方法有两种:

1、先从内存中申请一块存储区,对这块存储区进行模拟的分配和回收活动。

2、不申请存储区,自己定义一块虚拟的存储区,对这块存储区进行模拟的分配和回收活动,分配和回收仅仅是对数据结构的修改而已。

实验六 Linux系统的深入使用

一、实验目的

掌握Linux下进程和作业的控制

掌握Linux下内存管理、文件管理

掌握Linux下用户管理、RPM包管

二、实验任务

练习使用进程和作业的控制命令

练习使用内存管理、文件管理命令

练习使用用户管理、RPM包管命令

三、实验指导

参考《Linux上机实践教程》第四、八、九章内容。

相关文档