这里的父子进程通信,其实说白了就是一个上课时候学的PV操作,是一个进程的同步,而终止进程,基本上就是关闭创建子进程的句柄,回收给子进程的资源,从而关闭这个进程。其中出现了以下的问题;
在没有修改之前:
wsprintf(szCmdLine, "\"%s\" chid" , szFilename) ; //将szFilename 和 nCloneID 以规定的格式写入到szCmdLine中 STARTUPINFO si; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); PROCESS_INFORMATION pi; //这个结构体将存储CreateProcess返回的创建新的进程的一些信息以及他的主要线程 BOOL bCreateOK = ::CreateProcess( szFilename, szCmdLine, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi );在利用createprocess函数创建进程的时候,在命令行中就需要szCmdLine这个变量才作为argv中的的参数来创建这个子进程。由于两个参数是分开的,所以在wsprintf函数里面的"\"%s\" chid"这个部分,%s这里面要填入的是可执行文件的路径,而child是为了要创建子进程。所以两个参数之间要添加上空格才可以。
修改第一次之后:
wsprintf(szCmdLine, "\"%s\" chid" , szFilename) ;将此函数的child部分改为一个其他的字符串,那么在创建进程的时候,第二个参数就不再是child,很自然,在创建子进程的时候,进入的不再是child函数,而是parent函数,所以,程序就在不断的创建子进程,没有停止的趋势。创建进程函数的大致工作原理如下:
BOOL CreateProcess( LPCWSTR pszImageName, LPCWSTR pszCmdLine, LPSECURITY_ATTRIBUTES psaProcess, LPSECURITY_ATTRIBUTES psaThread, BOOL fInheritHandles, DWORD fdwCreate, LPVOID pvEnvironment, LPWSTR pszCurDir, LPSTARTUPINFOW psiStartInfo, LPPROCESS_INFORMATION pProcInfo );
第二次修改之后,修改的是WaitForSingleObject(hMutexSuicide, INFINITE);,将INFINITE变为0执行这个程序。
由于整个程序要显示的是父子进程的通信,那么在父子进程的同步正式通过pv操作来完成的。
P操作:WaitForSingleObject(hMutexSuicide, INFINITE);
V操作:ReleaseMutex(hMutexSuicide)
通过互斥体的方式,利用互斥的机制来实现同步,此程序并没有明显的临界区,如果非要划分一个临界区的话,那么就应该是parent函数里面的getchar(),因为在没有修改之前,父进程必须执行完这个语句的时候,才会执行接下来的V操作,从而让子进程继续执行下去。
子进程的P操作函数,接受到了hMutexSuicide这个信号量的信息,得以获得互斥体,进入临界区,则继续执行子进程,最后关闭进程,执行CloseHandle(hMutexSuicide);语句。至此,父子进程的通讯和进程的结束伴随着3次修改都已经完成。
#include<windows.h> #include<iostream> #include<stdio.h> #include<cstring> using namespace std; static LPCTSTR g_szMutexName = "w2kdg.ProcTerm.mutex.Suicide"; void StartClone() { TCHAR szFilename[MAX_PATH]; GetModuleFileName(NULL, szFilename, MAX_PATH); //返回 文件名,包括这个执行文件的路径,都同统一写入szFilename这个缓冲区里面,如果路径名或者文件名超过了MAX_PATH将会截断多出的部分 TCHAR szCmdLine[MAX_PATH]; wsprintf(szCmdLine, "\"%s\" child" , szFilename) ; //将szFilename 和 nCloneID 以规定的格式写入到szCmdLine中 STARTUPINFO si; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); PROCESS_INFORMATION pi; //这个结构体将存储CreateProcess返回的创建新的进程的一些信息以及他的主要线程 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 ); /* 互斥体实现了“互相排斥”(mutual exclusion)同步的简单形式(所以名为互斥体(mutex))。 互斥体禁止多个线程同时进入受保护的代码“临界区”(critical section)。 因此,在任意时刻,只有一个线程被允许进入这样的代码保护区。任何线程在进入临界区之前, 必须获取(acquire)与此区域相关联的互斥体的所有权。如果已有另一线程拥有了临界区的互斥体, 其他线程就不能再进入其中。这些线程必须等待,直到当前的属主线程释放(release)该互斥体。 */ if(hMutexSuicide != NULL) { cout << "creating the child process" << endl; StartClone(); cout << "telling the child process to quit" << endl; getchar(); ReleaseMutex(hMutexSuicide); /* 一个线程释放了互斥对象的控制权后,如果其他进程在等待互斥对象置位, 则等待的线程可以得到该互斥对象,等待函数返回,互斥对象被新的线程所拥有。 并且会发送信号给waitforsingleobject */ CloseHandle(hMutexSuicide); } } void Child() { HANDLE hMutexSuicide = OpenMutex( SYNCHRONIZE, FALSE, g_szMutexName ); if(hMutexSuicide != NULL) { cout << "child waiting for suicide instructions" << endl; WaitForSingleObject(hMutexSuicide, INFINITE); /* WaitForSingleObject函数用来检测hMutexSuicide事件的信号状态,在某一线程中调用该函数时 ,线程暂时挂起,如果在挂起的dwMilliseconds毫秒内, 线程所等待的对象变为有信号状态,则该函数立即返回; 如果超时时间已经到达dwMilliseconds毫秒,但hMutexSuicide所指向的对象还没有变成有信号状态,函数照样返回。 参数dwMilliseconds有两个具有特殊意义的值:0和INFINITE。若为0,则该函数立即返回; 若为INFINITE,则线程一直被挂起,直到hHandle所指向的对象变为有信号状态时为止。 */ cout << "Child quiting" << endl; getchar(); CloseHandle(hMutexSuicide); } } int main(int argc, char* argv[]) { if(argc > 1 && strcmp(argv[1], "child") == 0) { Child(); } else { Parent(); } return 0; }