能否通过Python获取进程线程信息?需线程名、内存占用等数据
获取进程线程的名称与内存消耗
是的,可以通过Python获取线程名称和内存消耗数据,psutil本身没封装这些功能,需要结合平台特定的API或系统文件来实现:
一、获取线程名称
Linux 平台
Linux的/proc文件系统直接暴露了线程的名称,位于/proc/<pid>/task/<tid>/comm路径下,读取这个文件就能得到线程名称:
import psutil def get_thread_names(pid): thread_names = {} proc = psutil.Process(pid) for thread in proc.threads(): tid = thread.id try: with open(f"/proc/{pid}/task/{tid}/comm", "r") as f: thread_names[tid] = f.read().strip() except FileNotFoundError: thread_names[tid] = "unknown" return thread_names # 示例:获取当前进程的线程名称 current_pid = psutil.Process().pid print(get_thread_names(current_pid))
Windows 平台
Windows 10 1607及以上版本可以通过kernel32.dll的GetThreadDescription函数获取线程名称,需要用ctypes调用系统API:
import psutil import ctypes from ctypes import wintypes kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) # 定义系统函数的参数和返回值类型 kernel32.OpenThread.argtypes = (wintypes.DWORD, wintypes.BOOL, wintypes.DWORD) kernel32.OpenThread.restype = wintypes.HANDLE kernel32.GetThreadDescription.argtypes = (wintypes.HANDLE, ctypes.POINTER(wintypes.LPWSTR)) kernel32.GetThreadDescription.restype = wintypes.HRESULT kernel32.CloseHandle.argtypes = (wintypes.HANDLE,) kernel32.CloseHandle.restype = wintypes.BOOL def get_thread_names_windows(pid): thread_names = {} proc = psutil.Process(pid) for thread in proc.threads(): tid = thread.id # 打开线程,需要THREAD_QUERY_LIMITED_INFORMATION权限 h_thread = kernel32.OpenThread(0x0800, False, tid) if not h_thread: thread_names[tid] = "unknown" continue name_ptr = wintypes.LPWSTR() if kernel32.GetThreadDescription(h_thread, ctypes.byref(name_ptr)) == 0: # S_OK thread_names[tid] = name_ptr.value kernel32.LocalFree(name_ptr) else: thread_names[tid] = "unknown" kernel32.CloseHandle(h_thread) return thread_names # 示例:获取当前进程的线程名称 current_pid = psutil.Process().pid print(get_thread_names_windows(current_pid))
二、获取线程内存消耗
线程的内存主要包括栈内存和线程本地存储(TLS),不同平台的获取方式不同:
Linux 平台
通过/proc/<pid>/task/<tid>/status文件可以读取线程的栈内存(VmStk字段)和TLS相关内存(VmData字段):
import psutil def get_thread_memory_linux(pid): thread_memory = {} proc = psutil.Process(pid) for thread in proc.threads(): tid = thread.id mem_info = {"stack_memory": "unknown", "tls_memory": "unknown"} try: with open(f"/proc/{pid}/task/{tid}/status", "r") as f: for line in f: if line.startswith("VmStk:"): mem_info["stack_memory"] = f"{line.split()[1]} {line.split()[2]}" elif line.startswith("VmData:"): mem_info["tls_memory"] = f"{line.split()[1]} {line.split()[2]}" thread_memory[tid] = mem_info except FileNotFoundError: thread_memory[tid] = mem_info return thread_memory # 示例:获取当前进程的线程内存信息 current_pid = psutil.Process().pid print(get_thread_memory_linux(current_pid))
Windows 平台
Windows没有直接获取线程内存的简单API,需要通过VirtualQueryEx遍历线程栈区域来计算栈内存大小,示例如下:
import psutil import ctypes from ctypes import wintypes kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) # 定义系统函数和数据结构 kernel32.OpenProcess.argtypes = (wintypes.DWORD, wintypes.BOOL, wintypes.DWORD) kernel32.OpenProcess.restype = wintypes.HANDLE kernel32.OpenThread.argtypes = (wintypes.DWORD, wintypes.BOOL, wintypes.DWORD) kernel32.OpenThread.restype = wintypes.HANDLE kernel32.GetThreadContext.argtypes = (wintypes.HANDLE, ctypes.POINTER(ctypes.c_void_p)) kernel32.GetThreadContext.restype = wintypes.BOOL kernel32.CloseHandle.argtypes = (wintypes.HANDLE,) kernel32.CloseHandle.restype = wintypes.BOOL class MEMORY_BASIC_INFORMATION(ctypes.Structure): _fields_ = [ ("BaseAddress", wintypes.LPVOID), ("AllocationBase", wintypes.LPVOID), ("AllocationProtect", wintypes.DWORD), ("RegionSize", wintypes.SIZE_T), ("State", wintypes.DWORD), ("Protect", wintypes.DWORD), ("Type", wintypes.DWORD) ] kernel32.VirtualQueryEx.argtypes = (wintypes.HANDLE, wintypes.LPCVOID, ctypes.POINTER(MEMORY_BASIC_INFORMATION), wintypes.SIZE_T) kernel32.VirtualQueryEx.restype = wintypes.SIZE_T def get_thread_stack_memory_windows(pid): thread_stack = {} # 打开进程,需要PROCESS_QUERY_INFORMATION和PROCESS_VM_READ权限 h_process = kernel32.OpenProcess(0x0400 | 0x0010, False, pid) if not h_process: return thread_stack proc = psutil.Process(pid) for thread in proc.threads(): tid = thread.id # 打开线程,需要THREAD_GET_CONTEXT权限 h_thread = kernel32.OpenThread(0x0010, False, tid) if not h_thread: thread_stack[tid] = "unknown" continue # 获取线程上下文,获取栈指针 class CONTEXT(ctypes.Structure): _fields_ = [("Esp", wintypes.DWORD)] if ctypes.sizeof(ctypes.c_void_p) == 4 else [("Rsp", wintypes.DWORD64)] context = CONTEXT() if kernel32.GetThreadContext(h_thread, ctypes.byref(context)): stack_ptr = context.Esp if ctypes.sizeof(ctypes.c_void_p) ==4 else context.Rsp total_size = 0 current_ptr = stack_ptr while True: mbi = MEMORY_BASIC_INFORMATION() size = kernel32.VirtualQueryEx(h_process, wintypes.LPCVOID(current_ptr), ctypes.byref(mbi), ctypes.sizeof(mbi)) if size ==0: break # 检查是否是已提交的栈区域 if mbi.State == 0x1000 and (mbi.Protect & 0x04): # MEM_COMMIT | PAGE_READWRITE total_size += mbi.RegionSize current_ptr = ctypes.cast(mbi.BaseAddress, wintypes.LPVOID).value + mbi.RegionSize else: break thread_stack[tid] = f"{total_size / 1024:.2f} KB" else: thread_stack[tid] = "unknown" kernel32.CloseHandle(h_thread) kernel32.CloseHandle(h_process) return thread_stack # 示例:获取当前进程的线程栈内存 current_pid = psutil.Process().pid print(get_thread_stack_memory_windows(current_pid))
注意:线程内存数据是近似值,因为进程的大部分内存是线程共享的,线程仅占用栈、TLS等私有内存区域。
内容的提问来源于stack exchange,提问作者ali khan
相关产品推荐
相关产品推荐

