进程
本质
- 进程是资源分配的基本单位,是一个执行环境的容器
- 在
Windows内核中,每个进程由一个EPROCESS(Executive Process)结构体表示
EPROCESS关键结构
|
1 2 3 4 5 6 7 8 9 10 |
EPROCESS核心字段: - UniqueProcessId: 进程ID(PID) - ImageFileName: 可执行文件名 - ActiveProcessLinks: 链接到全局进程链表 - ThreadListHead: 该进程的线程链表 - ObjectTable: 句柄表指针 - VadRoot: 虚拟地址描述符树根(VAD tree) - Peb: 指向进程环境块的指针 - Token: 安全令牌 - SectionObject: 映射的可执行文件对象 |
进程地址空间
- 概述
- 每个进程拥有独立的虚拟地址空间(
32位系统4GB,64位系统理论上256TB)
- 每个进程拥有独立的虚拟地址空间(
- 用户空间布局(
64位)0x00000000'00000000 - 0x00007FFF'FFFFFFFF:用户空间(低128TB)- 可执行文件映像、
DLL模块、堆、栈、TEB等都在这里
- 内核空间布局:
0xFFFF8000'00000000 - 0xFFFFFFFF'FFFFFFFF:内核空间(高128TB)- 所有进程共享同一内核空间映射
虚拟地址描述符(VAD)
VAD树是一个AVL平衡树,记录进程的每个内存区域:
|
1 2 3 4 5 |
VAD节点包含: - 起始地址和结束地址 - 保护属性(读/写/执行权限) - 提交状态(committed/reserved) - 映射类型(私有、映射文件、共享内存) |
windbg
|
1 2 3 |
!vad // 显示VAD树 !address // 显示地址空间摘要 !vprot <address> // 查看地址的保护属性 |
进程环境块(PEB)
PEB位于用户空间,包含进程的运行时信息:
|
1 2 3 4 5 6 |
PEB关键字段: - ImageBaseAddress: 主模块基址 - Ldr: 加载器数据(LDR_DATA_TABLE_ENTRY链表) - ProcessParameters: 命令行、环境变量等 - BeingDebugged: 调试标志 - NumberOfHeaps, ProcessHeaps: 堆信息 |
windbg
|
1 2 |
!peb // 显示完整PEB dt _PEB @$peb // 显示PEB结构 |
句柄表(Handle Table)
- 进程通过句柄访问内核对象(文件、事件、互斥体等):
|
1 2 3 4 |
句柄表结构: - 三级表结构(小表→中表→大表) - 每个句柄条目包含:对象指针、访问权限、标志 - 句柄值是表索引的编码(低2位是标志位) |
windbg
|
1 2 |
!handle // 显示所有句柄 !handle <handle_value> // 显示特定句柄详情 |
进程创建流程
|
1 2 3 4 5 6 7 8 9 10 11 |
CreateProcess API调用流程: 1. 用户态:kernel32!CreateProcessW 2. 过渡到:ntdll!NtCreateUserProcess 3. 内核态:系统调用进入内核 4. nt!NtCreateUserProcess - 创建EPROCESS对象 - 创建进程地址空间 - 映射ntdll.dll - 创建初始线程(TEB、栈) - 设置PEB 5. 返回用户态,线程开始执行 |
线程
本质
- 线程是
CPU调度的基本单位,是进程内的执行流 - 每个线程由
ETHREAD(Executive Thread)结构表示
ETHREAD关键结构
|
1 2 3 4 5 6 7 8 |
ETHREAD核心字段: - Cid: 客户端ID(包含PID和TID) - ThreadListEntry: 链接到进程的线程链表 - Tcb: KTHREAD(内核线程块) - StartAddress: 线程起始地址 - Win32StartAddress: Win32层起始地址 - ImpersonationInfo: 模拟信息 - ActiveTimerListHead: 定时器列表 |
KTHREAD关键结构
KTHREAD是ETHREAD的一部分,包含调度相关信息:
|
1 2 3 4 5 6 7 8 9 |
KTHREAD核心字段: - State: 线程状态(Running、Ready、Waiting等) - Priority: 当前优先级 - BasePriority: 基础优先级 - Affinity: CPU亲和性掩码 - KernelStack: 内核栈指针 - TrapFrame: 陷阱帧(保存用户态上下文) - WaitBlockList: 等待块链表 - Queue: 所属队列 |
线程环境块(TEB)
- 每个线程有自己的
TEB,位于用户空间:
|
1 2 3 4 5 6 7 8 9 |
TEB关键字段: - NtTib: NT线程信息块 - ExceptionList: SEH链表头 - StackBase: 栈基址 - StackLimit: 栈限制 - Self: 指向TEB自身(fs:[0x18]/gs:[0x30]) - ClientId: PID和TID - LastErrorValue: GetLastError()的值 - ThreadLocalStoragePointer: TLS指针 |
- 访问
TEB
|
1 2 3 4 5 6 |
x86: fs:[0] 指向TEB x64: gs:[0] 指向TEB WinDbg命令: !teb // 显示当前线程TEB dt _TEB @$teb // 显示TEB结构 |
线程栈
- 概述
- 每个线程有两个栈
- 用户态栈
- 默认大小
1MB(可通过链接器设置) - 从高地址向低地址增长
- 栈顶由
TEB记录 - 栈溢出保护页(
Guard Page)
- 默认大小
- 内核态栈
- 大小
12KB(x86)或24KB(x64) - 系统调用或中断时使用
- 保存在
KTHREAD中
- 大小
windbg
|
1 2 3 |
k // 显示调用栈 kv // 详细模式(显示参数) !tls // 显示TLS(线程本地存储) |
线程状态转换
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
线程状态机: Initialized → Ready → Running → Terminated ↓ ↑ Waiting ←┘ Transition Standby 状态说明: - Initialized: 正在初始化 - Ready: 就绪,等待CPU - Standby: 已选中,即将运行 - Running: 正在运行 - Waiting: 等待某个对象 - Transition: 等待资源(如换入内存) - Terminated: 已终止 |
|
1 2 3 |
!thread // 显示当前线程 !thread -1 0 // 显示所有线程摘要 ~*k // 所有线程的调用栈 |
线程调度机制与原理
Windows调度器架构
Windows使用基于优先级的抢占式多任务调度
优先级系统
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
优先级范围(0-31): 0: Zero Page线程(内存清零) 1-15: 动态优先级(普通线程) 16-31: 实时优先级 优先级类(进程级): - IDLE_PRIORITY_CLASS: 4 - BELOW_NORMAL_PRIORITY_CLASS: 6 - NORMAL_PRIORITY_CLASS: 8(默认) - ABOVE_NORMAL_PRIORITY_CLASS: 10 - HIGH_PRIORITY_CLASS: 13 - REALTIME_PRIORITY_CLASS: 24 线程相对优先级: - THREAD_PRIORITY_IDLE: -15 - THREAD_PRIORITY_LOWEST: -2 - THREAD_PRIORITY_BELOW_NORMAL: -1 - THREAD_PRIORITY_NORMAL: 0 - THREAD_PRIORITY_ABOVE_NORMAL: +1 - THREAD_PRIORITY_HIGHEST: +2 - THREAD_PRIORITY_TIME_CRITICAL: +15 最终优先级 = 进程基础优先级 + 线程相对优先级 |
调度队列
- 每个逻辑处理器维护一组就绪队列
|
1 2 3 |
- 32个优先级队列(每个优先级一个队列) - 高优先级队列优先被调度 - 同优先级采用时间片轮转(Round Robin) |
调度算法
- 优先级提升
|
1 2 3 4 5 6 7 8 |
动态优先级线程在以下情况会获得临时优先级提升: - I/O完成:+1到+3 - GUI事件响应:+2 - 前台窗口线程:+2 - 等待结束:+1 - 饥饿避免:逐渐提升到15 提升后的优先级会逐渐衰减回基础优先级 |
- 时间片
|
1 2 3 4 5 |
时间片长度由系统配置决定: - 客户端系统:短时间片(10-30ms),更好的交互性 - 服务器系统:长时间片(120ms),更好的吞吐量 前台进程可能获得3倍时间片 |
CPU亲和性(Affinity)
|
1 2 3 |
- 进程亲和性:限制进程可使用的CPU集合 - 理想CPU(Ideal Processor):优先调度到上次运行的CPU - 软亲和性:尽量保持在同一CPU,提高缓存命中率 |
NUMA感知
|
1 2 3 4 |
在NUMA系统上: - 优先分配本地内存节点 - 线程倾向于在本节点的CPU上运行 - 减少跨节点内存访问延迟 |
多核调度优化
- 核心停放(
Core Parking)
|
1 2 3 4 |
低负载时: - 将线程集中到少数核心 - 关闭空闲核心以节能 - 动态调整活跃核心数量 |
SMT/超线程调度
|
1 2 3 |
- 识别逻辑处理器和物理核心 - 优先使用不同物理核心 - 避免两个高负载线程共享物理核心 |
调度器数据结构
KPRCB(Processor Control Block)
|
1 2 3 4 5 6 |
每个逻辑处理器一个KPRCB: - ReadySummary: 位图,标记哪些优先级队列非空 - DispatcherReadyListHead[32]: 32个就绪队列 - CurrentThread: 当前运行线程 - NextThread: 下一个要运行的线程 - IdleThread: 空闲线程 |
windbg
|
1 2 3 4 |
!ready // 显示就绪队列 !running // 显示运行中的线程 !pcr // 显示处理器控制区域 dt nt!_KPRCB // 显示KPRCB结构 |
线程上下文切换
上下文概念
- 线程上下文包括
CPU寄存器状态和必要的执行环境信息
完整上下文(CONTEXT结构)
|
1 2 3 4 5 6 7 8 |
x64 CONTEXT包含: - 通用寄存器:RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, R8-R15 - 指令指针:RIP - 标志寄存器:RFLAGS - 段寄存器:CS, DS, ES, FS, GS, SS - 浮点寄存器:XMM0-XMM15(SSE) - AVX寄存器:YMM0-YMM15(如果支持) - 调试寄存器:DR0-DR7 |
上下文完整切换过程
- 中断或系统调用触发
- 时间片耗尽(时钟中断)
- 线程主动放弃
CPU(Sleep、WaitForSingleObject) - 高优先级线程就绪(抢占)
- 保存当前线程上下文
- 用户态→内核态切换时:
CPU自动保存SS, RSP, RFLAGS, CS, RIP到内核栈
中断处理程序保存其他寄存器到陷阱帧(KTRAP_FRAME) - 调度器接管
保存内核态寄存器到KTHREAD
保存浮点状态(如果使用了)
更新线程统计信息(运行时间等)
- 用户态→内核态切换时:
- 选择下一个线程
- 扫描就绪队列,选择最高优先级就绪线程
- 更新
KPRCB的CurrentThread - 如果跨进程切换,还需要切换地址空间
- 加载新线程上下文
- 切换地址空间(如果需要):
加载新的CR3寄存器(页表基址)
刷新TLB(Translation Lookaside Buffer) - 恢复线程上下文:
从KTHREAD恢复内核态寄存器
切换到新线程的内核栈
恢复浮点状态 - 返回用户态:
从陷阱帧恢复用户态寄存器
IRET指令返回用户态(恢复CS, RIP, RFLAGS, SS, RSP)
- 切换地址空间(如果需要):
- 新线程继续执行
陷阱帧(KTRAP_FRAME)
- 保存用户态到内核态切换时的寄存器
|
1 2 3 4 5 6 |
KTRAP_FRAME关键字段: - Rip: 用户态返回地址 - Rsp: 用户态栈指针 - Rax, Rcx, Rdx...: 通用寄存器 - SegCs, SegSs: 段选择子 - EFlags: 标志寄存器 |
windbg
|
1 2 3 |
.trap <address> // 切换到指定陷阱帧 !trap // 显示当前陷阱帧 dt nt!_KTRAP_FRAME // 显示结构定义 |
上下文切换成本
- 直接成本
|
1 2 3 4 5 |
- 保存/恢复寄存器:几十到上百个CPU周期 - 内核态切换开销:约1000-1500个周期 - 跨进程切换额外成本: - TLB刷新:数千个周期 - 缓存失效:首次访问内存变慢 |
- 间接成本
|
1 2 3 4 5 |
- CPU缓存污染(Cache Pollution) - L1缓存可能完全失效 - L2/L3缓存部分失效 - 分支预测器失效 - 指令流水线刷新 |
- 减少上下文切换的策略
|
1 2 3 4 5 |
- 合理设置线程数(通常不超过CPU核心数的2倍) - 使用线程池避免频繁创建销毁 - 减少锁竞争 - 使用无锁数据结构 - 异步I/O避免阻塞 |
线程同步机制
概述
- 线程同步解决多线程访问共享资源时的竞争条件和数据一致性问题
临界区(Critical Section)
- 特点
|
1 2 3 4 |
- 用户态轻量级同步对象 - 只能在同一进程内使用 - 支持递归锁定 - 有自旋锁优化(多核系统) |
- 内部结构
|
1 2 3 4 5 6 |
CRITICAL_SECTION结构: - LockCount: 锁计数器(-1表示未锁定) - RecursionCount: 递归计数 - OwningThread: 拥有线程ID - LockSemaphore: 内核信号量句柄(竞争时使用) - SpinCount: 自旋次数 |
- 工作原理
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
进入临界区: 1. 尝试原子性递减LockCount 2. 如果成功(从-1变为0),获取锁,记录OwningThread 3. 如果失败(已被锁定): - 如果是同一线程,增加RecursionCount(递归锁) - 如果是其他线程: a. 多核系统:自旋等待SpinCount次 b. 仍未获取:调用内核信号量等待 离开临界区: 1. 递减RecursionCount 2. 如果RecursionCount为0: - 清除OwningThread - 原子性递增LockCount - 如果有等待者,唤醒信号量 |
windbg
|
1 2 3 |
dt _RTL_CRITICAL_SECTION <address> !critsec <address> // 显示临界区状态 !locks // 显示所有临界区 |
互斥体(Mutex)
- 特点
|
1 2 3 4 5 |
- 内核同步对象 - 可跨进程使用(命名互斥体) - 支持递归锁定 - 所有权概念:只有拥有者能释放 - 线程终止时自动释放(abandoned状态) |
- 内部实现
|
1 2 3 4 |
基于内核的KMUTEX对象: - SignalState: 信号状态(1=未锁定,0=已锁定) - OwnerThread: 拥有线程指针 - ApcDisable: 禁用APC计数 |
- 等待机制
|
1 2 3 4 5 6 7 8 9 |
WaitForSingleObject(mutex, timeout): 1. 调用nt!NtWaitForSingleObject 2. 内核检查信号状态: - 如果可用:设置为不可用,返回 - 如果不可用: a. 创建KWAIT_BLOCK添加到对象等待列表 b. 设置线程状态为Waiting c. 调度器切换到其他线程 3. 被唤醒后继续执行 |
信号量(Semaphore)
- 特点
|
1 2 3 4 |
- 内核同步对象 - 维护一个计数器(资源数量) - 无所有权概念:任何线程都能释放 - 适合限制并发访问数量 |
- 工作原理
|
1 2 3 4 |
信号量计数器: - 初始值:最大资源数 - Wait操作:计数器-1,如果<=0则等待 - Release操作:计数器+1,唤醒等待线程 |
事件(Event)
- 特点
|
1 2 3 4 |
- 内核同步对象 - 两种类型: - ManualReset: 需要手动重置,可唤醒所有等待者 - AutoReset: 自动重置,只唤醒一个等待者 |
- 状态转换
|
1 2 3 4 5 6 |
Event状态: - Signaled(有信号): 等待会立即返回 - Non-signaled(无信号): 等待会阻塞 SetEvent(): 设置为有信号状态 ResetEvent(): 设置为无信号状态 |
读写锁(SRW Lock - Slim Reader/Writer Lock)
- 特点
|
1 2 3 4 |
- 用户态同步,性能优异 - 支持共享读和独占写 - 不支持递归锁定 - 非常轻量(一个指针大小) |
- 工作模式
|
1 2 3 4 5 6 7 8 |
共享模式(读锁): - 多个线程可同时持有 - 不阻塞其他读者 - 阻塞写者 独占模式(写锁): - 只有一个线程持有 - 阻塞所有读者和写者 |
- 内部实现
|
1 2 3 4 |
RTL_SRWLOCK(一个指针): - 低位表示状态(共享/独占/等待者) - 竞争时指向内核等待块链表 - 使用原子操作实现快速路径 |
条件变量(Condition Variable)
- 特点
|
1 2 3 |
- 与SRW Lock或临界区配合使用 - 实现复杂的等待条件 - 避免忙等待 |
- 使用模式
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
等待者: 1. 获取锁 2. 检查条件,如果不满足: - SleepConditionVariableCS/SRW() - 原子性释放锁并进入等待 3. 被唤醒后重新获取锁 4. 再次检查条件(防止虚假唤醒) 5. 释放锁 通知者: 1. 获取锁 2. 修改共享状态 3. WakeConditionVariable() / WakeAllConditionVariable() 4. 释放锁 |
自旋锁(Spinlock)
- 特点
|
1 2 3 4 |
- 忙等待,不进入内核 - 适合非常短的临界区 - 用户态通常使用InterlockedXxx实现 - 内核态有专门的KSPIN_LOCK |
- 实现原理
|
1 2 3 4 5 6 7 8 9 |
简单自旋锁: while (InterlockedCompareExchange(&lock, 1, 0) != 0) { // 自旋等待 _mm_pause(); // CPU hint,降低功耗 } // 临界区代码 InterlockedExchange(&lock, 0); // 释放 |
进程间同步
命名互斥体(Named Mutex)
- 确保只有一个进程访问资源,没有传递任何数据
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// 进程A创建 HANDLE hMutex = CreateMutex( NULL, // 默认安全描述符 FALSE, // 初始不拥有 L"Global\\MyMutex" // 全局命名空间 ); // 进程B打开 HANDLE hMutex = OpenMutex( SYNCHRONIZE, // 访问权限 FALSE, // 不继承 L"Global\\MyMutex" ); // 两个进程现在可以同步访问共享资源 WaitForSingleObject(hMutex, INFINITE); // ... 临界区代码 ... ReleaseMutex(hMutex); |
|
1 2 3 4 5 |
Global\: 全局命名空间,所有会话可见 Local\: 本地命名空间,仅当前会话可见(默认) Session\<n>\: 特定会话命名空间 在服务和桌面应用交互时,Global\很重要 |
命名事件(Event)
B等待A完成,只是协调执行顺序,没有数据内容
|
1 2 3 4 5 6 7 8 9 |
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, L"Global\\Ready"); // 进程A // 初始化完成后 SetEvent(hEvent); // 进程B WaitForSingleObject(hEvent, INFINITE); // 现在可以开始工作 |
命名信号量(Semaphore)
- 控制并发访问数量,不传递数据
|
1 2 3 4 5 6 |
HANDLE hSem = CreateSemaphore(NULL, 3, 3, L"Global\\Pool"); // 多个进程竞争资源池 WaitForSingleObject(hSem, INFINITE); // 使用资源 ReleaseSemaphore(hSem, 1, NULL); |
|
1 2 3 4 5 6 7 8 9 |
// 限制跨进程的并发访问数 HANDLE hSem = CreateSemaphore( NULL, 5, // 初始计数 5, // 最大计数 L"Global\\ResourcePool" ); // 其他进程可以OpenSemaphore访问 |
命名管道(Named Pipe)
- 虽然主要用于通信,但也能实现同步
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// 服务器端 HANDLE hPipe = CreateNamedPipe( L"\\\\.\\pipe\\MyPipe", PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 512, 512, 0, NULL ); // 等待客户端连接(阻塞操作,实现同步) ConnectNamedPipe(hPipe, NULL); // 客户端 HANDLE hPipe = CreateFile( L"\\\\.\\pipe\\MyPipe", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); |
邮槽(Mailslot)
- 单向广播通信机制,可用于简单同步
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 接收者 HANDLE hMailslot = CreateMailslot( L"\\\\.\\mailslot\\MyMailslot", 0, MAILSLOT_WAIT_FOREVER, NULL ); // 发送者(任何进程) HANDLE hFile = CreateFile( L"\\\\.\\mailslot\\MyMailslot", GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); WriteFile(hFile, data, size, &written, NULL); |
WM_COPYDATA消息(窗口程序)
- 进程间发送消息,实现同步通知
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 发送进程 HWND hTargetWnd = FindWindow(NULL, L"Target Window"); COPYDATASTRUCT cds; cds.dwData = 1; cds.cbData = strlen(message) + 1; cds.lpData = message; SendMessage(hTargetWnd, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cds); // 接收进程 case WM_COPYDATA: { COPYDATASTRUCT* cds = (COPYDATASTRUCT*)lParam; // 处理数据 return TRUE; } |
全局原子(Global Atom)
- 共享字符串的轻量级机制
|
1 2 3 4 5 6 7 8 9 10 |
// 进程A ATOM atom = GlobalAddAtom(L"MyUniqueString"); // 进程B ATOM atom = GlobalFindAtom(L"MyUniqueString"); if (atom != 0) { // 字符串存在 } GlobalDeleteAtom(atom); |
作业对象(Job Object)
- 控制和同步一组进程
- 注意是一组进程,并非进程间
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 创建作业对象 HANDLE hJob = CreateJobObject(NULL, L"Global\\MyJob"); // 设置限制 JOBOBJECT_BASIC_LIMIT_INFORMATION jobli = {0}; jobli.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; SetInformationJobObject(hJob, JobObjectBasicLimitInformation, &jobli, sizeof(jobli)); // 将进程加入作业 AssignProcessToJobObject(hJob, hProcess); // 关闭作业句柄时,所有进程都会终止(同步终止) CloseHandle(hJob); |
进程间通信
管道
- 父进程向子进程发送了"
Hello"这个数据
|
1 2 3 4 5 6 7 8 9 |
// 匿名管道:父子进程间传递数据 HANDLE hRead, hWrite; CreatePipe(&hRead, &hWrite, NULL, 0); // 父进程写 WriteFile(hWrite, "Hello", 5, &written, NULL); // 子进程读 ReadFile(hRead, buffer, size, &read, NULL); |
命名管道(Named Pipe)
- 客户端向服务器传递数据
|
1 2 3 4 5 6 7 8 |
// 服务器 HANDLE hPipe = CreateNamedPipe(L"\\\\.\\pipe\\MyPipe", ...); ConnectNamedPipe(hPipe, NULL); ReadFile(hPipe, buffer, size, &read, NULL); // 客户端 HANDLE hPipe = CreateFile(L"\\\\.\\pipe\\MyPipe", ...); WriteFile(hPipe, data, size, &written, NULL); |
共享内存
- 进程
A向进程B传递了"Shared Data"
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 进程A HANDLE hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, L"MySharedMem"); char* pBuf = (char*)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 1024); strcpy(pBuf, "Shared Data"); // 写入数据 // 进程B HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"MySharedMem"); char* pBuf = (char*)MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 1024); printf("%s", pBuf); // 读取数据 |
消息队列
- 发送消息和数据
|
1 2 |
// Windows使用PostMessage/SendMessage PostMessage(hTargetWnd, WM_USER+1, wParam, lParam); |
套接字(Socket)
- 网络数据传输
|
1 2 3 |
// TCP通信 send(socket, data, size, 0); recv(socket, buffer, size, 0); |
RPC(Remote Procedure Call)
- 传递参数和返回值
|
1 2 |
// 调用远程过程 result = RemoteFunction(param1, param2); |
COM(Component Object Model)
- 方法调用传递数据
|
1 2 |
// 跨进程COM调用 pInterface->Method(data); |
其他
死锁
- 场景
|
1 2 3 |
线程A:持有锁X,等待锁Y 线程B:持有锁Y,等待锁X → 形成等待环,死锁! |
windbg
|
1 2 3 4 5 6 7 8 9 10 |
!locks // 显示所有锁 !cs -l // 显示临界区和等待者 !handle 0 f // 显示所有句柄 !thread <address> // 查看线程等待的对象 查找等待链: 1. 找到阻塞的线程 2. 查看它等待的对象 3. 查看谁持有该对象 4. 递归分析,找出等待环 |
原子操作
InterlockedXxx系列:
|
1 2 3 4 5 6 7 8 9 |
常用原子操作: - InterlockedIncrement/Decrement: 原子加减 - InterlockedExchange: 原子交换 - InterlockedCompareExchange: CAS操作 - InterlockedAdd: 原子加法 - InterlockedAnd/Or/Xor: 原子位操作 这些操作使用CPU的LOCK前缀指令实现, 保证多核系统的原子性和内存可见性 |
内存屏障
- 内存序保证
|
1 2 3 4 5 |
MemoryBarrier(): 全屏障 ReadAcquire/WriteRelease语义 防止编译器和CPU重排序指令, 确保多线程程序的正确性 |
Dump分析中的应用
查看进程信息
|
1 2 3 4 |
!process 0 0 // 所有进程摘要 !process <address> 7 // 详细信息,包括所有线程 lm // 加载的模块 !handle 0 f // 所有句柄 |
分析线程状态
|
1 2 3 4 |
~*k // 所有线程调用栈 !runaway // 线程CPU时间统计 !ready // 就绪队列 !thread <address> // 线程详细信息 |
同步问题诊断
|
1 2 3 4 |
!locks // 查找死锁 !cs -l -o // 临界区和拥有者 !handle <handle> f // 互斥体/事件状态 !winsock // 网络相关等待 |
关键分析技巧
- 死锁分析:
- 找出
Waiting状态的线程 - 查看等待的对象(
WaitBlockList) - 找到对象的拥有者
- 构建等待图,识别环
- 找出
- 性能问题:
- 使用
!runaway查看CPU时间 - 检查是否过度上下文切换
- 分析锁竞争(
!cs -s查看争用计数) - 检查优先级反转
- 使用
- 内存访问违例:
- 查看陷阱帧确定异常地址
- 检查
VAD确认地址是否有效 - 分析调用栈推断原因
声明:本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ C++并发编程 _ 共享数据05/16
- ♥ Dump分析:重复释放堆内存,死锁03/17
- ♥ Linux 线程概述&&创建03/31
- ♥ Linux 进程描述&&相关介绍03/28
- ♥ Linux 线程等待&&取消&&终止03/31
- ♥ C++_关于Async、Packaged_task、Promise的总结11/13