操作系统第三版实验指导Word格式.docx
《操作系统第三版实验指导Word格式.docx》由会员分享,可在线阅读,更多相关《操作系统第三版实验指导Word格式.docx(25页珍藏版)》请在冰豆网上搜索。
![操作系统第三版实验指导Word格式.docx](https://file1.bdocx.com/fileroot1/2022-12/17/5f7a571a-2f4d-4749-a125-71ae371c6593/5f7a571a-2f4d-4749-a125-71ae371c65931.gif)
可执行文件>
启动后台进程的命令格式为:
bg<
解释程序在前台进程运行期间需要一直等待,直到前台进程运行结束才打印命令提示符,而在后台进程运行期间不必等待,会立刻打印出命令提示符,允许用户输入下一条命令。
命令解释程序中还需要捕获Ctrl+C组合键的信号来结束前台正在运行的进程,并返回用户输入界面,等待新命令输入。
(4)其他要求。
该命令解释程序应该具有相应的出错提示功能。
程序每次接收用户输入的一行命令,在用户按下回车键(Enter)后开始执行命令。
空命令只打印一个新的提示行,不做其他处理。
定义空格为分隔符,程序应能处理命令行中出现的重复空格符。
提供帮助命令help,供使用者查询每个命令的用法。
1.2.2进一步要求
(1)实现管道命令。
命令格式为:
<
命令>
{|<
}
管道命令的作用是将管道分隔符|前一个命令的输出作为后一个命令的输入。
(2)仿照MS-DOSCommand命令解释程序对现有命令语法进行改进,实现命令参数处理功能。
例如dir命令,附加/A(显示具有指定属性的文件),附加/B(使用空格式),附加/C(在文件大小中显示千位数分隔符)等参数。
如dir /A。
(3)实现进程的前台/后台切换命令,这需要查阅相关WindowsAPI来解决。
1.2.3实验步骤建议
(1)熟悉Windows相关API函数的调用。
(2)编写小程序练习使用这些系统调用。
(3)编写命令解释器设计文档。
(4)按照设计文档编写代码。
(5)不断完善程序细节。
(6)测试。
(7)写实验报告(包括需求、设计、测试和使用说明等内容,格式可参考1.6节源程序与运行结果之“实验报告模板”)。
1.3相关基础知识
1.3.1命令解释程序与内核的关系
命令解释程序是用户和系统内核之间的接口程序。
对于Windows系统来说,由于已经提供了具有良好交互性的图形用户界面,传统的控制台命令解释程序已经很少被广大用户所了解和使用了。
但是,对于某些应用,例如删除所有扩展名为tmp的文件,或是删除某些具有特殊名字的病毒文件,在图形用户界面下很难甚至不能完成,这时需要通过Windows提供的Command命令接口来完成。
Command程序是一个命令语言解释器,它拥有自己内建的命令集,用户或其他应用程序都可通过对Command程序的调用完成与系统内核的交互。
我们可以把系统内核想象成一个球体的中心,Command命令解释程序就是包围内核的外壳,如图1-2所示。
图1-2硬件、内核、系统调用以及Command之间的层次关系
1.3.2系统调用
应用程序是以函数调用的方式来通知操作系统执行相应的内核功能。
操作系统所能够完成的每一个特殊功能通常都有一个函数与其对应,即操作系统把它所能够完成的功能以函数的形式提供给应用程序使用。
应用程序对这些函数的调用叫做系统调用,这些函数的集合就是Windows操作系统提供给应用程序编程的接口(ApplicationProgrammingInterface),简称WindowsAPI或Win32API(注:
某些Win32API,如管理Windows线程的API等,它们并没有操纵内核对象,因此不是系统调用。
本实验只讨论API的使用,不再做进一步区分)。
所有在Win32平台上运行的应用程序都可以调用这些函数。
使用WindowsAPI,应用程序可以充分挖掘Windows的32位操作系统的潜力。
Microsoft的所有32位平台都支持统一的API,包括函数、结构、消息、宏及接口。
使用WindowsAPI不但可以开发出在各种Windows平台上都能运行的应用程序,而且也可以充分利用每个平台特有的功能和属性。
Windows的相关API都可以在MSDN中查到,包括定义、使用方法等。
下面简单介绍本次实验中所涉及的WindowsAPI。
1.GetCurrentDirectory函数
功能说明
查找当前进程的当前目录,调用成功返回装载到lpBuffer的字节数。
格式
DWORDGetCurrentDirectory(
DWORDnBufferLength,
LPTSTRlpBuffer
)
参数说明
nBufferLength:
缓冲区的长度。
lpBuffer:
指定一个预定义字串,用于装载当前目录。
2.WaitForSingleObject函数
等待一个事件信号直至该信号出现或是超时。
若有信号则返回WAIT_OBJECT_0,若等待超过dwMilliseconds时间还是无信号则返回WAIT_TIMEOUT。
DWORDWaitForSingleObject(
HANDLEhHandle,
DWORDdwMilliseconds
hHandle:
事件的句柄。
dwMilliseconds:
最大等待时间,以ms计时。
3.SetCurrentDirectory
设置当前目录,非0表示成功,0表示失败。
BOOLSetCurrentDirectory(
LPCTSTRlpPathName
lpPathName:
新设置的当前目录的路径。
4.FindFirstFile函数
该函数用于到一个文件夹(包括子文件夹)中搜索指定文件,由这个函数返回的句柄可以作为一个参数用于FindNextFile函数。
这样一来,就可以方便地枚举出与lpFileName参数指定的文件名相符的所有文件。
HANDLEFindFirstFile(
LPCTSTRlpFileName,
LPWIN32_FIND_DATAlpFindFileData
lpFileName:
文件名字符串。
lpFindFileData:
指向一个用于保存文件信息的结构体。
5.FindNextFile函数
继续查找FindFirstFile函数搜索后的文件。
由这个函数返回的句柄可以作为一个参数用于FindNextFile()函数。
BOOLFindNextFile(
HANDLEhFindFile,
hFindFile:
前一个搜索到的文件的句柄。
6.GetVolumeInformation函数
用于获取磁盘相关信息。
BOOLGetVolumeInformation(
LPCTSTRlpRootPathName,
LPTSTRlpVolumeNameBuffer,
DWORDnVolumeNameSize,
LPDWORDlpVolumeSerialNumber,
LPDWORDlpMaximumComponentLength,
LPDWORDlpFileSystemFlags,
LPTSTRlpFileSystemNameBuffer,
DWORDnFileSystemNameSize
lpRootPathName:
磁盘驱动器代码字符串。
lpVolumeNameBuffer:
磁盘驱动器卷标名称。
nVolumeNameSize:
磁盘驱动器卷标名称长度。
lpVolumeSerialNumber:
磁盘驱动器卷标序列号。
lpMaximumComponentLength:
系统允许的最大文件长度。
lpFileSystemFlags:
文件系统标识。
lpFileSystemNameBuffer:
文件系统名称。
nFileSystemNameSize:
文件系统名称长度。
7.GetDiskFreeSpaceEx函数
获取与一个磁盘的组织以及剩余空间容量有关的信息。
BOOLGetDiskFreeSpaceEx(
PULARGE_INTEGERlpFreeBytesAvailableToCaller,
PULARGE_INTEGERlpTotalNumberOfBytes,
PULARGE_INTEGERlpTotalNumberOfFreeBytes,
不包括卷名的磁盘根路径名。
lpFreeBytesAvailableToCaller:
调用者可用的字节数量。
lpTotalNumberOfBytes:
磁盘上的总字节数。
lpTotalNumberOfFreeBytes:
磁盘上可用的字节数。
8.FileTimeToLocalFileTime函数
将一个FILETIME结构转换成本地时间。
BOOLFileTimeToLocalFileTime(
constFILETIME*lpFileTime,
LPFILETIMElpLocalFileTime
lpFileTime:
包含了UTC时间信息的一个结构。
lpLocalFileTime:
用于装载转换过的本地时间的结构体。
9.FileTimeToSystemTime函数
根据一个FILETIME结构的内容,装载一个SYSTEMTIME结构。
BOOLFileTimeToSystemTime(
constFILETIME*lpFileTime,
LPSYSTEMTIMElpSystemTime
包含了文件时间的一个结构。
lpSystemTime:
用于装载系统时间信息的一个结构体。
10.CreateToolhelp32Snapshot函数
为指定的进程、进程使用的堆(heap)、模块(module)、线程(thread)建立一个快照(snapshot)。
快照建立成功则返回快照的句柄,失败则返回INVAID_HANDLE_VALUE。
HANDLEWINAPICreateToolhelp32Snapshot(
DWORDdwFlags,
DWORDth32ProcessID
dwFlags:
指定快照中包含的系统内容。
th32ProcessID:
指定将要快照的进程ID。
11.Process32First函数
Process32First是一个进程获取函数,当使用CreateToolhelp32Snapshot()函数获得当前运行进程的快照后,可以使用Process32First()函数获得第一个进程的句柄。
BOOLWINAPIProcess32First(
HANDLEhSnapshot,
LPPROCESSENTRY32lppe
)
hSnapshot:
快照句柄。
lppe:
指向一个保存进程快照信息的PROCESSENTRY32结构体。
12.Process32Next函数
获取进程快照中下一个进程信息。
BOOLWINAPIProcess32Next(
HANDLEhSnapshot,
由Process32First()函数或Process32Next()函数获得的快照句柄。
13.OpenProcess函数
OpenProcess()函数打开一个已存在的进程对象。
若成功,返回值为指定进程的打开句柄。
若失败,返回值为空。
HANDLEOpenProcess(
DWORDdwDesiredAccess,
BOOLbInheritHandle,
DWORDdwProcessId
dwDesiredAccess:
权限标识。
bInheritHandle:
句柄继承标识。
dwProcessId:
进程ID。
14.SetConsoleCtrlHandler函数
添加或删除一个事件钩子(handler)。
BOOLSetConsoleCtrlHandler(
PHANDLER_ROUTINEHandlerRoutine,
BOOLAdd
HandlerRoutine:
回调函数的指针。
Add:
表示添加或是删除。
15.CreateProcess函数
创建一个新的进程和它的主线程,这个新进程运行指定的可执行文件。
BOOLCreateProcess(
LPCTSTRlpApplicationName,
LPTSTRlpCommandLine,
LPSECURITY_ATTRIBUTESlpProcessAttributes,
LPSECURITY_ATTRIBUTESlpThreadAttributes,
BOOLbInheritHandles,
DWORDdwCreationFlags,
LPVOIDlpEnvironment,
LPCTSTRlpCurrentDirectory,
LPSTARTUPINFOlpStartupInfo,
LPPROCESS_INFORMATIONlpProcessInformation
lpApplicationName:
指定可执行模块的字符串。
lpCommandLine:
指定要运行的命令行。
lpProcessAttributes:
决定返回句柄能否被继承,该参数定义了进程的安全特性。
lpThreadAttributes:
决定返回句柄能否被继承,该参数定义了进程之主线程的安全特性。
bInheritHandles:
表示新进程是否从调用进程处继承了句柄。
dwCreationFlags:
控制优先类和进程的创建标志。
lpEnvironment:
指向一个新进程的环境块。
lpCurrentDirectory:
指定子进程的工作路径。
lpStartupInfo:
决定新进程的主窗体显示方式的结构体。
lpProcessInformation:
接收新进程的识别信息。
16.GetExitCodeProcess函数
获取一个已中断进程的退出代码。
BOOLGetExitCodeProcess(
HANDLEhProcess,
LPDWORDlpExitCode
hProcess:
为进程句柄。
lpExitCode:
指向接受退出码的变量。
17.TerminateProcess函数
以给定的退出码终止一个进程。
BOOLTerminateProcess(
HANDLEhProcess,
UINTuExitCode
进程句柄。
uExitCode:
进程退出码。
1.3.3重要API的使用说明
这里详细介绍实验中涉及的、重要的WindowsAPI函数。
1.创建进程
Windows中使用CreateProcess()函数创建进程,与Linux中的fork()函数有所不同。
Windows里的进程/线程是继承自OS/2的。
在Windows中,“进程”是指一个程序,而“线程”是一个“进程”里的一个执行“线索”。
从核心上讲,Windows与Linux的多进程并无多大的区别,Windows中的线程相当于Linux中的进程,是一个实际正在执行的代码,同一个进程里的各个线程之间是共享数据段的。
在Windows中,当使用CreateProcess()创建一个进程时,系统也会为其创建一个主线程,并从指定的可执行代码处开始运行,而不像Linux那样从创建处开始运行。
此后,可以使用CreateThread()函数为进程创建更多的线程。
CreateProcess()函数包含了多个参数,这里只介绍几个相关的参数,其他参数使用默认值。
LPCTSTRlpCurrentDirectory,
主要参数说明
这是指向一个NULL结尾的、用来指定可执行模块的字符串。
这个字符串可以是可执行模块的绝对路径,也可以是相对路径。
在后一种情况下,函数使用当前驱动器和目录建立可执行模块的路径。
这个参数可以设置为NULL,在这种情况下,可执行模块的名称必须处于lpCommandLine参数的最前面并由空格符与后面的字符分开。
指向一个NULL结尾的、用来指定要运行的命令行。
这个参数可以为空,这时函数将使用参数指定的字符串当作要运行的程序的命令行。
如果lpApplicationName和lpCommandLine参数都不为空,那么lpApplicationName参数指定将要被运行的模块,lpCommandLine参数指定将被运行的模块的命令行。
指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体,包括窗口的显示位置、大小,是否有输入和输出及错误输出(具体参见MSDN的参数说明)。
其中输出句柄可以用于进程的管道通信。
使用这个结构体时要注意先要初始化它的大小,当进程创建的时候可以用GetStartupInfo来获得STARTUPINFO结构体。
指向一个用来接收新进程的识别信息的PROCESS_INFOR-MATION结构体。
其中包含了新进程的多个信息。
例如进程句柄、进程主线程的句柄、进程ID、主线程ID。
通过获得的进程信息即可对该进程进行进一步操作。
2.处理控制台消息
本实验需要在用户按下Ctrl+C组合键时终止当前进程的运行,这需要程序能够处理相应的控制台消息。
处理控制台消息首先需要安装一个事件钩子,可通过SetConsoleCtrl-Handler()函数来完成。
值为TRUE时表示添加一个事件钩子,为FALSE时则表示删除对应的事件钩子。
指向处理消息事件的回调函数的指针,原型如下
BOOLWINAPIHandlerRoutine(
DWORDdwCtrlType
所有的HandlerRoutine()函数都只有一个参数dwCtrlType,表示控制台发出了什么消息,可能有以下取值。
∙Ctrl_C_Event:
用户按下Ctrl+C组合键,或者由GenerateConsoleCtrlEventAPI发出。
∙Ctrl_Break_Event:
用户按下Ctrl+Break组合键,或者由GenerateConsoleCtrlEventAPI发出。
∙Ctrl_Close_Event:
当试图关闭控制台程序时,系统发送关闭消息。
∙Ctrl_Logoff_Event:
用户退出时,但是不能确定是哪个用户。
∙Ctrl_Shutdown_Event:
当系统被关闭时的取值。
当收到事件消息时,HandlerRoutine()可以选择处理,或是简单的忽略。
如果回调函数选择忽略,函数返回FALSE,系统将处理下一个钩子程序。
如果选择处理消息,程序在处理完消息后应该返回TRUE。
1.4实验设计
本实验是在WindowsXP+VC6.0环境下实现的,利用WindowsSDK提供的系统接口(API)完成程序的功能。
实验在Windows系统下安装VC后进行,因为VC是一个集成开发环境,其中包含了WindowsSDK所有工具和定义,所以安装了VC后就不用特意安装SDK了。
实验中所用的API,即应用程序接口,是操作系统提供的、用以进行应用程序设计的系统功能接口。
要使用这些API,需要包含对这些函数进行说明的SDK头文件,最常见的就是windows.h。
一些特殊的API调用还需要包含其他的头文件。
由于本实验涉及了较多的API,所以建议安装MSDN作为参考。
1.4.1重要的数据结构
1.历史命令循环数组
在history命令中,用数组来存放我们输入过的历史命令。
假设一个能够记录12条历史记录的数组如图1-3(a)所示。
数组的定义如下:
typedefstructENV_HISTROY{
intstart=0;
intend=0;
charhis_cmd[12][100];
}ENV_HISTORY;
ENV_HISTORYenvhis;
可以看到,每个his_cmd[i]对应图中一块圆环,一共12块,能存放12条命令。
当用户输入一个命令时,只需执