Windows需用DuplicateHandle将伪句柄转为有效句柄后调用GetThreadTimes获取内核时间(100ns单位),Linux则解析/proc/[tid]/stat第15字段stime(jiffies),二者均需换算并注意采样间隔与精度限制。

Windows下用GetThreadTimes获取线程内核时间
Windows提供GetThreadTimes函数,能直接拿到线程在内核态(Kernel Mode)执行的时间,单位是100纳秒。它需要线程句柄,且必须有THREAD_QUERY_INFORMATION权限。
常见错误是传入当前线程的伪句柄(如(HANDLE)-1)后没做DuplicateHandle——伪句柄不能直接用于GetThreadTimes,否则返回失败,GetLastError()为ERROR_INVALID_HANDLE。
- 先用
GetCurrentThread()拿到伪句柄,再用DuplicateHandle复制出一个可查询的句柄 -
GetThreadTimes返回四个FILETIME:创建时间、退出时间、内核时间、用户时间;关注第三个参数 - 把
FILETIME转成毫秒:先组合成64位整数,再除以10000(因为1ms = 10000×100ns)
// 示例:获取当前线程内核时间(ms)
HANDLE hThread;
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
GetCurrentProcess(), &hThread,
THREAD_QUERY_INFORMATION, FALSE, 0);
FILETIME ftCreate, ftExit, ftKernel, ftUser;
if (GetThreadTimes(hThread, &ftCreate, &ftExit, &ftKernel, &ftUser)) {
ULARGE_INTEGER ui;
ui.LowPart = ftKernel.dwLowDateTime;
ui.HighPart = ftKernel.dwHighDateTime;
DWORD kernelMs = (DWORD)(ui.QuadPart / 10000);
}
CloseHandle(hThread);
Linux下通过/proc/[tid]/stat读取内核态jiffies
Linux没有直接API,得靠解析/proc/[tid]/stat文件。第14个字段(utime)是用户态jiffies,第15个(stime)才是内核态jiffies。注意:这是自进程启动以来的累计值,不是实时差值。
容易踩的坑是误读/proc/self/stat——它对应的是当前进程,不是当前线程;要精确到线程,必须用/proc/[tid]/stat,其中tid可通过syscall(SYS_gettid)获取。
立即学习“C++免费学习笔记(深入)”;
-
tid和pid在Linux线程模型中通常相同(NPTL),但多线程下tid更准确 - jiffies需换算成毫秒:除以
sysconf(_SC_CLK_TCK)得到秒,再×1000;典型值为100Hz → 1 jiffy = 10ms - 文件可能被其他线程删除(线程退出后
/proc/[tid]消失),读取前建议检查access(..., F_OK)
跨平台时别指望std::this_thread::get_id()能帮你
std::this_thread::get_id()只返回逻辑ID,不携带OS线程标识,无法映射到Windows句柄或Linux tid。想跨平台获取内核时间,必须走系统API分支,不能依赖标准库。
有人试图用pthread_self()(POSIX)反向查tid,但glibc里pthread_t是结构体,不是tid数值;Linux下真正可用的是syscall(SYS_gettid),Windows则必须用GetCurrentThread() + DuplicateHandle。
- 不要尝试把
std::thread::native_handle()强转为HANDLE或pthread_t——类型不兼容,行为未定义 - 如果封装成工具函数,建议按
#ifdef _WIN32/#ifdef __linux__分治,避免运行时探测
精度和采样时机影响结果可信度
内核时间是累计值,两次读取的差值才有意义。但两次读之间若线程被调度出去,差值会包含其他线程占用CPU的时间(即非独占性),所以单次差值只能反映“该线程在此时段内内核态总耗时”,不能等价于“该线程实际独占CPU的时间”。
更隐蔽的问题是:短时间间隔(比如几毫秒)下,jiffies或100ns tick可能没变化,导致差值为0,不代表没干活——尤其在低负载或高tick率系统上。
- 建议最小采样间隔 ≥ 10ms,避免噪声干扰
- 频繁调用
GetThreadTimes或读/proc有开销,生产环境慎用于高频监控 - 调试时可用,但性能敏感路径别嵌入实时采集逻辑
真正难的不是读数据,而是理解“内核时间”在调度器视角下的含义:它是线程在内核代码中执行的时长,比如系统调用、中断处理、锁竞争等待等,不是CPU物理核心的占用时间。这点常被忽略。


















