获取这两个参数,主要的目的是计算机器的理论浮点峰值。言归正传,问题有两个部分,一个是如何获取这两个值,另一个是怎么传递到C程序中。


1. 获取

(以下内容在非Redhat 系统上需要变通以下,不能用是正常的)

先看一个cpuinfo的例子:

$cat /proc/cpuinfo
processor  : 0
vendor_id  :GenuineIntel
cpu family  :6
model    :26
model name :Intel(R) Xeon(R) CPU           E5520  @ 2.27GHz
stepping    :5
cpu MHz    :1600.000
cache size  : 8192 KB
physical id  :0
siblings    :8
core id     : 0
cpu cores   :4
apicid         :0
fpu       :yes
fpu_exception :yes
cpuid level   : 11
wp      :yes
flags      : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm syscall nx rdtscp lm constant_tsc ida nonstop_tsc pni monitor ds_cpl vmx est tm2 cx16 xtpr popcnt lahf_lm
bogomips   :4522.12
clflush size  :64
cache_alignment  : 64
address sizes    : 40 bits physical, 48 bits virtual
power management :



这次我们比较关心的几个值:

processor - 逻辑处理器编号,对没有开超线程的多核机器,这个编号对应每个物理核;对开了超线程的机器,多个逻辑核对应一个物理核

core id - 所在CPU socket上的物理核编号,可能不连续,多个socket上的编号会重复

siblings - 单个CPU上的逻辑处理器数目

cpu cores - 所在CPU的物理核数目


要获取机器的物理核数,有些平台上简单的用core id 这行就可以(前提是超线程产生的两个核是连续列出的):

$cat /proc/cpuinfo | grep 'core id'| uniq | wc -l

复杂一些的情况,需要用 num_socket * cores_per_socket (前提是所有CPU上核数相同):

#!/bin/sh

sockets=$(grep -E 'physical id' /proc/cpuinfo | sort | uniq | wc -l)
cores_per_socket=$(grep -E 'cpu cores' /proc/cpuinfo | uniq | sed -e 's/.*\s\+\([0-9]\+\).*/\1/')
num_cores=$((${sockets}*${cores_per_socket}))
echo $num_cores



另一个问题,如何获取主频。cpuinfo上列出的 “cpu MHz” 明显不行,因为内核的新特性,这一行显示的值可能是cpu待机降频的频率。网上有说关闭cpuspeed服务的,且不说有没有root权限,这个办法也太凶残了。如果使用“E5520 @ 2.27GHz” 这个字段,也不科学,这个字串只是CPU系列的名字,后面的频率可能是一个名义值(不确定)。更好的办法是只用dmesg输出的信息:

$dmesg | grep -e 'Detected' | grep -e 'processor' 
Detected 2270.030 MHz processor.

不同系统输出稍有不同。如果要提取出数字:

$dmesg | grep -e 'Detected' | grep -e 'processor' | sed -e 's/.*\s\+\([.0-9]\+\)\s\+MHz.*/\1/'




2. 把shell命令得到的值传到C程序中

核心是在C程序中使用管道,C库的API是 popen / pclose。popen() 函数创建一个管道,调用 fork 产生一个子进程,执行一个 shell 命令,管道可以为输入或输出中的一种,创建之后的使用与文件流相同。具体用法自行man或者搜索……


于是C程序中获取物理核数目可以这么写(按上面较简单一些的情况):


char *cmd_get_num_cores = "cat /proc/cpuinfo | grep 'core id' | sort | uniq |wc -l"; 
FILE *pd = NULL;                       
assert( (pd = popen( cmd_get_num_cores, "r")) != NULL );
int nc = fscanf(pd, "%d", &num_cores);
assert( nc == 1 && num_cores > 0 );
pclose(pd);



获取主频的程序类似,要注意shell中的反斜杠不能变成C语言中的转义符“\”,所以是这样:

char *cmd_get_freq_MHz = "dmesg | grep -e 'Detected' | grep -e 'processor' | sed -e 's/.*\\s\\+\\([.0-9]\\+\\)\\s\\+MHz.*/\\1/'";



要避免C中转义符的麻烦,最好把命令写在一个shell 脚本中,C程序里调用 popen("sh script.sh", "r"); 这样就可以了。


补充:更换运行的平台后,脚本命令都需要检查一下正确性。需要更好的可移植性,可以尝试一些成熟的工具,如 hwloc 或者likwid