写在前面
又到了 一年一度期末课设的时候,距离考试还有两周的时间,此时胡小宁还没有复习(准确的说是学习)之前的课程,所以在这个平平无奇的周一,胡小宁就要开始做课设了!
实验一 Windows 进程管理
实验目的
(1)学会使用 VC 编写基本的 Win32 Consol Application(控制台应用程序)。
(2)通过创建进程、观察正在运行的进程和终止进程的程序设计和调试操作,进一步熟悉操
作系统的进程概念,理解 Windows 进程的“一生”。
(3)通过阅读和分析实验程序,学习创建进程、观察进程、终止进程以及父子进程同步的基
本程序设计方法。
实验内容和步骤
(1) 编写基本的 Win32 Consol Application:
指导书上的步骤有很多缺陷。我在Windows上做课设所使用的编译器是CodeBlocks,所以在操作和代码方面与指导书给的标准有些出入。这里创建的是Consol Application。
代码如下:
1 2 3 4 5 6
| #include <iostream> using namespace std; int main() { cout << "Hello, Win32 Consol Application" << endl ; }
|
这样就完成了编写基本的Consol Application。
分析:按照指导书的操作运行会不成功,原因有二。一是编译器的不同,CodeBlocks不允许主函数无返回类型。二是从指导书上直接复制过来的代码有许多非法字符,非法空格,这些都需要更改。
(2)创建进程
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include <windows.h> #include <iostream> #include <stdio.h>
void StartClone(int nCloneID) {
TCHAR szFilename[MAX_PATH] ; GetModuleFileName(NULL, szFilename, MAX_PATH) ;
TCHAR szCmdLine[MAX_PATH]; sprintf(szCmdLine,"\"%s\" %d",szFilename,nCloneID);
STARTUPINFO si; ZeroMemory(&si , sizeof(si) ) ; si.cb = sizeof(si) ;
PROCESS_INFORMATION pi;
BOOL bCreateOK=::CreateProcess( szFilename, szCmdLine, NULL, NULL, FALSE, CREATE_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) {
:: sscanf(argv[1] , "%d" , &nClone) ; }
std :: cout << "Process ID:" << :: GetCurrentProcessId() << ", Clone ID:" << nClone << std :: endl;
const int c_nCloneMax=5; if (nClone < c_nCloneMax) {
StartClone(++nClone) ; }
getchar(); return 0; }
|
运行结果如下:
修改nClone=1;
运行结果如下:
修改nClone=2;
运行结果如下:
如上是nClone取值不同所产生的不同运行结果。可以看出,进程ID起始值同nClone的值相同。如果将nClone的初始化位置换在下方,将会有非常严重的后果产生—进程创建的死循环!不一会儿你的电脑内存就炸了!!!
分析:程序从main函数开始,创建进程,每次引用该进程exe文件位置创建下一个进程(再反过来继续调用main函数,如此循环,使用c_nCloneMax作为限制)。因为调换了nClone的初始化位置,会使nClone永远达不到c_nCloneMax,造成死循环。
(3)父子进程的简单通信及终止进程
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| # include <windows.h> # include <iostream> # include <stdio.h> static LPCTSTR g_szMutexName = "w2kdg.ProcTerm.mutex.Suicide" ;
void StartClone() {
TCHAR szFilename[MAX_PATH] ; GetModuleFileName(NULL, szFilename, MAX_PATH) ;
TCHAR szCmdLine[MAX_PATH] ;
sprintf(szCmdLine, "\"%s\" child", szFilename) ;
STARTUPINFO si; ZeroMemory(&si,sizeof(si)) ; si.cb = sizeof(si) ;
PROCESS_INFORMATION pi;
BOOL bCreateOK=CreateProcess( szFilename, szCmdLine, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi ) ;
if (bCreateOK) { CloseHandle(pi.hProcess) ; CloseHandle(pi.hThread) ; } } void Parent() {
HANDLE hMutexSuicide=CreateMutex( NULL, TRUE, g_szMutexName) ; if (hMutexSuicide != NULL) {
std :: cout << "Creating the child process." << std :: endl; StartClone() ;
std :: cout << "Telling the child process to quit. "<< std :: endl;
getchar() ;
ReleaseMutex(hMutexSuicide) ;
CloseHandle(hMutexSuicide) ; } } void Child() {
HANDLE hMutexSuicide = OpenMutex( SYNCHRONIZE, FALSE, g_szMutexName) ; if (hMutexSuicide != NULL) {
std :: cout <<"Child waiting for suicide instructions. " << std :: endl;
WaitForSingleObject(hMutexSuicide, INFINITE) ;
std :: cout << "Child quiting." << std :: endl; CloseHandle(hMutexSuicide) ; } } int main(int argc, char* argv[] ) {
if (argc>1 && :: strcmp(argv[1] , "child" )== 0) { Child() ; } else { Parent() ; } return 0; }
|
在利用CreateProcess函数创建进程时,命令行中需要szCmdLine来作为argv的参数创建这个子进程,两个参数是分开的, sprintf()函数中的”\”%s\” child”,%s填入的是可执行文件的路径,child则是创建子进程。两个参数之间必须加上空格才正确。如果只是抄过来指导书上的代码,将会进入死循环。内存炸了哦!
步骤3:第一次修改后,创建进程的第二个参数不是child了,因此子进程进入的是parent函数,所以程序在不断的创建子进程,死循环。
步骤4:第二次修改后,WaitForSingleObject(hMutexSuicide, INFINITE);INFINITE变成了0,所以会进入临界区,继续执行子进程,最后关闭子进程并关闭句柄。
整个程序要体现的是父子进程通信,父子进程的同步是由PV操作完成的。
P操作:WaitForSingleObject(hMutexSuicide, INFINITE);
V操作:ReleaseMutex(hMutexSuicide)
HANDLE hMutexSuicide = CreateMutex(NULL,TRUE,g_szMutexName);
互斥体实现了“互相排斥”(mutual exclusion)同步的简单形式(所以名为互斥体(mutex))。
互斥体禁止多个线程同时进入受保护的代码“临界区”(critical section)。
因此,在任意时刻,只有一个线程被允许进入这样的代码保护区。任何线程在进入临界区之前,
必须获取(acquire)与此区域相关联的互斥体的所有权。如果已有另一线程拥有了临界区的互斥体,其他线程就不能再进入其中。这些线程必须等待,直到当前的属主线程释放(release)该互斥体。
ReleaseMutex(hMutexSuicide);
一个线程释放了互斥对象的控制权后,如果其他进程在等待互斥对象置位,则等待的线程可以得到该互斥对象,等待函数返回,互斥对象被新的线程所拥有。并且会发送信号给waitforsingleobject
WaitForSingleObject(hMutexSuicide, INFINITE);
WaitForSingleObject函数用来检测hMutexSuicide事件的信号状态,在某一线程中调用该函数时,线程暂时挂起,如果在挂起的dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回;如果超时时间已经到达dwMilliseconds毫秒,但hMutexSuicide所指向的对象还没有变成有信号状态,函数照样返回。
参数dwMilliseconds有两个具有特殊意义的值:0和INFINITE。若为0,则该函数立即返回;若为INFINITE,则线程一直为阻塞状态,直到hHandle所指向的对象变为有信号状态时为止。
OpenMutex() 打开已有的互斥体