一、进程的核心定义
1.1 基本概念
进程 是操作系统进行资源分配和调度的基本单位 ,是一个正在执行的程序实例。它不仅包含程序代码,还包括执行该程序所需的所有系统资源状态。
1.2 经典定义对比
| 视角 | 定义 | 关键点 |
|---|---|---|
| 程序角度 | 程序的一次执行过程 | 动态 vs 静态 |
| 资源角度 | 拥有独立资源的基本单位 | 隔离性、保护性 |
| 系统角度 | 操作系统调度的实体 | 并发性、状态性 |
一个程序可对应多个进程,一个进程只能执行一个程序。
具体区别:
| 特征 | 程序 | 进程 |
|---|---|---|
| 存在形式 | 静态文件(.exe, .dll) | 动态执行实体 |
| 生命周期 | 永久存储 | 创建→运行→终止 |
| 资源占用 | 仅磁盘空间 | CPU、内存、I/O等 |
| 并发性 | 无 | 可并发执行 |
| 状态 | 无状态概念 | 就绪、运行、阻塞等 |
二、进程与线程的区别
| 对比维度 | 进程 | 线程 |
|---|---|---|
| 资源拥有 | 资源的所有者 | 资源的使用者 |
| 地址空间 | 独立虚拟地址空间 | 共享进程地址空间 |
| 通信成本 | 高(需要IPC) | 低(直接共享内存) |
| 创建开销 | 大(需分配资源) | 小(共享资源) |
| 健壮性 | 一个进程崩溃不影响其他进程 | 一个线程崩溃可能导致整个进程崩溃 |
| 安全性 | 高(有内存保护) | 较低(可互相访问) |
三、Windows进程的架构
3.1 进程作为内核对象
在Windows中,进程本身是一个内核对象 (类型为Process):
cpp
// 进程内核对象的结构(简化概念)
typedef struct _EPROCESS { // Executive Process Block
KPROCESS Pcb; // 内核进程控制块
HANDLE UniqueProcessId; // 进程ID (PID)
HANDLE InheritedFromUniqueProcessId; // 父进程ID
LIST_ENTRY ActiveProcessLinks; // 活动进程链表
PVOID SectionBaseAddress; // 映像基地址
// ... 其他字段(超过60个)
} EPROCESS;
3.2 进程的组成要素
一个完整的Windows进程包含:
3.2.1 虚拟地址空间
3.2.2 核心数据结构
cpp
// 进程的关键组件
struct PROCESS_COMPONENTS {
EPROCESS* eprocess; // 内核进程块
PEB* peb; // 进程环境块(用户态)
HANDLE_TABLE* handle_table; // 句柄表
VM_SPACE* address_space; // 虚拟内存管理器
TOKEN* access_token; // 安全访问令牌
JOB* job; // 可能的作业对象
LIST thread_list; // 线程列表
LIST loaded_modules; // 已加载模块(DLL)
};
3.2.3 进程环境块(PEB)
PEB位于用户地址空间,包含用户态信息:
cpp
typedef struct _PEB {
BOOLEAN InheritedAddressSpace;
BOOLEAN ReadImageFileExecOptions;
BOOLEAN BeingDebugged; // 正在被调试?
HANDLE ProcessHeap; // 进程堆句柄
PPEB_LDR_DATA Ldr; // 模块加载信息
PRTL_USER_PROCESS_PARAMETERS ProcessParameters; // 进程参数
PVOID SubSystemData;
HANDLE ProcessHeap;
// ... 更多字段
} PEB;
四、进程的生命周期
4.1 进程状态转换

4.2 Windows进程的完整生命周期
阶段1:创建进程
cpp
// Windows创建进程的步骤:
1. 验证可执行文件(PE格式检查)
2. 创建进程内核对象(EPROCESS)
3. 创建初始线程内核对象(ETHREAD)
4. 分配进程地址空间
5. 加载可执行映像和必要的DLL
6. 创建PEB和TEB(线程环境块)
7. 开始执行初始线程
创建进程的API调用链:
cpp
CreateProcess() [用户态API]
↓
CreateProcessInternal()
↓
NtCreateUserProcess() [系统调用]
↓
PspCreateProcess() [内核函数]
↓
创建EPROCESS、分配资源...
阶段2:进程执行
-
进程调度 :实际上调度的是线程,进程是容器
-
资源管理:CPU时间片、内存、I/O等
-
异常处理:结构化异常处理(SEH)、向量化异常处理(VEH)
阶段3:进程终止
cpp
// 终止方式:
1. 主线程正常退出(Main函数返回)
2. 调用ExitProcess() 或 TerminateProcess()
3. 所有线程终止(如果设置)
4. 未处理异常导致崩溃
终止时的清理工作:
-
所有线程被终止
-
所有打开的内核对象句柄被关闭
-
进程对象变为有信号状态(用于WaitForSingleObject)
-
进程退出码被设置
-
进程对象本身:当所有句柄关闭后,内核才删除对象
五、Windows进程的特殊特性
5.1 进程优先级与类
cpp
// Windows进程优先级类
typedef enum _PROCESS_PRIORITY_CLASS {
IDLE_PRIORITY_CLASS = 0x00000040, // 空闲(4)
BELOW_NORMAL_PRIORITY_CLASS = 0x00004000, // 低于正常(6)
NORMAL_PRIORITY_CLASS = 0x00000020, // 正常(8)
ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000, // 高于正常(10)
HIGH_PRIORITY_CLASS = 0x00000080, // 高(13)
REALTIME_PRIORITY_CLASS = 0x00000100 // 实时(24)
} PROCESS_PRIORITY_CLASS;
// 设置进程优先级
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
5.2 作业对象(Job Objects)
Windows允许将多个进程分组到作业中统一管理:
cpp
// 创建和使用作业对象
HANDLE hJob = CreateJobObject(NULL, L"MyJob");
// 限制设置
JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
info.BasicLimitInformation.LimitFlags =
JOB_OBJECT_LIMIT_PROCESS_TIME | // 限制CPU时间
JOB_OBJECT_LIMIT_WORKINGSET; // 限制工作集大小
SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &info, sizeof(info));
// 将进程分配给作业
AssignProcessToJobObject(hJob, hProcess);
5.3 进程的完整性级别(Integrity Levels)
Windows Vista+引入的安全机制:
cpp
完整性级别(从低到高):
1. 不可信 (Untrusted) - 匿名进程
2. 低 (Low) - 保护模式IE
3. 中 (Medium) - 普通用户进程
4. 高 (High) - 管理员权限进程
5. 系统 (System) - 系统服务
原则:低完整性进程不能写高完整性资源
5.4 虚拟化机制(UAC虚拟化)
cpp
// 对于不支持UAC的旧程序,Windows提供虚拟化
// 对系统位置的写入被重定向到用户虚拟存储
C:\Program Files\OldApp → C:\Users\User\AppData\Local\VirtualStore\Program Files\OldApp
六、进程间通信(IPC)
6.1 Windows IPC机制概览
| 机制 | 特点 | 适用场景 |
|---|---|---|
| 文件映射 | 高性能,支持大数据 | 进程间共享数据 |
| 命名管道 | 支持双向通信 | 客户端/服务器通信 |
| 邮件槽 | 一对多广播 | 简单消息广播 |
| 剪贴板 | 系统全局共享 | 用户界面数据交换 |
| COM/OLE | 基于接口的通信 | 组件间通信 |
| RPC | 跨机器通信 | 分布式系统 |
| 窗口消息 | 仅Windows GUI程序 | 简单消息通知 |
| 套接字 | 跨网络和本地 | 网络通信 |
6.2 文件映射示例
cpp
// 进程A:创建共享内存
HANDLE hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // 使用页面文件
NULL, // 默认安全
PAGE_READWRITE, // 读写权限
0, 4096, // 高32位, 低32位大小
L"Local\\MySharedMem"); // 名称
LPVOID pBuf = MapViewOfFile(
hMapFile, // 映射对象句柄
FILE_MAP_ALL_ACCESS, // 访问权限
0, 0, 4096); // 偏移和大小
// 写入数据
strcpy((char*)pBuf, "Hello from Process A");
// 进程B:打开同一共享内存
HANDLE hMapFileB = OpenFileMapping(
FILE_MAP_ALL_ACCESS, // 访问权限
FALSE, // 不继承
L"Local\\MySharedMem"); // 同一名称
LPVOID pBufB = MapViewOfFile(hMapFileB, ...);
printf("收到: %s\n", (char*)pBufB); // 输出: Hello from Process A
七、进程相关函数
1 进程创建与终止
CreateProcess - 创建新进程
cpp
BOOL CreateProcess(
LPCTSTR lpApplicationName, // 可执行文件路径
LPTSTR lpCommandLine, // 命令行参数
LPSECURITY_ATTRIBUTES lpProcessAttributes, // 进程安全属性
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全属性
BOOL bInheritHandles, // 是否继承句柄
DWORD dwCreationFlags, // 创建标志
LPVOID lpEnvironment, // 环境变量
LPCTSTR lpCurrentDirectory, // 当前目录
LPSTARTUPINFO lpStartupInfo, // 启动信息
LPPROCESS_INFORMATION lpProcessInformation // 进程信息
);
关键参数详解:
cpp
// dwCreationFlags 常用值:
CREATE_NEW_CONSOLE // 创建新控制台
CREATE_SUSPENDED // 挂起主线程
DETACHED_PROCESS // 无控制台进程
CREATE_NO_WINDOW // 不创建窗口(控制台程序)
CREATE_UNICODE_ENVIRONMENT // 使用Unicode环境
HIGH_PRIORITY_CLASS // 高优先级
IDLE_PRIORITY_CLASS // 空闲优先级
// STARTUPINFO 结构重要成员:
typedef struct _STARTUPINFO {
DWORD cb; // 结构大小
LPTSTR lpReserved; // 保留
LPTSTR lpDesktop; // 桌面名称
LPTSTR lpTitle; // 控制台窗口标题
DWORD dwX, dwY; // 窗口位置
DWORD dwXSize, dwYSize;// 窗口大小
DWORD dwXCountChars; // 控制台缓冲区宽度
DWORD dwYCountChars; // 控制台缓冲区高度
DWORD dwFillAttribute; // 控制台文本颜色
DWORD dwFlags; // 标志位
WORD wShowWindow; // 窗口显示状态
// ... 其他字段
} STARTUPINFO, *LPSTARTUPINFO;
// PROCESS_INFORMATION 结构:
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess; // 进程句柄
HANDLE hThread; // 主线程句柄
DWORD dwProcessId; // 进程ID
DWORD dwThreadId; // 线程ID
} PROCESS_INFORMATION;
使用示例:
cpp
// 创建进程的基本示例
void CreateProcessExample() {
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
// 设置启动信息
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
// 创建进程
BOOL success = CreateProcess(
L"C:\\Windows\\System32\\notepad.exe", // 应用程序名
L"notepad test.txt", // 命令行(包含参数)
NULL, // 进程安全属性
NULL, // 线程安全属性
FALSE, // 不继承句柄
CREATE_NEW_CONSOLE, // 创建新控制台
NULL, // 使用当前环境
NULL, // 使用当前目录
&si, // 启动信息
&pi // 进程信息
);
if (success) {
printf("进程创建成功!PID: %d\n", pi.dwProcessId);
// 等待进程结束(可选)
WaitForSingleObject(pi.hProcess, INFINITE);
// 获取退出码
DWORD exitCode;
GetExitCodeProcess(pi.hProcess, &exitCode);
printf("进程退出码: %d\n", exitCode);
// 关闭句柄(重要!)
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
} else {
printf("创建进程失败!错误码: %d\n", GetLastError());
}
}
ExitProcess - 终止当前进程
cpp
VOID ExitProcess(UINT uExitCode); // 退出码
特点:
-
立即终止整个进程(所有线程)
-
调用进程中的所有DLL都会收到
DLL_PROCESS_DETACH通知 -
不会返回调用者
示例:
cpp
void CleanExit() {
// 清理资源
// ...
// 退出进程,退出码为0表示成功
ExitProcess(0);
}
TerminateProcess - 强制终止进程
cpp
BOOL TerminateProcess(
HANDLE hProcess, // 进程句柄
UINT uExitCode // 退出码
);
-
强制终止,不执行清理操作
-
目标进程没有机会执行清理代码
-
DLL不会收到
DLL_PROCESS_DETACH通知 -
需要
PROCESS_TERMINATE权限
示例:
cpp
void KillProcess(DWORD pid) {
// 打开进程(需要适当权限)
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if (hProcess) {
TerminateProcess(hProcess, 1); // 强制终止
CloseHandle(hProcess);
}
}
2 进程信息查询
GetCurrentProcessId - 获取当前进程ID
cpp
DWORD GetCurrentProcessId();
GetCurrentProcess - 获取当前进程伪句柄
cpp
HANDLE GetCurrentProcess(); // 返回值为-1的伪句柄
伪句柄特点:
-
值固定为
-1((HANDLE)-1) -
只能在当前进程使用
-
不需要关闭
-
调用
DuplicateHandle可转换为真实句柄
OpenProcess - 打开现有进程
cpp
HANDLE OpenProcess(
DWORD dwDesiredAccess, // 访问权限
BOOL bInheritHandle, // 是否可继承
DWORD dwProcessId // 进程ID
);
常用访问权限:
cpp
PROCESS_ALL_ACCESS // 所有权限
PROCESS_QUERY_INFORMATION // 查询信息
PROCESS_VM_READ // 读取内存
PROCESS_VM_WRITE // 写入内存
PROCESS_TERMINATE // 终止进程
PROCESS_DUP_HANDLE // 复制句柄
GetExitCodeProcess - 获取进程退出码
cpp
BOOL GetExitCodeProcess(
HANDLE hProcess, // 进程句柄
LPDWORD lpExitCode // 接收退出码
);
退出码说明:
-
STILL_ACTIVE(259):进程仍在运行 -
其他值:进程已终止,值为退出码
GetProcessTimes - 获取进程时间信息
cpp
BOOL GetProcessTimes(
HANDLE hProcess, // 进程句柄
LPFILETIME lpCreationTime, // 创建时间
LPFILETIME lpExitTime, // 退出时间
LPFILETIME lpKernelTime, // 内核模式时间
LPFILETIME lpUserTime // 用户模式时间
);
示例:
cpp
void PrintProcessTimes(HANDLE hProcess) {
FILETIME creationTime, exitTime, kernelTime, userTime;
SYSTEMTIME sysTime;
if (GetProcessTimes(hProcess, &creationTime, &exitTime,
&kernelTime, &userTime)) {
// 转换时间格式
FileTimeToSystemTime(&creationTime, &sysTime);
printf("进程创建时间: %04d-%02d-%02d %02d:%02d:%02d\n",
sysTime.wYear, sysTime.wMonth, sysTime.wDay,
sysTime.wHour, sysTime.wMinute, sysTime.wSecond);
// 计算总CPU时间(100纳秒为单位)
ULARGE_INTEGER kernel, user;
kernel.LowPart = kernelTime.dwLowDateTime;
kernel.HighPart = kernelTime.dwHighDateTime;
user.LowPart = userTime.dwLowDateTime;
user.HighPart = userTime.dwHighDateTime;
ULONGLONG totalCPUTime = kernel.QuadPart + user.QuadPart;
printf("总CPU时间: %.2f 秒\n", totalCPUTime / 1e7);
}
}
3 进程枚举与控制
CreateToolhelp32Snapshot - 创建系统快照
cpp
HANDLE CreateToolhelp32Snapshot(
DWORD dwFlags, // 快照类型
DWORD th32ProcessID // 进程ID(0表示当前进程)
);
常用快照类型:
cpp
TH32CS_SNAPPROCESS // 进程快照
TH32CS_SNAPTHREAD // 线程快照
TH32CS_SNAPMODULE // 模块快照
TH32CS_SNAPHEAPLIST // 堆列表快照
TH32CS_SNAPALL // 所有类型
Process32First/Process32Next - 遍历进程
cpp
BOOL Process32First(
HANDLE hSnapshot, // 快照句柄
LPPROCESSENTRY32 lppe // 进程条目
);
BOOL Process32Next(
HANDLE hSnapshot,
LPPROCESSENTRY32 lppe
);
PROCESSENTRY32结构:
cpp
typedef struct tagPROCESSENTRY32 {
DWORD dwSize; // 结构大小
DWORD cntUsage; // 引用计数
DWORD th32ProcessID; // 进程ID
ULONG_PTR th32DefaultHeapID; // 默认堆ID
DWORD th32ModuleID; // 模块ID
DWORD cntThreads; // 线程数
DWORD th32ParentProcessID; // 父进程ID
LONG pcPriClassBase; // 基本优先级
DWORD dwFlags; // 标志
CHAR szExeFile[MAX_PATH]; // 可执行文件名
} PROCESSENTRY32;
4 进程优先级与内存
SetPriorityClass - 设置进程优先级
cpp
BOOL SetPriorityClass(
HANDLE hProcess, // 进程句柄
DWORD dwPriorityClass // 优先级类
);
GetPriorityClass - 获取进程优先级
GetProcessMemoryInfo - 获取进程内存信息
八、线程相关函数
1 线程创建与终止
CreateThread - 创建线程
cpp
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全属性
SIZE_T dwStackSize, // 初始堆栈大小(0=默认)
LPTHREAD_START_ROUTINE lpStartAddress, // 线程函数地址
LPVOID lpParameter, // 线程参数
DWORD dwCreationFlags, // 创建标志
LPDWORD lpThreadId // 线程ID
);
注意 :C运行时库(CRT)用户应使用_beginthreadex,因为CreateThread可能导致内存泄漏。
_beginthreadex - CRT安全线程创建
cpp
uintptr_t _beginthreadex(
void* security, // 安全属性
unsigned stack_size, // 堆栈大小
unsigned (__stdcall* start_address)(void*), // 线程函数
void* arglist, // 参数
unsigned initflag, // 初始标志
unsigned* thrdaddr // 线程ID
);
ExitThread - 终止当前线程
-
立即终止线程,不执行栈展开
-
不会调用C++对象的析构函数
-
应让线程函数自然返回
TerminateThread - 强制终止线程
-
极度危险!避免使用
-
不释放线程资源(内存泄漏)
-
可能导致死锁(未释放锁)
-
仅在极端情况下使用
2 线程信息与控制
GetCurrentThreadId - 获取当前线程ID
GetCurrentThread - 获取当前线程伪句柄
OpenThread - 打开现有线程
SuspendThread/ResumeThread - 挂起/恢复线程
-
挂起计数机制:可多次挂起,需相同次数恢复
-
挂起内核模式线程可能导致死锁
-
谨慎使用,主要用于调试
Sleep - 线程休眠
SwitchToThread - 切换到其他就绪线程
3 线程枚举与同步
Thread32First/Thread32Next - 遍历线程
4 等待函数
WaitForSingleObject - 等待单个对象
WaitForMultipleObjects - 等待多个对象
九、模块
包含着代码和数据的可以运行的文件我们称之为可执行文件.
在windows平台常见的可执行文件:
Exe、dll
一个运行中的进程通常都需要将好几个可执行文件加载到他的虚拟内存空间,那么每个在此进程中被加载的可执行文件都称之为模块.