如何以低CPU开销动态跟踪Windows进程的加载与卸载?
针对你在老旧系统上需要低CPU开销跟踪进程启停的需求,我整理了几个实用方案,从简单优化到原生系统机制都有,你可以根据自己的场景选择:
问题根源分析
你当前用轮询K32EnumProcesses的方式,因为是无限高频循环查询,CPU占用高是必然的——老旧系统本身算力有限,轮询会持续占用核心资源。而tasklist本质也是基于相同的枚举逻辑,所以不会有任何改善。
具体优化方案
1. 快速优化现有轮询逻辑(改动最小,见效快)
如果暂时不想调整整体架构,可以先降低轮询频率,根据你的恶意扫描敏感度需求,把无限循环改成每隔500ms-1秒查询一次,这样CPU占用会大幅下降。比如在你的代码里加个休眠:
import time # ... 保留你原有的Procs类代码不变 if __name__ == '__main__': proc_obj = Procs() prev_procs = set() while True: curr_procs = set(proc_obj.get_all_processes()) dead = proc_obj.find_dead(prev_procs, curr_procs) live = proc_obj.find_live(prev_procs, curr_procs) [print(f'died: {d}') for d in dead] [print(f'live: {l}') for l in live] prev_procs = curr_procs time.sleep(1) # 增加休眠,降低轮询频率
这种方式兼容性拉满,适合老旧系统快速落地,但缺点是无法做到实时通知,会有一定延迟。
2. 使用WMI事件通知(用户态,无需驱动,低开销)
Windows的WMI支持事件订阅机制,可以让系统在进程创建/终止时主动通知你的程序,而非你主动轮询。这种方式CPU开销极低,只有事件发生时才会触发回调。
示例Python代码(依赖wmi库):
import wmi def on_process_created(event): print(f"新进程启动: PID {event.ProcessId}, 名称 {event.ProcessName}") def on_process_terminated(event): print(f"进程终止: PID {event.ProcessId}, 名称 {event.ProcessName}") if __name__ == '__main__': c = wmi.WMI() # 订阅进程创建事件 creation_watcher = c.Win32_Process.watch_for("creation") # 订阅进程终止事件 termination_watcher = c.Win32_Process.watch_for("deletion") while True: try: # 等待创建事件触发,超时1秒避免阻塞 creation_event = creation_watcher(timeout_ms=1000) if creation_event: on_process_created(creation_event) except wmi.x_wmi_timed_out: pass try: termination_event = termination_watcher(timeout_ms=1000) if termination_event: on_process_terminated(termination_event) except wmi.x_wmi_timed_out: pass
注意:需要先安装wmi库(pip install wmi),老旧系统只要默认启用了WMI服务(大部分Windows系统都默认开启)就能正常运行,无需管理员权限。
3. 使用ETW(Event Tracing for Windows)—— 极致性能
ETW是Windows原生的高性能事件跟踪机制,几乎不占用CPU资源,适合对性能敏感的场景。你可以订阅Microsoft-Windows-Kernel-Process提供的进程创建/终止事件。
示例Python代码(依赖pyetw库):
from pyetw import ETW, providers def process_event_callback(event): if event.event_id == 10: # 进程创建事件ID pid = event.properties['ProcessID'] image_path = event.properties['ImageName'] print(f"进程启动: PID {pid}, 路径 {image_path}") elif event.event_id == 11: # 进程终止事件ID pid = event.properties['ProcessID'] print(f"进程终止: PID {pid}") if __name__ == '__main__': etw_tracker = ETW( providers=[providers.Microsoft_Windows_Kernel_Process], event_callback=process_event_callback ) etw_tracker.start() try: input("按回车键停止跟踪...\n") finally: etw_tracker.stop()
ETW的优点是性能拉满,但需要管理员权限,且Windows XP及更早版本对ETW的支持有限,适合Windows 7及以上的老旧系统。
4. 内核驱动(最后选项,门槛高)
如果上述用户态方案都无法满足实时性要求,只能选择内核驱动方案。通过注册PsSetCreateProcessNotifyRoutine回调,可以捕获最底层的进程创建/终止事件,几乎无延迟,CPU开销极低。
但缺点很明显:
- 需要用C/C++编写内核驱动代码,开发门槛高
- 驱动需要签名(现代Windows强制要求,XP等老旧系统可能不需要)
- 内核编程容易导致系统崩溃,需要严谨测试
对于你的恶意软件扫描场景,优先考虑用户态方案即可,驱动是最后的兜底选项。
场景适配推荐
结合你的老旧系统+恶意软件扫描需求:
- 能接受几秒延迟:优先优化轮询逻辑,改动最小,兼容性最好
- 需要接近实时通知:优先用WMI,用户态实现,无需特殊权限,开销低
- 追求极致性能:用ETW,适合Windows 7及以上系统
- 驱动方案仅在上述都无法满足时考虑
内容的提问来源于stack exchange,提问作者ActualUser

