作者:小 琛
欢迎转载,请标明出处
(未完结)
进程与线程的概念
应用程序包含一个或多个进程。进程具有虚拟地址空间、可执行代码、系统对象的打开句柄、安全上下文、唯一进程标识符、环境变量、优先级类、最小和最大工作集大小,以及至少一个执行线程。
线程 是操作系统向其分配处理器时间的基本单元。线程 是进程中可计划执行的实体。 进程的所有线程共享其虚拟地址空间和系统资源。 此外,每个线程都维护异常处理程序、计划优先级、线程本地存储、唯一线程标识符以及系统用于保存线程上下文的一组结构,直到计划线程上下文。
Windows 支持抢先式多任务处理,这可产生同时执行多个进程中的多个线程的效果。
个人理解,二者最大区别:在多进程和多线程中,每个进程拥有独立的虚拟地址空间,而每个线程共享一个虚拟地址空间。也带来了二者的诸多差别,例如进程间设计通信问题,线程间设计资源安全问题。
关于进程
创建进程
创建进程:CreateProcess
BOOL CreateProcess(
[in, optional] LPCSTR lpApplicationName,
[in, out, optional] LPSTR lpCommandLine,
[in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes,
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] BOOL bInheritHandles,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCSTR lpCurrentDirectory,
[in] LPSTARTUPINFOA lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
参数
-
[in, optional] lpApplicationName
要执行的模块的名称。该模块可以是基于 Windows 的应用程序。如果适当的子系统在本地计算机上可用,它可以是某种其他类型的模块(例如,MS-DOS 或 OS/2)。 -
[in, out, optional] lpCommandLine
要执行的命令行。
此字符串的最大长度为 32,767 个字符,包括 Unicode 终止空字符。如果lpApplicationName为NULL ,则lpCommandLine的模块名称部分仅限于MAX_PATH字符。
lpCommandLine参数可以为 NULL 。在这种情况下,函数使用lpApplicationName指向的字符串作为命令行。 -
[in, optional] lpProcessAttributes
指向 SECURITY_ATTRIBUTES结构的指针,该结构确定返回的新进程对象的句柄是否可以被子进程继承。如果lpProcessAttributes为NULL,则不能继承句柄。 -
[in, optional] lpThreadAttributes
指向 SECURITY_ATTRIBUTES结构的指针,该结构确定返回的新线程对象的句柄是否可以被子进程继承。如果lpThreadAttributes为 NULL,则不能继承句柄。 -
[in] bInheritHandles
如果此参数为 TRUE,则调用进程中的每个可继承句柄都由新进程继承。如果参数为 FALSE,则不继承句柄。请注意,继承的句柄与原始句柄具有相同的值和访问权限。 -
[in] dwCreationFlags
控制优先级和进程创建的标志。
如果 dwCreationFlags 参数的值为 0:
该进程继承了调用者的错误模式和父控制台的错误模式。
假定新进程的环境块包含 ANSI 字符(有关其他信息,请参阅lpEnvironment参数)。
基于 Windows 的 16 位应用程序在共享的虚拟 DOS 机器 (VDM) 中运行。 -
[in, optional] lpEnvironment
指向新进程的环境块的指针。如果此参数为NULL,则新进程使用调用进程的环境。 -
[in, optional] lpCurrentDirectory
进程当前目录的完整路径。该字符串还可以指定 UNC 路径。
如果此参数为NULL,则新进程将具有与调用进程相同的当前驱动器和目录。(此功能主要是为需要启动应用程序并指定其初始驱动器和工作目录的 shell 提供的。) -
[in] lpStartupInfo
指向 STARTUPINFO或STARTUPINFOEX结构的指针。
要设置扩展属性,请使用STARTUPINFOEX结构并在dwCreationFlags参数中指定 EXTENDED_STARTUPINFO_PRESENT。
STARTUPINFO或STARTUPINFOEX中的句柄 在不再需要时必须使用 CloseHandle关闭。 -
[out] lpProcessInformation
指向 PROCESS_INFORMATION结构的指针,该结构接收有关新进程的标识信息。
PROCESS_INFORMATION中的句柄 在不再需要时必须使用 CloseHandle关闭。
例子
PROCESS_INFORMATION pi; //进程信息
STARTUPINFO si; //进程启动信息
memset(&si, 0, sizeof(STARTUPINFO));
si.cb = sizeof(si);
si.wShowWindow = SW_SHOW;
si.dwFlags = STARTF_USESHOWWINDOW;
if (!CreateProcess(_T("F://腾讯会议//wemeetapp.exe"),
NULL, NULL, NULL, false, 0, NULL, NULL, &si, &pi)) {
printf("CreateProcess failed (%d).\n", GetLastError());
}
进程句柄和标识符
当 CreateProcess 函数创建新进程时,将返回新进程的句柄及其主线程。 这些句柄是使用完全访问权限创建的。
进程可以使用 GetCurrentProcessId 函数获取其自己的进程标识符 (也称为进程 ID 或 PID) 。 进程可以使用 Process32First 函数获取其父进程的进程标识符。
终止进程
当进程终止时,内核对象的打开句柄会自动关闭,但对象本身存在,直到关闭这些对象的所有打开句柄。 因此,如果另一个进程具有打开的句柄,则对象在使用它的进程终止后将保持有效。
GetExitCodeProcess函数返回进程的终止状态。 进程正在执行时,其终止状态仍为 活动。 当进程终止时,其终止状态从"仍处于活动状态更改到进程的退出代码。
进程将一直执行,直到发生下列事件之一:
- 进程的任何线程都调用 ExitProcess 函数。 请注意,C 运行时库的一些实现 (CRT) 进程的主线程返回时调用 ExitProcess。
- 进程的最后一个线程终止。
- 任何线程都使用进程的句柄调用 TerminateProcess 函数。
- 对于控制台进程,默认 控制台控件处理程序在控制台收到 CTRL+C 或 CTRL+BREAK 信号时调用 ExitProcess。
- 用户关闭系统或注销
父子进程的继承
有关环境变量
关于线程
创建线程
CreateThread();
HANDLE CreateThread(
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] SIZE_T dwStackSize,
[in] LPTHREAD_START_ROUTINE lpStartAddress,
[in, optional] __drv_aliasesMem LPVOID lpParameter,
[in] DWORD dwCreationFlags,
[out, optional] LPDWORD lpThreadId
);
-
[in, optional] lpThreadAttributes
指向SECURITY_ATTRIBUTES 结构的指针,该结构确定返回的句柄是否可以被子进程继承。如果 lpThreadAttributes为 NULL,则不能继承句柄。
该结构的lpSecurityDescriptor成员指定新线程的安全描述符。如果lpThreadAttributes为 NULL,则线程获取默认安全描述符。线程的默认安全描述符中的 ACL 来自创建者的主令牌。 -
[in] dwStackSize
堆栈的初始大小,以字节为单位。系统将此值四舍五入到最近的页面。如果此参数为零,则新线程使用可执行文件的默认大小。有关详细信息,请参阅 线程堆栈大小。 -
[in] lpStartAddress
指向要由线程执行的应用程序定义函数的指针。该指针表示线程的起始地址。有关线程函数的更多信息,请参阅 ThreadProc。 -
[in, optional] lpParameter
指向要传递给线程的变量的指针。 -
[in] dwCreationFlags
控制线程创建的标志。
#include <iostream>
#include <windows.h>
using namespace std;
HANDLE hMutex;
DWORD WINAPI thread(LPVOID lpParamter)
{
while (1) {
WaitForSingleObject(hMutex, INFINITE);
cout << "I am thread!" << endl;
Sleep(1000);
ReleaseMutex(hMutex);
std::cout << GetCurrentThreadId() << endl;
}
}
int main()
{
HANDLE hThread = CreateThread(NULL, 0, thread, NULL, 0, NULL);
hMutex = CreateMutex(NULL, FALSE, TEXT("test"));
CloseHandle(hThread);
while (1) {
WaitForSingleObject(hMutex, INFINITE);
cout << "I am main!" << endl;
Sleep(2000);
ReleaseMutex(hMutex);
}
return 0;
}
线程句柄和标识符
当 CreateThread 或 CreateRemoteThread 函数创建新线程时,将返回线程的句柄。 默认情况下,此句柄具有完全访问权限,并且(受安全访问检查限制)可用于接受线程句柄的任何函数。
线程可以使用 GetCurrentThreadId 函数获取其自己的线程标识符
挂起线程执行
线程的同步
终止线程
线程将一直执行,直到发生下列事件之一:
- 线程调用 ExitThread 函数。
- 进程的任何线程都调用 ExitProcess 函数。
- 线程函数返回 。
- 任何线程都调用具有线程句柄的 TerminateThread 函数。
- 任何线程都使用进程的句柄调用 TerminateProcess 函数。