2024年1月24日发(作者:)

实验二:Windows进程控制

1. 实验目的

每个进程都有一个独立的受到保护的地址空间,其他进程不能访问。一个进程可以包含一个或更多的线程。进程能够在其内部创建新的、独立的线程,并且管理对象间的通信和同步。

通过对Windows系统编程,进一步熟悉操作系统的基本概念,较好地理解Windows操作系统的系统结构和编程特点。

2. 进程控制

Windows所创建的每个进程都从调用CreateProcess() API函数开始,该函数的任务是在对象管理器子系统内初始化进程对象。每一进程都以调用ExitProcess()或TerminateProcess()

API函数终止。通常应用程序的框架负责调用ExitProcess()函数。对C++运行库来说,这一调用发生在应用程序的main()函数返回之后,如果采用C运行库,则调用WinMain()函数。

通过创建进程、观察正在运行的进程和终止进程的程序设计和调试操作,进一步熟悉操作系统的进程概念,理解Windows进程的生命周期。

2.1 进程控制相关的API

基本的Win32进程管理函数是CreateProcess,它可以创建拥有单个线程的进程。因为进程需要代码,所以有必要指定可执行程序文件名作为CreateProcess调用的一部分。

CreateProcess有10个参数支持其灵活性和强大功能。该函数并不返回一个HANDLE,而是在一个结构(在调用中指定)中返回表示进程和线程的两个句柄。

2.1.1 创建进程CreateProcess()函数

函数格式:

BOOL CreateProcess(LPCTSTR lpApplicationName, LPTSTR lpCommandLine,

LPSECURITY_ATTRIBUTES lpProcessAttributes,

LPSECURITY_ATTRIBUTES lpThreadAttributes,

BOOL bInheritHandles, DWORD dwCreationFlags,

LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory,

LPSTRATUPINFO lpStartupInfo,

LPPROCESS_INFORMATION lpProcessInformation);

参数:

(1)lpszApplicationName和lpCommandLine指定了新进程将使用的可执行文件和传递给新进程的命令行字符串。

lpszCommandLine可以设定CreateProcess中用于创建新进程的命令行。CreateProcess在解析lpszCommandLine字符串的时候,它先查看字符串中的第一个符号。如果它是一个可执行文件名且不含有扩展名,就假定它的扩展名为EXE。CreateProcess将按照以下顺序来搜索可执行文件:

1)含有调用进程的EXE文件的目录;

2)调用进程的当前目录;

3)Windows系统目录,该目录由GetSystemDirectory函数得到;

4)Windows目录,该目录由GetWindowsDirectory函数得到;

5)列在PATH环境变量中的目录。

当然,如果文件名中包含完整的路径,系统就使用完整路径搜索可执行文件。如果系统找到了可执行文件,就创建一个新进程,并为它生成一个4GB的地址空间,从而使可执行文件的代码和数据映射到这个地址空间。

(2)lpProcessAttribute和lpThreadAttribute是指向进程和线程安全属性结构的指针。当用NULL表示时,为默认的安全性。

(3)FInheritHandles表明新进程是否继承调用进程的打开句柄的副本。继承的句柄与原来的句柄具有相同属性。

(4)FdwCreate是几个标志的组合。其中包含以下几个标志:

1)CREATE_SUSPENDED : 新进程的主线程创建时处于挂起状态,直到调用ResumeThread函数时才能运行。

2)DETACHED_PROCESS和CREATE_NEW_CONSOLE相互排斥,二者不能同时使用。第一个标志是创建没有控制台的进程,第二个标志是创建新的有控制台的进程。如果二者都没有设置,进程将继承父进程的控制台。

3)CREATE_NEW_PROCESS_GROUP指定新进程是新进程组的根进程。如果组中所有的进程都共享同一控制台,则它们都将接收控制台的控制信号。

(5)lpvEnvironment指向新进程的环境块。如果此值为NULL,进程会使用父进程的环境块。环境块包含名称和值字符串,如搜索路径。

(6)lpszCurDir指向新进程的驱动器和目录。若为NULL,将使用父进程的工作目录。

(7)lpsiStartInfo指向新进程的主窗口外观和标准设备句柄。使用GetStartupInfo函数得到父进程信息。

(8)lpProcessInformation指向包含返回的进程和线程句柄、进程和线程标识符的PROCESS_INFORMATION结构的指针。

返回值:如果进程和主线程创建成功,则返回TRUE。

该函数可使系统创建一个进程内核对象和一个线程内核对象。且打开进程和线程对象,并将与进程相关的每个对象句柄放入PROCESS_INFORMATION的结构中。

PROCESS_INFORMATION结构定义如下:

Typedef struct _PROCESS_INFORMATION

{

HANDLE hProcess;//新创建进程的句柄

HANDLE hThread;//新创建进程的主线程的句柄

DWORD dwProcessId;//新创建进程的标识

DWORD dwThreadId;//新创建进程的主线程的标识

}PROCESS_INFORMATION, *LPPROCESS_INFORMATION;

为了保护被创建的对象,系统定义了对象的安全属性结构,其定义如下:

Typedef struct _SECURITY_AFFRIBUTES

{

LPVOID lpSecurityDesriptor;

BOOL hInheritHandle;

}

其中,nLength代表这个结构的以字节为单位的大小,lpSecurityDescriptor是控制共享该对象的安全描述符的指针。如果该值为0,该对象被赋予默认的安全描述符。bInheritHandle是一个布尔值,指示返回的对象句柄是否可被新创建进程继承,TRUE表示可以继承。

2.1.2 获得当前进程的标识符GetCurrentProcessId()函数

函数格式:

DWORD GetCurrentProcessId(void);

参数:无。

返回值:返回当前进程的标识符。

2.1.3 挂起当前的执行线程Sleep()函数

函数格式:

VOID Sleep(DWORD dwMilliseconds);

参数:dwMilliseconds为指定的挂起执行线程的时间,以毫秒为单位。取值为0时,该线程将余下的时间片交给处于就绪状态的同一优先级的其他线程。若没有处于就绪状态的同一状态的其他线程,则函数立即返回,该线程继续执行。若取值为INFINITE则造成无限延迟。

返回值:该函数没有返回值。

2.1.4 关闭对象CloseHandle()函数

函数格式:

BOOL CloseHandle(HANDLE hObject);

参数:hObject代表一个已打开对象的句柄。

返回值:执行成功返回TRUE,否则返回FALSE,并可以调用GetLastError()获知失败原因。

关闭一个内核对象,其中包括文件、文件映射、进程、线程、安全和各种同步对象等。在创建或打开对象成功返回该对象的句柄时,系统会为该对象设置一个打开计数,且将该内核对象的计数加1.该函数的作用与释放动态申请的内存空间类似,这样可以保证系统资源不会泄漏,程序可以在安全的状态下运行。CloseHandle()函数使指定的对象句柄数减1。当对象的句柄计数为0时,该对象就从系统中被删除。

2.1.5 创建子进程的程序示例

例1: 通过显示创建子进程的基本框架,来观察子进程的创建情况。

// 创建子进程的文件proc_

#include

#include

#include

// 创建一个克隆的进程并赋予其ID值

void StartClone(int nCloneID)

{

// 创建子进程命令行的格式化,获得应用程序的EXE文件名和克隆进程的ID值

TCHAR szCmdLine[MAX_PATH];

sprintf((char *)szCmdLine, ""%s"%d", szFilename, nCloneID);

// 获得用于当前可执行文件的文件名

TCHAR szFilename[MAX_PATH];

GetModuleFileName(NULL, szFilename, MAX_PATH);

}

STARTUPINFO si;// 用于子进程的STARTUPINFO结构

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

=sizeof(si);

PROCESS_INFORMATION pi;// 说明一个用于记录子进程的相关信息的结构变量

BOOL bCreateOK = CreateProcess(

szFilename,

szCmdLine,

NULL,

NULL,

//可执行的应用程序的名称

//指定创建一个子进程的符号标识

//缺省的进程安全性

//缺省的线程安全性

//不继承打开文件的句柄

//使用新的控制台

//新的环境

//当前目录

//返回进程和线程信息

FALSE,

NULL,

NULL,

&si,

&pi);

CREATE_NEW_CONSOLE,

//启动信息

// 运行结束,关闭进程和其线程的句柄

if(bCreateOK)

{

}

CloseHandle(ss);

CloseHandle(d);

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

{

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

int nClone(0);

if(argc > 1)

{

}

// 显示进程位置

printf("Process ID:%d,Clone ID: %dn", GetCurrentProcessId(), nClone);

// 创建2个子进程

const int c_nCloneMax = 2;

if(nClone < c_nCloneMax)

{

}

Sleep(500); // 在终止之前暂停0.5秒

StartClone(++nClone); // 发送新进程的命令行和克隆号

Sleep(1000); // 暂停1秒

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

sscanf(argv[1], "%d", &nClone);

}

return 0;

本例程序展示了一个简单的使用CreateProcess() API函数的例子。首先形成简单的命令行,提供当前的EXE文件的指定文件名和代表生成克隆进程的号码。大多数参数都可取缺省值,但是创建标志参数使用了CREATE_NEW_CONSOLE标志,指示新进程分配它自己的控制台,这使得运行该程序时,在任务栏上产生许多活动标记。然后该克隆进程的创建方法关闭传递过来的句柄并返回main()函数。在关闭程序之前,每一进程的执行主线程暂停一下,以便让用户看到其中的至少一个窗口。

2.2 进程对象相关的函数

操作系统将当前运行的应用程序看做是进程对象,利用系统提供的进程唯一的称为句柄(HANDLE)的标识,就可与对应的进程对象交互。

2.2.1 得到当前进程的句柄GetCurrentProcess()函数

函数格式:

HANDLE GetCurrentProcess(void);

参数:无。

返回值:若函数调用成功,返回值为当前进程的句柄。

2.2.2 打开一个进程OpenProcess()函数

函数格式:

HANDLE OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId);

参数:

(1)dwDesiredAccess为指定进程对象的访问权限;

(2)bInheritHandle为若要子进程获得对该对象的访问权限,就设置为TRUE,否则设为FALSE;

(3)dwProcessId为指定系统范围内的进程标识符。

返回值:若函数调用成功,则返回进程对象的句柄;否则,返回FALSE。

2.2.3 获得进程的优先级类别GetPriorityClass()函数

函数格式:

DWORD GetPriorityClass(HANDLE);

参数:HANDLE为指定进程的句柄。

返回值:若函数调用成功,返回值为指定进程的基本优先级;若函数调用失败,返回值为0。

如果要了解进程的返回码,就需要有PROCESS_QUERY_INFORMATION的访问权限。

2.2.4 改变进程优先级SetPriorityClass()函数

函数格式:

BOOL SetPriorityClass(HANDLE hProcess, DWORD dwPriorityClass);

参数:hProcessy为指定进程的句柄,dwPriorityClass为指定进程要设置的优先级。

返回值:函数成功调用,返回值为非0,否则返回值为0。

2.2.5 获得系统中当前进程的快照CreateToolhelp32Snapshot()函数

函数格式:

HANDLE CreateToolhelp32Snapshot(DWORD dwFlags, DWORD th32ProcessID);

参数:dwFlags为指定快照中包括系统线程的情况,th32ProcessID为指定进程的标识符。

返回值:若函数调用成功,则返回值为进程快照的句柄。

若参数dwFlags为TH32CS_SNAPPROCESS,则表明快照包括系统中的所有线程,且th32ProcessID为包括在快照中进程的进程标识符。

2.2.6 将指定的存储空间清0 ZeroMemory函数

函数格式:

VOID ZeroMemoery(PVOID Destination, SIZE_T Length);

参数:Destination为指向分配空间的起始地址,Length为分配空间字节长度。

返回值:该函数没有返回值。

2.2.7 获得系统中的第一个进程的快照信息 Process32First()函数

函数格式:

BOOL Process32First(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);

参数:hSnapshot为指定快照句柄;lppe为指向PROCESSENTRY32结构指针。

返回值:如果返回值为真,则将系统快照中的第一个进程信息复制到PROCESSENTRY32结构的缓冲区中,否则不做任何操作。

用Process32Next()函数指向下一个进程信息,其中PROCESSENTRY32结构定义如下:

typedef struct tagPROCESSENTRY32

{

DWORD dwSize;

DWORD cntUsage;

DWORD th32ProcessID;

ULONG_PTR th32DefaultHeapID;

DWORD th32ModuleID;

DWORD cntThreads;

DWORD th32ParentProcessID;

LONG pcPriClassBase;

DWORD dwFlags;

TCHAR szExeFile[MAX_PATH];

}PROCESSENTRY32, *PROCESSENTRY32;

2.2.8 获得系统下一个进程的快照信息Process32Next()函数

函数格式:

BOOL Process32Next(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);

参数:hSnapshot为指定快照句柄;lppe为指定一个PROCESSENTRY32结构。

返回值:如果进程列表的下一入口点被复制到缓冲区,返回值为真。

2.2.9 获得与进程相关的时间信息GetProcessTimes()函数

函数格式:

BOOL GetProcessTimes(HANDLE hProcess, LPFILETIME lpCreationTime,

LPFILETIME lpExitTime, LPFILETIME lpKernelTime,

LPFILETIME lpUserTime);

参数:

·hProcess为一个进程句柄;

·lpCreationTime为包含有进程的创建时间的一个FILETIME结构;

·lpExitTime为包含有进程的终止时间的一个FILETIME结构;

·lpKernelTime为包含有进程在核心态执行时所消耗的时间的一个FILETIME结构;

·lpUserTime为包含有进程在用户态执行时所消耗的时间的一个FILETIME结构。

返回值:如果函数成功调用,返回值为非0;如果失败,返回值为0。

2.2.10 获得进程句柄的程序示例

例2:获得和使用进程句柄

// 获得进程句柄的文件

#include

#include

// 确定自身优先级的一个简单应用程序

void main()

{

}

// 显示当前进程所属的优先级类型

printf("current process priority: ");

switch (dwPriority)

{

case HIGH_PRIORITY_CLASS:

}

printf("Highn");

break;

printf("Normaln");

break;

printf("Idlen");

break;

printf("Realtimen");

break;

printf("n");

break;

// 获得该进程所属的优先级类

DWORD dwPriority = GetPriorityClass(hProcessThis);

// 调用如下函数,以获得当前进程的句柄

HANDLE hProcessThis = GetCurrentProcess();

case NORMAL_PRIORITY_CLASS:

case IDLE_PRIORITY_CLASS:

case REALTIME_PRIORITY_CLASS:

default:

本例列出的是一种获得进程句柄的方法。对于进程句柄可进行的唯一有用的操作是在API调用时,将其作为参数传送系统,如程序中的GetPriorityClass()函数的调用所带的就是进程的句柄。在这种情况下,系统查看进程对象,以决定其优先级,然后将此优先级返回给应用程序。使用OpenProcess()和CreateProcess()函数也可以用于获得进程句柄,前者获得的是已经存在的进程的句柄,而后者创建一个新进程,并返回其句柄。

2.2.11 利用句柄获得进程详细信息的程序示例

例3:利用进程句柄查出进程的详细信息。

// 获得进程句柄信息的文件

//

#include

#include

#include

using namespace std;

#pragma comment(lib, "")

// 当在用户态及核心态下都提供所耗时间时,计算在核心态下所消耗的时间(以64位表示)

DWORD GetKernelModePercentage(const FILETIME& ftKernel,

{

}

// 应用程序显示当前运行进程名字以及在核心态下占用时间的百分比

void main()

{

//初始化过程入口

PROCESSENTRY32 pe; // 声明进程入口的PROCESSNTRY32结构的变量

// 获得系统当前进程的快照

HANDLE hSnapshot = CreateToolhelp32Snapshot(

TH32CS_SNAPPROCESS,

0);

// 获得当前进程

// 如果是当前进程,就将其忽略

// 将消耗时间相加,然后计算消耗在核心态下所用时间百分比

ULONGLONG qwTotal = qwKernel + qwUser;

DWORD dwPct = (DWORD)(((ULONGLONG)100 * qwKernel) / qwTotal);

return dwPct;

// 将FILETIME结构转化为64位整数

ULONGLONG qwKernel = (((ULONGLONG)DateTime) << 32) +

ULONGLONG qwUser = (((ULONGLONG)DateTime) << 32) +

const FILETIME& ftUser)

ateTime;

ateTime;

}

ZeroMemory(&pe, sizeof(pe) );// 为pe分配存储空间,并将其清0

= sizeof(pe);

BOOL bMore = Process32First(hSnapshot, &pe); // 获得系统快照中的第一个进程信息

while(bMore)

{

}

// 打开用于读取的进程

HANDLE hProcess = OpenProcess(

PROCESS_QUERY_INFORMATION, // 指明要得到信息

FALSE,

// 不必继承这一句柄

// 要打开的进程 32ProcessID);

if(hProcess != NULL)

{

}

// 转向下一个进程

bMore = Process32Next(hSnapshot, &pe);

// 找出进程的时间

FILETIME ftCreation, ftKernelMode, ftUserMode, ftExit;

GetProcessTimes(

hProcess,

&ftExit,

// 所感兴趣的进程

&ftCreation, // 进程的启动时间

// 结束时间

&ftKernelMode, // 在核心态下消耗的时间

&ftUserMode); // 在用户态下消耗的时间

// 计算核心态下消耗的时间百分比

DWORD dwPctKernel = GetKernelModePercentage(ftKernelMode, ftUserMode);

// 向用户显示进程的某些信息

cout << "process ID:" << 32ProcessID

<< ", EXE file:" << ile

<< ",%in Kernel mode:" << dwPctKernel << endl;

// 关闭句柄

CloseHandle(hProcess);

该程序首先利用Windows系统的新特性(即工具帮助库),来获得当前运行的所有进程的快照。然后应用程序进入快照中的每一个进程,得到其以PROCESSENTRY32结构表示的属性,这一结构用来向OpenProcess()函数提供进程的ID。Windows跟踪每一个进程的有关时间,本例是通过打开的进程句柄和GetProcessTimes()函数来直接得到有关时间,接下来计算进程在核心态下消耗的时间占总时间的百分比。程序的其余部分比较简单,只是将有关信息显示给用户,关闭进程句柄,然后继续循环,直到所有进程都计算完为止。

2.3 运行进程

对于在系统中的每个进程来说,当它至少拥有一个执行线程时就是运行的。通常情况下,进程使用主线程作为其生命周期指示器。当主线程结束时,调用ExitProcess() API函数,通知系统终止它所拥有的所有正在运行、准备运行或正在挂起的其他线程。当进程正在进行时,通过下面一些API,可以获得它的许多特性。

2.3.1 获得一个进程的启动信息GetStartupInfo()函数

函数格式:

VOID GetStartupInfo(LPSTARTUPINFO lpStartupInfo);

参数:lpStartupInf为STARTUPINFO结构,该结构用于最终载入进程的启动信息。

返回值:无。

2.3.2 获得系统关闭时当前进程与其他进程关闭的迟早情况

GetProcessShutdownParameters()函数

函数格式:

BOOL GetProcessShutdownParameters(LPDWORD lpdwLevel, LPDWORD lpdwFlags);

参数:lpdwLevel为指定关闭优先级;lpdwFlags为指定关闭标志,若指定为SHUTDOWN-NORETRY标志,如关闭一个进程超时,会出现一个重试对话框。通过设置这个标志,就可以禁止那个对话框显示出来,并直接关闭进程。

返回值:如果函数执行成功,返回值为非0,否则返回值为0。

2.3.3 系统关闭时指定当前进程与其他进程关闭的顺序SetProcessShutdownParameters()函数

函数格式:

BOOL SetProcessShutdownParameters(DWORD dwlevel, DWORD dwFlags);

参数:dwLevel指向进程关闭优先级,系统按照从高到低优先级关闭进程;dwFlags可以为SHUTDOWN_NORETRY值,当系统关闭进程时不出现RETRY对话框。

返回值:函数成功调用,返回值为非0,否则返回值为0。

2.3.4 获得当前命令行缓冲区的一个指针GetCommandLine()函数

函数格式:

LPTSTR GetCommandLine(void);

参数:无

返回值:如果函数执行成功,返回命令缓冲区在内存中的地址。

2.3.5 查询进程进行的操作系统版本信息GetProcessVersion()函数

函数格式:

DWORD GetProcessVersion(DWORD processId)

参数:ProcessId为指定进程的标识符

返回值:如果函数执行成功,返值为系统版本信息。

2.3.6 提取与平台和操作系统的版本相关的信息GetVersionEx()函数

函数格式:

BOOL GetVersionEx(LPOSVERSIONINFO lpVersionInfo);

参数:lpVersionInfo为指向OSVERSIONINFOEX结构,该结构用于装载操作系统的版本信息。

返回值:如果函数执行成功,返回值为非0,否则返回值为0。

2.3.7 关闭前台应用程序优先级的提升SetProcessPriorityBoost()函数

函数格式:

BOOL SetProcessPriorityBoost(HANDLE hProcess, BOOL DisablePriorityBoost);

参数:hProcess为指定进程的句柄,DisablePriorityBoost为指定可否提升优先级的标志。

返回值:如果函数执行成功,返回值为非0,否则返回值为0。

2.3.8 设置进程可用的最大和最小工作集SetProcessWorkingSet()函数

函数格式:

BOOL SetProcessWorkingSet(HANDLE hProcess, SIZE_T dwMinimumWorkingSetSize,

SIZE_T dwMaximumWorkingSetSize);

参数:hProcess为指定进程的句柄,dwMinimumWorkingSetSize为进程最小工作区域的字节数,dwMaximumWorkingSetSize为进程最大工作区域的字节数。

返回值:如果函数执行成功,返回值为非0,否则返回值为0。

2.3.9 获取运行进程操作系统版本号的程序示例

例4:获得运行进程的操作系统版本号。

// 获取运行进程操作系统版本号的程序

#include

#include

using namespace std;

// 利用进程和操作系统的版本信息的简单示例

void main()

{

// 提取版本信息和报告

GetVersionEx(reinterpret_cast(&osvix));

printf("Running on OS: %d.%dn", rVersion, rVersion);

// 如果是Windows NT5(windows 2000)系统,那么将其提升为高优先权

if(formId == VER_PLATFORM_WIN32_NT && rVersion >= 5)

{

// 改变当前运行进程的优先级为高

SetPriorityClass(

GetCurrentProcess(), // 得到该进程的ID

HIGH_PRIORITY_CLASS);// 改变为high

printf("Process ID: %d, requires OS: %d%dn", dwIdThis, wMajorReq, wMinorReq);

// 设置版本信息的数据结构,以便保存操作系统的版本信息

OSVERSIONINFOEX osvix;

ZeroMemory(&osvix, sizeof(osvix));

rsionInfoSize = sizeof(osvix);

DWORD dwIdThis = GetCurrentProcessId();// 获得进程的ID号

DWORD dwVerReq = GetProcessVersion(dwIdThis);// 获得进程和报告使用的操作系统版本

WORD wMajorReq = (WORD)(dwVerReq > 16);// 获得操作系统的主版本号

WORD wMinorReq = (WORD)(dwVerReq & 0xffff);// 获得操作系统的次版本号

// 报告给用户

printf("Task Manager should now indicate this");

printf("process is high priority.n");

}

}

该程序展示了如何获得当前进程标识符PID和进程运行的操作系统的版本信息。为了运行这一程序,系统处理了所有的版本不兼容问题。接着,程序演示了如何使用GetVersionEx()函数来提取OSVERSIONINFOEX结构。该结构中包括了操作系统的版本信息,Window 2000/XP在此数据块内是以Windows NT 5.1表示的。

程序的最后一段利用了操作系统的版本信息,以确认运行的是Windows 2000还是Windows XP。代码接着将当前进程的优先级提升到高优先级。在Windows2000/XP中增加了ABOVE_NORMAL_PRIORITY_CLASS级别,它介于NORMAL_PRIORITY_CLASS和HIGH_PRIORITY_CLASS之间。当右击该进程名以改变其优先级时,任务管理器”应用程序应该立即反映这个变化。

2.4 进程终止和进程同步等待

所有进程都是以调用ExitProcess()或TerminateProcess()函数结束的。前者是在进程完成了它的所有的关闭“职责”之后以正常的终止方法来调用的,而后者是外部进程发生故障后终止进程时调用的,由于关闭时的途径不太正常,有可能引起错误的行为。

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

进程同步是指一个进程或线程可以通过调用系统的相应函数等待一个或多个内核对象变为有信号状态,来实现进程或线程与这些内核对象的同步。能够作为同步对象的有进程、线程、文件、事件、互斥体、信号量等。这里主要介绍等待一个或多个内核对象WaitForsingleObject()或WaitForMultipleObjects()函数。

2.4.1 进程终止

1)进程正常终止ExitProcess()函数

函数格式:

VOID ExitProcess(UINT uExitCode);

参数:uExitCode为终止进程的退出码。

返回值:无。

2)中止指定句柄的进程TerminateProcess()函数

函数格式:

BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode);

参数:hProcess为指定要中断的那个进程的句柄,uExitCode为进程的一个退出码。

返回值:函数成功调用,返回值为非0,否则返回值为0。

2.4.2 获取指定句柄的已终止进程的退出码 GetExitCodeProcess()函数

函数格式:

BOOL GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode);

参数:hProcess为指定终止的那个进程的句柄;uExitCode为用于获得进程退出码的一个整数变量。

返回值:函数成功调用,返回值为非0,否则返回值为0。

2.4.3 等待一个同步对象WaitForsingleObject()函数

函数格式:

DWORD WaitForsingleObject(HANDLE hHandle, DWORD dwMilliseconds);

参数:

(1)hHandle为被等待对象的句柄

(2)dwMilliseconds为指定以毫秒为单位的等待时间。

2.4.4 等待多个同步对象WaitForMultipleObjects()函数

函数格式:

DWORD WaitForMultipleObjects(DWORD cObjects,

LPHANDLE lphandles, BOOL bwaitALL, DWORD dwtimeout);

参数:cobjects是被等待的对象个数;lphandles是被等待对象的句柄数组指针;bwaitALL为TRUE时,则等待全部同步对象变为有信号状态,否则,有一个变为有信号状态就结束等待。立即返回;wtimeout为等待的时间,含义同WaitForsingleObject()。

返回值:如果函数调用成功,返回值表明引起函数返回的事件。

2.4.5 终止进程的示例程序

例5:终止进程

// 终止进程应用程序

#include

#include

#include

static LPCTSTR g_szMutexName = (LPCTSTR)"e";

// 创建当前进程的克隆进程的简单方法

HANDLE StartClone()

{

// 利用同样的可执行文件名和命令行创建进程,并指明它是一个子进程

BOOL bCreateOK = CreateProcess(

szFilename,

szCmdLine,

NULL,

STARTUPINFO si; // 子进程的启动信息结构

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

= sizeof(si);

PROCESS_INFORMATION pi; // 说明一个结构,用来返回子进程信息

// 格式化子进程的命令行,指明它是一个EXE文件和子进程

TCHAR szCmdLine[MAX_PATH];

sprintf((char*)szCmdLine, ""%s"child", szFilename);

TCHAR szFilename[MAX_PATH];

// 获得当前可执行文件的文件名

GetModuleFileName(NULL, szFilename, MAX_PATH);

}

NULL,

FALSE,

CREATE_NEW_CONSOLE,

NULL,

NULL,

&si,

&pi);

// 关闭子进程及它的线程

if(bCreateOK)

{

}

else

return INVALID_HANDLE_VALUE;

CloseHandle(ss);

CloseHandle(d);

return ss;

void Parent()

{

}

void Child()

{

}

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

{

// 决定其行为是父进程还是子进程

if(argc > 1 && strcmp(argv[1], "child") == 0)

{

Child();

printf("Child is quiting.n");

Sleep(5000);

printf("Creating the child process and waited child process to quit.n");

HANDLE hchild = StartClone();

if(hchild != INVALID_HANDLE_VALUE)

{

}

else

printf("Create child process failed.n");

// 等待子进程终止

WaitForSingleObject(hchild, INFINITE);

printf("The child process had quited.n");

}

}

else

{

}

return 0;

Parent();

该程序说明一个进程从“生”到”死”的整个生命期。第一次执行时,它创建一个子进程,其如同“父进程”。然后父进程调用WaitForSingleObject()等待子进程终止。子进程终止后,父进程也终止,该程序运行结束。

子进程被创建后,调用sleep()模拟处理事务,之后终止自己,并唤醒父进程。

ExitProcess() API是终止进程的比较好的方法,但使用时要小心。当调用该函数时,进程中的所有线程都被立刻通知停止。在设计应用程序时,必须让主线程在正常的C++运行期关闭(这是由编译器提供的缺省行为),之后来调用这一函数,通常情况下,应用程序的框架负责调用ExitProcess()函数。对于C++运行库来说,这一调用发生在应用程序的main()函数返回之后(如果采用C运行库,则调用WinMain()函数)。当它转向有信号状态时,通常创建一个其所有活动线程都可等待和停止的终止事件。