正在执行的应用程序称为进程,进程不仅仅是指令和数据,它还有状态。状态是保存在处理器寄存器中的一组值,如当前执行指令的地址、保存在内存中的值,以及唯一定义进程在任一时刻任务的所有其他值。进程与应用程序的一个重要的区别在于,进程运行时,进程的状态会发生变化。
进程是应用程序的基本构建块。同时运行的多个应用程序实际上就是多个进程。要支持多个用户,通常是利用具有不同权限的多个进程实现的。除非一个进程创建时显式与另一进程共享状态,否则其所有状态应是该进程私有的,即对其他进程不可见。
进程的优点在于每个进程是孤立的,一个进程死掉对其他正在运行的进程没有任何影响。多进程的缺点是每个进程需要自己的TLB(Translation Look-aside Buffer,转换旁视缓冲器)条目,从而增加了TLB和缓存的未命中率。使用多进程的另一个缺点是进程之间共享数据需要显式控制,而这种操作可能开销很大。
谷歌的Chrome浏览器是多进程的,浏览器可以使用多个标签页显示不同的网页。每个标签页是一个单独的进程,这样一个标签页失败不会导致整个浏览器终止。浏览器曾经是多线程的,如果一个线程执行不良代码,整个浏览器就会崩溃。
在Windows下,CreateProcess函数用于创建一个新进程,该进程独立于创建进程运行,其声明如下:
WINBASEAPI
BOOL
WINAPI
CreateProcessA(
_In_opt_ LPCSTR lpApplicationName,
_Inout_opt_ LPSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOA lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
);
该函数需要几个参数,其中3个重要参数为要运行的进程的名称(及所有参数),指向STARTUPINFO结构的指针,以及指向PROCESS_INFORMATION结构的指针。下表列出了CreateProcess函数的所有参数:
第一个参数是应用程序的名称,第二个参数是命令行。这两个参数共同起作用,如果其中一个为空,另一个就用作整个命令行。如果两者都非空,将执行第一个参数命名的应用程序,但传递第二个参数给出的命令行。这就是说,命令行中的第一个参数应该与应用程序名重复。当调用CreateProcess函数的Unicode版本CreateProcessW时,可以修改命令行,因此命令行应该存储于变量中,而非字符串常量中。如果应用程序名为空,那么所执行的应用程序应该是命令行中以空格符分隔的第一串文本。如果应用程序的路径中包含空格,则整个路径和程序名都应包含在引号中。
第三个和第四个参数为可选参数,是指向SECURITY_ATTRIBUTES结构的指针。第一个结构给出被创建进程的属性,第二个为被创建进程的第一个线程的属性。这些属性主要确定被创建进程的子进程是否继承被创建进程所有的句柄。指针为空值则向被创建进程提供默认属性。
STARTUPINFO和PROCESS_INFORMATION结构应在调用前填充零。STARTUPINFO结构的cb成员应设置为此结构的大小。CreateProcess函数调用将复制STARTUPINFO结构中的信息。最重要的信息是新进程的句柄,将记录在PROCESS_INFORMATION结构的hProcess成员中。
要将参数传递给子进程,第一个命令行参数必须重复应用程序名,整个命令行会被传递给子进程。
注:以上内容主要整理自:《多核应用编程实战》
以下是测试代码段:
int process_programming_windows()
{
char filename[MAX_PATH];
auto size = GetModuleFileName(nullptr, filename, MAX_PATH);
if (size == 0) {
fprintf(stderr, "fail to GetModuleFileName\n");
return -1;
}
// reference: https://docs.microsoft.com/en-us/windows/win32/procthread/creating-processes
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
char argument[512];
//sprintf(argument, "\"%s\" C:\\windows\\system32\\cmd.exe", filename);
sprintf(argument, "C:\\windows\\system32\\cmd.exe");
// Start the child process.
if (!CreateProcess(nullptr,
argument, // Command line
nullptr, // Process handle not inheritable
nullptr, // Thread handle not inheritable
false, // Set handle inheritance to FALSE
0, // No creation flags
nullptr, // Use parent's environment block
nullptr, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
) {
fprintf(stderr, "fail to CreateProcess: %d\n", GetLastError());
return -1;
}
fprintf(stdout, "##### success to create child process #####\n");
// Wait until child process exits.
WaitForSingleObject(pi.hProcess, INFINITE);
fprintf(stdout, "##### child process exit #####\n");
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}
执行结果如下图所示: