Windows学习笔记-15(进程、线程、模块)

一、进程的核心定义

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. 未处理异常导致崩溃

终止时的清理工作

  1. 所有线程被终止

  2. 所有打开的内核对象句柄被关闭

  3. 进程对象变为有信号状态(用于WaitForSingleObject)

  4. 进程退出码被设置

  5. 进程对象本身:当所有句柄关闭后,内核才删除对象

五、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

一个运行中的进程通常都需要将好几个可执行文件加载到他的虚拟内存空间,那么每个在此进程中被加载的可执行文件都称之为模块.

相关推荐
时代的凡人8 小时前
0208晨间笔记
笔记
今天只学一颗糖9 小时前
1、《深入理解计算机系统》--计算机系统介绍
linux·笔记·学习·系统架构
testpassportcn9 小时前
AWS DOP-C02 認證完整解析|AWS DevOps Engineer Professional 考試
网络·学习·改行学it
游乐码12 小时前
c#变长关键字和参数默认值
学习·c#
x***r15113 小时前
SuperScan4单文件扫描安装步骤详解(附端口扫描与主机存活检测教程)
windows
饭碗、碗碗香13 小时前
【Python学习笔记】:Python的hashlib算法简明指南:选型、场景与示例
笔记·python·学习
Wils0nEdwards13 小时前
初中化学1
笔记
不爱学习的老登14 小时前
Windows客户端与Linux服务器配置ssh无密码登录
linux·服务器·windows
魔力军14 小时前
Rust学习Day4: 所有权、引用和切片介绍
开发语言·学习·rust
wubba lubba dub dub75014 小时前
第三十六周 学习周报
学习