如何在C/C++中跨进程获取目标活跃线程的资源使用信息?
Great question! Let's break this down clearly—first how to implement thread monitoring in C/C++, then how to pull resource usage data (like total CPU time and input operations) from threads in other processes without touching their source code or the OS kernel. I'll cover both Linux and Windows since those are the most common environments.
We can split this into two scenarios: monitoring threads within your own process, and monitoring threads in external processes (which is what you're most interested in for the second part of your question).
In-Process Thread Monitoring
For threads you create yourself, the native APIs give you direct access to their metadata and resource usage:
- Linux: Use pthread APIs like
pthread_getattr_np()to fetch thread attributes, andpthread_self()to get the current thread ID. To track CPU time, you can callclock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts)to get the thread-specific CPU time. - Windows: After creating a thread with
CreateThread(), you hold a handle to it. UseGetThreadTimes()to get user/kernel mode CPU time, andGetThreadPriority()to check its priority level.
Cross-Process Thread Monitoring (No Source/Kernel Modifications)
This is the core of your question, and both platforms have standard, non-intrusive ways to do this:
Linux Platform
Linux's /proc filesystem is your best friend here—it's a virtual filesystem that exposes real-time process/thread data without requiring kernel changes or target program modifications.
- Thread Enumeration: List all threads of a target process by looking at the subdirectories under
/proc/[PID]/task/—each subdirectory name is a thread ID (TID). - Total CPU Time: For each thread, read the
/proc/[PID]/task/[TID]/statfile. The 14th field (utime) is user-mode CPU time, and the 15th field (stime) is kernel-mode CPU time, both measured in jiffies. Convert these to seconds by dividing bysysconf(_SC_CLK_TCK). - Input/IO Operations: Read the
/proc/[PID]/task/[TID]/iofile—fields likerchar(total bytes read by the thread) andread_bytes(actual disk reads) give you input operation metrics. - Simpler Alternative: Use the
libprocpslibrary (part of most Linux distros) to wrap/procparsing, so you don't have to manually parse text files. Functions likereadproc()can fetch thread data directly.
Here's a quick C example to fetch a thread's CPU time:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int get_thread_cpu_time(pid_t pid, pid_t tid, double *user_sec, double *sys_sec) { char path[256]; snprintf(path, sizeof(path), "/proc/%d/task/%d/stat", pid, tid); FILE *fp = fopen(path, "r"); if (!fp) return -1; char buf[1024]; if (!fgets(buf, sizeof(buf), fp)) { fclose(fp); return -1; } // Parse utime (14th field) and stime (15th field) long utime, stime; int fields_read = sscanf(buf, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %ld %ld", &utime, &stime); fclose(fp); if (fields_read != 2) return -1; long clk_tck = sysconf(_SC_CLK_TCK); *user_sec = (double)utime / clk_tck; *sys_sec = (double)stime / clk_tck; return 0; } int main() { pid_t target_pid = 1234; // Replace with your target process ID pid_t target_tid = 1235; // Replace with your target thread ID double user, sys; if (get_thread_cpu_time(target_pid, target_tid, &user, &sys) == 0) { printf("Thread %d (PID %d) CPU Time: User %.2fs | Kernel %.2fs\n", target_tid, target_pid, user, sys); } else { perror("Failed to retrieve thread CPU time"); } return 0; }
Windows Platform
Windows provides native APIs to enumerate threads and fetch their resource data without modifying the target or kernel:
- Thread Enumeration: Use
CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)to take a snapshot of all system threads, thenThread32First()/Thread32Next()to filter threads belonging to your target PID. - Total CPU Time: For each thread ID, call
OpenThread(THREAD_QUERY_INFORMATION, FALSE, tid)to get a thread handle, then useGetThreadTimes()to retrieve user-mode and kernel-mode CPU time (converted fromFILETIMEto seconds by dividing by 10^7). - Input/IO Operations: Windows doesn't expose thread-level IO stats directly via simple APIs—you'll need to use Event Tracing for Windows (ETW). ETW lets you capture IO events associated with specific threads without modifying the target program. You'll need to register a trace session, enable IO providers, and parse the events to aggregate thread-level IO data.
Here's a C++ example to enumerate a process's threads and print their CPU time:
#include <windows.h> #include <tlhelp32.h> #include <stdio.h> void print_thread_cpu_times(DWORD target_pid) { HANDLE h_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (h_snapshot == INVALID_HANDLE_VALUE) { printf("CreateToolhelp32Snapshot failed: %d\n", GetLastError()); return; } THREADENTRY32 thread_entry; thread_entry.dwSize = sizeof(THREADENTRY32); if (!Thread32First(h_snapshot, &thread_entry)) { printf("Thread32First failed: %d\n", GetLastError()); CloseHandle(h_snapshot); return; } do { if (thread_entry.th32OwnerProcessID == target_pid) { HANDLE h_thread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, thread_entry.th32ThreadID); if (h_thread) { FILETIME create_time, exit_time, kernel_time, user_time; if (GetThreadTimes(h_thread, &create_time, &exit_time, &kernel_time, &user_time)) { // Convert FILETIME (100-nanosecond intervals) to seconds ULARGE_INTEGER kernel, user; kernel.LowPart = kernel_time.dwLowDateTime; kernel.HighPart = kernel_time.dwHighDateTime; user.LowPart = user_time.dwLowDateTime; user.HighPart = user_time.dwHighDateTime; double kernel_sec = (double)kernel.QuadPart / 10000000.0; double user_sec = (double)user.QuadPart / 10000000.0; printf("Thread ID: %lu | User Time: %.2fs | Kernel Time: %.2fs\n", thread_entry.th32ThreadID, user_sec, kernel_sec); } else { printf("GetThreadTimes failed for thread %lu: %d\n", thread_entry.th32ThreadID, GetLastError()); } CloseHandle(h_thread); } else { printf("OpenThread failed for thread %lu: %d\n", thread_entry.th32ThreadID, GetLastError()); } } } while (Thread32Next(h_snapshot, &thread_entry)); CloseHandle(h_snapshot); } int main() { DWORD target_pid = 1234; // Replace with your target process ID print_thread_cpu_times(target_pid); return 0; }
- Permissions: On Linux, you'll need read access to
/proc(most users can read their own processes; root is needed for others). On Windows, your program needsPROCESS_QUERY_INFORMATIONaccess to the target process andTHREAD_QUERY_INFORMATIONfor threads. - Performance: Reading
/procor using Windows snapshot APIs is lightweight and won't impact target processes significantly. ETW can have minor overhead if tracing high-volume events, but it's still non-intrusive. - Compatibility:
/procis standard across all Linux distros. Windows APIs likeCreateToolhelp32Snapshotwork on XP and later; ETW is supported from Vista onwards.
内容的提问来源于stack exchange,提问作者Jean-Luc Nacif Coelho

