必须通过两步合成获取亲和性映射:先用getlogicalprocessorinformation()(windows)或/sys/devices/system/cpu/(linux)获取物理核心与逻辑cpu的拓扑映射,再用getprocessaffinitymask()或sched_getaffinity()获取当前亲和性掩码,最后交叉比对得出可用物理核心。

不能直接获取“亲和性映射”——亲和性是进程/线程级设置,不是系统固有拓扑;你真正需要的是:先获取物理核心与逻辑处理器的精确拓扑映射,再叠加当前进程或线程的实际亲和性掩码,二者交叉比对才能得出“哪些逻辑核可用、属于哪个物理核心”。
Windows:用 GetLogicalProcessorInformation() 提取拓扑,再用 GetProcessAffinityMask() 叠加亲和性
Windows 没有“亲和性映射表”这种结构,必须两步合成:
- 第一步:调用
GetLogicalProcessorInformation()两次,过滤出所有RelationProcessorCore类型条目,每个条目的ProcessorMask给出该物理核心上所有逻辑 CPU 的位图(bit 位置即逻辑 CPU ID) - 第二步:调用
GetProcessAffinityMask()获取当前进程允许运行的逻辑 CPU 掩码(DWORD_PTR类型),注意它只返回进程级掩码,不反映线程级单独设置 - 交叉比对:对每个
RelationProcessorCore条目,计算ProcessorMask & affinity_mask—— 若结果非零,说明该物理核心至少有一个逻辑 CPU 在亲和性范围内 - 常见坑:
ProcessorMask是ULONG_PTR,在 64 位系统上最多覆盖 64 个逻辑 CPU;若系统有超 64 个逻辑核(如启用处理器分组),必须改用GetLogicalProcessorInformationEx(RelationGroup, ...),否则高位被截断
Linux:解析 /sys/devices/system/cpu/ + sched_getaffinity() 合成可用物理核心
Linux 下没有统一 API 返回“亲和性映射”,可靠路径是文件系统 + 系统调用组合:
- 遍历
/sys/devices/system/cpu/cpu[0-9]*/目录,跳过软链接(如cpu0指向online),只处理真实目录 - 对每个
cpuN,读取topology/core_id和topology/physical_package_id,构建唯一键(package_id, core_id)→ {逻辑 CPU 列表} 映射 - 调用
sched_getaffinity(0, sizeof(cpu_set_t), &mask)获取当前线程亲和性集合,用CPU_ISSET(i, &mask)遍历(上限用sysconf(_SC_NPROCESSORS_ONLN),别硬写CPU_SETSIZE) - 对每个物理核心键,检查其包含的所有逻辑 CPU 是否至少有一个在
mask中 —— 若有,则该物理核心“部分可用”;全无则完全不可用 - 容易漏掉的细节:
topology/thread_siblings_list虽然更直观,但需额外解析范围(如"0-3"),而core_id + package_id更稳定,尤其在 ARM 或启用了 SMT 的虚拟机中
跨平台时别碰 std::thread::hardware_concurrency()
这个函数完全不能用于构建亲和性映射:
- 它返回值可能为 0(glibc 旧版本、静态链接、WSL1)、宿主机值(Docker/KVM)、或被 cgroups 隐藏后的错误值(容器内)
- 它不区分物理/逻辑,也不携带任何拓扑信息,无法告诉你哪个逻辑 CPU 属于哪个物理核心
- 它不反映当前亲和性限制 —— 即使你用
taskset -c 0,2启动程序,hardware_concurrency()仍返回全部逻辑核数 - 替代方案只有:Windows 用
GetLogicalProcessorInformation()+GetProcessAffinityMask(),Linux 用/sys/devices/system/cpu/+sched_getaffinity()
真正难的不是单点查询,而是把拓扑结构和亲和性动态叠加 —— 物理核心是否“可用”,取决于它的任意一个逻辑线程是否在当前掩码中;而这个判断必须在每次查询时重新做,因为亲和性可能被外部工具(如 taskset、numactl、cgroups)随时修改。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











