如何使用C语言在Linux中获取CPU数量?

77

有没有可以获取Linux可用CPU数量的API?我的意思是,不使用/proc/cpuinfo或任何其他sys节点文件……

我找到了这个使用sched.h的实现:

int GetCPUCount()
{
 cpu_set_t cs;
 CPU_ZERO(&cs);
 sched_getaffinity(0, sizeof(cs), &cs);

 int count = 0;
 for (int i = 0; i < 64; i++)
 {
  if (CPU_ISSET(i, &cs))
   count++;
  else
   break;
 }
 return count;
}

但是,难道没有使用常见库的更高级别的东西吗?


25
为什么人们如此害怕使用 /proc 目录?过去 15 年里我见过的每台 Linux 机器都有它,它始终与内核所知道的内容保持最新,而且其中现有内容的格式变化不大。 - cHao
1
我认为你尝试学习不同的做事方式很棒,但是你是在试图重新发明轮子吗? - David Weiser
对于gnulib系统,这个方法可以通过查看/proc来实现,但如果你真的想要一个简单的一行代码,并且没有太多的性能/安全方面的考虑,你可以使用 (system("exit `nproc`") >> 8) ...即使是busybox也有一个内部的nproc,所以在任何Linux上都应该可以正常工作(例如,我的路由器固件...)。移位操作是必需的,因为sh退出码嵌入了一个尾随的空字节,以便进行字符串处理。 - l.k
1
请参见 https://dev59.com/EXVC5IYBdhLWcg3w51lv。 - Jay Sullivan
4
/proc 不具备可移植性。 - Edd Barrett
1
@cHao 因为 /proc 忽略 cgroups,这可能会导致“超额订阅”,如果进程运行的线程数超过了在 cpuset cgroup 中授予它的数量。 - phzx_munki
9个回答

95
#include <unistd.h>
long number_of_processors = sysconf(_SC_NPROCESSORS_ONLN);

2
不错的解决方案,但似乎是Linux对POSIX的扩展:http://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html - Ciro Santilli OurBigBook.com
这将给出在线核心的数量。如果我使任何核心离线,它将不会在此返回。 - iDebD_gh
1
@iDebD_gh - 如果我没记错的话,_SC_NPROCESSORS_CONF 就是提供这个信息的。 - selbie
2
@iDebD_gh 你所说的离线核心是什么意思? - Lewis Chan

50
#include <stdio.h>
#include <sys/sysinfo.h>

int main(int argc, char *argv[])
{
    printf("This system has %d processors configured and "
        "%d processors available.\n",
        get_nprocs_conf(), get_nprocs());
    return 0;
}

https://linux.die.net/man/3/get_nprocs


4
这个答案与问题中给出的片段不会得到相同的结果。如果使用taskset将进程绑定到机器上的CPU子集,则使用sched_getaffinity()方法将给出分配的CPU数量,而get_nprocs()则给出机器可用的CPU总数。如果您使用此方法决定线程数,这是不好的,因为如果在多核机器上仅分配了一个核心,则该进程将会过载。 - Ed Bennett

21

这段代码(源自此处)应该适用于Windows和* NIX平台。

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>


int main() {
  long nprocs = -1;
  long nprocs_max = -1;
#ifdef _WIN32
#ifndef _SC_NPROCESSORS_ONLN
SYSTEM_INFO info;
GetSystemInfo(&info);
#define sysconf(a) info.dwNumberOfProcessors
#define _SC_NPROCESSORS_ONLN
#endif
#endif
#ifdef _SC_NPROCESSORS_ONLN
  nprocs = sysconf(_SC_NPROCESSORS_ONLN);
  if (nprocs < 1)
  {
    fprintf(stderr, "Could not determine number of CPUs online:\n%s\n", 
strerror (errno));
    exit (EXIT_FAILURE);
  }
  nprocs_max = sysconf(_SC_NPROCESSORS_CONF);
  if (nprocs_max < 1)
  {
    fprintf(stderr, "Could not determine number of CPUs configured:\n%s\n", 
strerror (errno));
    exit (EXIT_FAILURE);
  }
  printf ("%ld of %ld processors online\n",nprocs, nprocs_max);
  exit (EXIT_SUCCESS);
#else
  fprintf(stderr, "Could not determine number of CPUs");
  exit (EXIT_FAILURE);
#endif
}

我很久以前从某人那里得到了这段代码(不记得名字了)。 - Vikram.exe
2
我不确定发布这段代码片段真正回答了楼主的问题,尽管他们可能会从中逆向工程出一些有用的信息。 - MarkR
2
我同意 MarkR 的观点。chrisaycock 给出了简明扼要的答案。 - poindexter
1
如果_SC_NPROCESSORS_ONLN未定义,您应该使用#error预处理器指令。这是编译时失败,而不是运行时失败。 - Guido Flohr

13

sched_affinity() 开头提到的版本仍然比 /proc/cpuinfo 和/或 _SC_NPROCESSORS_ONLN 更好,因为它只计算给定进程可用的 CPU 数量(某些可能被外部进程调用 sched_setaffinity() 禁用)。唯一的变化是在循环中使用 CPU_COUNT() 代替 CPU_ISSET


12
使用/proc/cpuinfo是最干净且最具可移植性的解决方案。如果打开失败,您可以假设有1个CPU或2个CPU。依赖于已知CPU数量而不是进行微优化(例如选择要运行的理想线程数)的代码几乎肯定是在做一些愚蠢的事情。 _SC_NPROCESSORS_ONLN解决方案依赖于非标准(仅限于glibc)sysconf扩展,这比/proc(所有Linux系统都有/proc,但某些系统可能具有非glibc libc或较旧版本的glibc缺少_SC_NPROCESSORS_ONLN)更大的依赖性。

14
+1 这个发帖者似乎坚决要上吊,所以我给了他绳子。 - chrisaycock
14
/proc/cpuinfo 在哪些方面是可移植的?这是一个特定于 Linux 的接口(其他一些系统模拟它,例如 FreeBSD 通过在 /proc 中挂载 linprocfs 文件系统)。然而,像 sysconfig _SC_NPROCESSORS_ONLN 这样的接口也被 FreeBSD 支持。 - MarkR
5
它是可携带的,因为它不会阻止您的程序在没有它的系统上运行,并且在没有特殊含义的 /proc 的系统上,管理员可以将包含正确信息的简单文本文件存储在 /proc/cpuinfo 中。 - R.. GitHub STOP HELPING ICE
6
解析文件以获取底层信息是非常原始的做法(如果文件格式发生变化或在不同的实现中有所不同,则很难维护)。 - ebasconp
3
@cHao:这是不正确的。关注CPU数量(尤其是超线程核心数量)只有在它是大多数非平凡现代程序所需的基本信息时才是“原始的”(是的,现在已经不是1980年了)。虽然其他操作系统可能在通过编程获取此信息方面表现不佳(例如,在Windows下需要35行代码循环和多个内存分配),但Linux方法“解析文本文件”简直荒谬可笑。更令人困惑的是,他们甚至不能就是否使用/proc或/sys达成一致。 - Damon
显示剩余3条评论

7

涉及 sysconf(...)get_nprocs() 的答案都不正确,无法遵守 CPU 亲和性限制任务数的数量。

要获取可供任务使用的处理器数量,您需要像这样进行:

#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>

int nprocs()
{
  cpu_set_t cs;
  CPU_ZERO(&cs);
  sched_getaffinity(0, sizeof(cs), &cs);
  return CPU_COUNT(&cs);
}

int main()
{
  printf("procs=%d\n", nprocs());
  return 0;
}

如果进程已经调用cpu_setaffinity来限制亲和CPU数量,这仍然是正确的吗? - user1637056
对于测试,我有一个bash脚本while : ; do echo 0 > /sys/devices/system/cpu/cpu3/online && sleep 0.5 && echo 1 > /sys/devices/system/cpu/cpu3/online ; sleep 0.5 ; echo Hey! ; done;它快速开关cpu3。当将sysconf(_SC_NPROCESSORS_ONLN)放入while循环中时,无法显示正确的CPU,但如果将其放在watch -n 0.1 ./a.out(糟糕的选择)中,则可以正确显示核心计数。使用getconf也是一样,在watch中重新启动并显示正确信息。您的脚本也显示了正确的值。 - 15 Volts
但是需要注意的是,如果我使用 task -c 0 ./a.out 命令,它会给出 procs=1 而不是 4,换句话说,它只计算了分配给该进程的 CPU 数量。 - 15 Volts
1
@S.Goswami:这段代码的目的正是让您知道可以启动多少个线程以使进程与亲和掩码中的核心数最大化。这对于某些场景非常有用。 - Peter Cordes

6

针对最近的Intel CPU(不是一般的x86,而是只针对Intel),我使用 EAX=0Bh cpuid leaf。有关当前套接字/封装中核心的信息,请参见维基百科。在多套接字系统上,这可能是系统范围内物理/逻辑核心数量的一半或四分之一。Intel发布了一篇关于枚举CPU并提供有关多套接字系统的检查细节的白皮书。此代码不执行该操作,它只检查一个子叶(ECX=1)。

int main()
{
unsigned int eax=11,ebx=0,ecx=1,edx=0;

asm volatile("cpuid"
        : "=a" (eax),
          "=b" (ebx),
          "=c" (ecx),
          "=d" (edx)
        : "0" (eax), "2" (ecx)
        : );
            
printf("Cores: %d\nThreads: %d\nActual thread: %d\n",eax,ebx,edx);
}

输出:

Cores: 4
Threads: 8
Actual thread: 1

或者更简洁地说:
#include <stdio.h>

int main()
{
unsigned int ncores=0,nthreads=0,ht=0;

asm volatile("cpuid": "=a" (ncores), "=b" (nthreads) : "a" (0xb), "c" (0x1) : );

ht=(ncores!=nthreads);

printf("Cores: %d\nThreads: %d\nHyperThreading: %s\n",ncores,nthreads,ht?"Yes":"No");

return 0;
}

输出:

Cores: 4
Threads: 8
HyperThreading: Yes

此外,这仅适用于英特尔处理器 https://en.wikipedia.org/wiki/CPUID#EAX=4_and_EAX=Bh:_Intel_thread/core_and_cache_topology - Alexis Wilke
@AlexisWilke 我在我的回答的第一行就已经说了。你是错过了还是喜欢重复显而易见的事情呢? ;) - Zibri
在我的机器上运行此答案中发布的代码,我得到了4个核心,6个线程,超线程为YES。但是,如果安装并从我的cpp文件中调用库cpuinfo,则有6个核心和6个处理器。我不明白为什么会得到不同数量的核心。 - ChrisZZ
1
请注意,即使物理 CPU 有 4 个核心和 8 个线程,您当前的 cgroup 或任务亲和力可能仅将您的进程限制在 2 个核心/2 个线程。为什么需要知道核心或线程数? - Mikko Rantalainen
在虚拟机中,它可能仍然报告你正在运行的实际硬件的几何形状,而不是客户机中你的虚拟机运行的虚拟 CPU 的数量。这可能很有趣,但对于大多数目的并不是你想要的。(如果虚拟机希望为客户端提供适当的数字,则可以虚拟化CPUID结果,但我认为某些虚拟机可能不会这样做。) - Peter Cordes
显示剩余4条评论

2

在Linux(Ubuntu)上:

#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <sched.h>

int GetCPUCount()
{
 cpu_set_t cs;
 sched_getaffinity(0, sizeof(cs), &cs);
 return CPU_COUNT_S(sizeof(cs), &cs);
}

(假设当前进程具有默认的CPU亲和性。)

0

扫描/sys文件系统下的cpu*目录的另一种方法:

#include<stdio.h>
#include <dirent.h>
#include <errno.h>
#define LINUX_SYS_CPU_DIRECTORY "/sys/devices/system/cpu"

int main() {
   int cpu_count = 0;
   DIR *sys_cpu_dir = opendir(LINUX_SYS_CPU_DIRECTORY);
   if (sys_cpu_dir == NULL) {
       int err = errno;
       printf("Cannot open %s directory, error (%d).\n", LINUX_SYS_CPU_DIRECTORY, strerror(err));
       return -1;
   }
   const struct dirent *cpu_dir;
   while((cpu_dir = readdir(sys_cpu_dir)) != NULL) {
       if (fnmatch("cpu[0-9]*", cpu_dir->d_name, 0) != 0)
       {
          /* Skip the file which does not represent a CPU */
          continue;
       }
       cpu_count++;
   }
   printf("CPU count: %d\n", cpu_count);
   return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接