一、linux c查看cpu核
1、命令行查看cpu有几个核
cat /proc/cpuinfo |grep processor | wc -l
或
nproc
2、linux c代码查看
#include <unistd.h>
int sysconf(_SC_NPROCESSORS_CONF);/* 返回系统可以使用的核数,但是其值会包括系统中禁用的核的数目,因此该值并不代表当前系统中可用的核数 */
int sysconf(_SC_NPROCESSORS_ONLN);/* 返回值真正的代表了系统当前可用的核数 */
/* 以下两个函数与上述类似 */
#include <sys/sysinfo.h>
int get_nprocs_conf (void);/* 可用核数,其值会包括系统中禁用的核的数目 */
int get_nprocs (void);/* 真正的反映了当前可用核数 */
二、设置任务绑定特定的CPU核
1、cpu_set_t ---- CPU集合
cpu_set_t用来描述CPU的集合,被sched_setaffinity等类似的函数使用。
1.1用法及例子
cpu_set_t set1, set2;
CPU_ZERO(&set1); //清空集合,即set1里不包含任何CPU,本质为所有bit清零
CPU_ZERO(&set2); //清空集合,即set2里不包含任何CPU,本质为所有bit清零
CPU_SET(0, &set1); //将cpu0添加到集合set1中,本质为对应bit置1
CPU_SET(1, &set2); //将cpu1添加到集合set2中,本质为对应bit置1
CPU_CLR(0, &set1); //将cpu0从集合set1中移除,本质为对应bit清零
int ret = CPU_ISSET(1, &set2); //判断cpu1是否在集合set2中,在返回非零,不在返回0
int cnt = CPU_COUNT(&set2); //返回集合set2中的CPU的个数
cpu_set_t result;
CPU_AND(&result, &set1, &set2); //set1和set2的所有bit按位与,结果存入result
CPU_OR(); //按位或
CPU_XOR(); //按位异或
ret = CPU_EQUAL(&set1, &set2); //集合set1和集合set2相等的话,ret为非零,不相等,ret为0
用法举例:
#define _GNU_SOURCE
#include <sched.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <assert.h>
int main(int argc, char *argv[])
{
cpu_set_t *cpusetp;
size_t size;
int num_cpus, cpu;
if (argc < 2) {
fprintf(stderr, "Usage: %s <num-cpus>\n", argv[0]);
exit(EXIT_FAILURE);
}
num_cpus = atoi(argv[1]);
/* 申请一个能够存放num_cpus个CPU的集合 */
cpusetp = CPU_ALLOC(num_cpus);
if (cpusetp == NULL) {
perror("CPU_ALLOC");
exit(EXIT_FAILURE);
}
/* 获取能存放num_cpus个CPU的集合的大小(单位字节) */
size = CPU_ALLOC_SIZE(num_cpus);
/* 清零cpusetp */
CPU_ZERO_S(size, cpusetp);
for (cpu = 0; cpu < num_cpus; cpu += 2)
CPU_SET_S(cpu, size, cpusetp); //将cpu0,cpu2,... 添加到cpusetp集合
printf("CPU_COUNT() of set: %d\n", CPU_COUNT_S(size, cpusetp));
/* 释放cpusetp空间 */
CPU_FREE(cpusetp);
exit(EXIT_SUCCESS);
}
2、绑定任务到指定CPU
CPU亲和性只是一种倾向性,当绑定的CPU不存在或者存在但是被禁用了,任务会在其他的CPU上执行
设置任务亲和性的接口有:
sched_setaffinity:修改指定pid_t的任务的亲和性
pthread_setaffinity_np:gnu接口,修改指定pthrad_t的任务的亲和性。
pthread_attr_setaffinity_np:gnu接口。创建线程前,通过线程属性结构体控制新线程的亲和性。
注:np的意思为不可移植,即在非gnu的系统上,应该是没有这个接口的。
2.1 sched_setaffinity
如果考虑可移植性的话,推荐使用sched_setaffinity()函数将任务绑定到特定CPU执行。
但是sched_setaffinity函数的不方便之处在于,无法给线程指定亲和性,要用sched_setaffinity给线程指定亲和性,比较麻烦,需要使用到不可移植的函数gettid()。
test.c:使用sched_setaffinity修改线程的CPU亲和性。主要逻辑为,主线程设置亲和性为cpu0,启动两个新线程th1和th2,此时从htop可以看到,主线程阻塞,th1和th2交替运行在cpu0上。然后过一段时间后,线程函数自己修改自己的亲和性(th1将自己的亲和性设置为cpu1,th2将自己的亲和性设置为cpu2),此时从htop可以看到,th1到cpu1上运行,th2到cpu2上运行。
test.c:
/* 该代码只是为了验证亲和性,故多数函数均没有检查返回值 */
#define _GNU_SOURCE
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/sysinfo.h>
#include <sched.h>
#include <string.h>
#include <sys/syscall.h> /* For SYS_xxx definitions */
void *func(void *arg)
{
/* 用来计时 */
int count = 0;
pthread_setname_np(pthread_self(), (char *)arg);
while(1)
{
char name[128] = {};
printf("%s\n", (char *)arg);
/* 死循环耗时,避免打印过快,不同的机器可能需要调整循环次数 */
for(int i=0; i<1000000000; i++);
count++;
/* count小于15时,线程都运行在cpu0上;等于15时,线程会设置自身的亲和性,导致后续时间,亲和性就不再是cpu0了 */
if(count == 15)
{
pid_t tid;
cpu_set_t set;
CPU_ZERO(&set);
/* 获取本线程的pid_t类型的ID号,因为sched_setaffinity的参数类型为pid_t */
tid = syscall(SYS_gettid);
if(!strncmp((char *)arg, "th1", strlen("th1")+1))
{
CPU_SET(1, &set);
sched_setaffinity(tid, sizeof(cpu_set_t), &set);
}
else
{
CPU_SET(2, &set);
sched_setaffinity(tid, sizeof(cpu_set_t), &set);
}
}
}
return NULL;
}
int main(void)
{
pthread_t th1, th2;
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(0, &set);
/* 设置主线程亲和性为cpu0,这样的话,默认新线程亲和性也是cpu0 */
int ret = sched_setaffinity(getpid(), sizeof(cpu_set_t), &set);
if (ret)
{
perror("sched_setaffinity");
return -1;
}
pthread_create(&th1, NULL, func, "th1");
pthread_create(&th2, NULL, func, "th2");
pthread_join(th1, NULL);
pthread_join(th2, NULL);
return 0;
}
2.2 pthread_setaffinity_np
如果要设置已存在的线程的亲和性,就可以考虑使用pthread_setaffinity_np,尽管pthread_setaffinity_np是不可移植的,是gnu独有的。因为就算是考虑到移植性,想要使用sched_setaffinity,也避免不了要使用gettid函数(该函数也是gnu独有)。与使用sched_setaffinity设置线程亲和性相比,使用pthread_setaffinity_np的另一个好处就是,可以在线程外设置任一线程的亲和性(只要知道pthread_t即可),而sched_setaffinity的gettid,需要在线程函数内调用。
test.c:使用pthread_setaffinity_np,在main函数中,对两个子线程设置其亲和性。前10s亲和性都追随主函数(cpu0),10s后使用pthread_setaffinity_np设置th1亲和性为cpu1,th2亲和性为cpu2,结果见htop截图。
/* 该代码只是为了验证亲和性,故多数函数均没有检查返回值 */
#define _GNU_SOURCE
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/sysinfo.h>
#include <sched.h>
#include <string.h>
#include <sys/syscall.h> /* For SYS_xxx definitions */
void *func(void *arg)
{
pthread_setname_np(pthread_self(), (char *)arg);
while(1)
{
char name[128] = {};
printf("%s\n", (char *)arg);
/* 死循环耗时,避免打印过快,不同的机器可能需要调整循环次数 */
for(int i=0; i<1000000000; i++);
}
return NULL;
}
int main(void)
{
pthread_t th1, th2;
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(0, &set);
/* 设置主线程亲和性为cpu0,这样的话,默认新线程亲和性也是cpu0 */
int ret = sched_setaffinity(getpid(), sizeof(cpu_set_t), &set);
if (ret)
{
perror("sched_setaffinity");
return -1;
}
pthread_create(&th1, NULL, func, "th1");
pthread_create(&th2, NULL, func, "th2");
/* 前10s,亲和性都是cpu0 */
sleep(10);
/* 10s过后,亲和性被修改 */
/* 移除CPU集合中的cpu0,此时集合中没有任何CPU */
CPU_CLR(0, &set);
/* 增加cpu1,此时集合中只有cpu1 */
CPU_SET(1, &set);
/* 设置th1的亲和性为cpu1 */
pthread_setaffinity_np(th1, sizeof(cpu_set_t), &set);
/* 移除集合中的cpu1,此时集合中没有任何CPU */
CPU_CLR(1, &set);
/* 增加cpu2,此时集合中只有cpu2 */
CPU_SET(2, &set);
/* 设置th2的亲和性为cpu2 */
pthread_setaffinity_np(th2, sizeof(cpu_set_t), &set);
pthread_join(th1, NULL);
pthread_join(th2, NULL);
return 0;
}
2.3 pthread_attr_setaffinity_np
创建新线程时,通过属性结构体,控制新线程的亲和性。
test.c:
/* 该代码只是为了验证亲和性,故多数函数均没有检查返回值 */
#define _GNU_SOURCE
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/sysinfo.h>
#include <sched.h>
#include <string.h>
#include <sys/syscall.h> /* For SYS_xxx definitions */
void *func(void *arg)
{
pthread_setname_np(pthread_self(), (char *)arg);
while(1)
{
char name[128] = {};
printf("%s\n", (char *)arg);
/* 死循环耗时,避免打印过快,不同的机器可能需要调整循环次数 */
for(int i=0; i<1000000000; i++);
}
return NULL;
}
int main(void)
{
pthread_t th1;
cpu_set_t set;
pthread_attr_t attr;
CPU_ZERO(&set);
CPU_SET(0, &set);
/* 设置主线程亲和性为cpu0,这样的话,默认新线程亲和性也应该是cpu0 */
/* 但是由于新线程创建的时候,使用属性指定了亲和性,就会发现新线程启动之后直接
* 就在指定的cpu运行了 */
int ret = sched_setaffinity(getpid(), sizeof(cpu_set_t), &set);
if (ret)
{
perror("sched_setaffinity");
return -1;
}
pthread_attr_init(&attr);
CPU_ZERO(&set);
CPU_SET(1, &set);
pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &set);
pthread_create(&th1, &attr, func, "th1");
pthread_join(th1, NULL);
return 0;
}
2.4主线程和线程分开设置亲和性
#define _GNU_SOURCE //代表使用GNU,pthread_setaffinity_np是不可移植的,是gnu独有的。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <pthread.h>
#include <sched.h>
static int g_processCpuNum = 0; // 进程占用的CPU核
/** 线程读取数据 */
void *read_data(void *args)
{
// 配置线程绑定的CPU核
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(g_processCpuNum + 1, &mask); // 线程和进程用不同的CPU核
if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0)
{
return;
}
// 线程循环
while(1)
{
..............
}
}
// 获取调度策略
int get_thread_policy(pthread_attr_t *attr)
{
int policy;
int rs = pthread_attr_getschedpolicy(attr, &policy);
switch (policy)
{
case SCHED_FIFO:
printf("policy= SCHED_FIFO\n");
break;
case SCHED_RR:
printf("policy= SCHED_RR");
break;
case SCHED_OTHER:
printf("policy=SCHED_OTHER\n");
break;
default:
printf("policy=UNKNOWN\n");
break;
}
return policy;
}
/** 主函数 */
int main( int argc, char **argv )
{
int interruptCpuNum = 2; //中断绑定CPU核心号
int interrruptNum = 6; //中断号
// 绑定中断CPU核,暂时采用固定中断号的形式,也可以通过系统获取中断号
if(interruptCpuNum && interrruptNum)
{
sprintf(sysCmd, "echo %d > /proc/irq/%d/smp_affinity", 1 << interruptCpuNum, interrruptNum);//通过命令形式绑定
system(sysCmd);
}
// 绑定进程的CPU核
if(!g_processCpuNum)
{
CPU_ZERO(&set); //清空CPU集合
CPU_SET(g_processCpuNum, &set); //向集合中添加CPU核,这个cpu核指参数输入的整数,比如输入了2,代表了cpu2
if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) /* 设置主线程的亲和性,子线程会继承父线程的亲和性设置(见man pthread_create) */
{
printf("parent sched_setaffinity error\n");
return -1;
}
}
// 创建线程读取数据,并分配线程优先级
pthread_t pid; //线程标识
pthread_attr_t attr; //线程属性
struct sched_param param;
pthread_attr_init(&attr);
#if 1
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); // 线程不继承父线程优先级,即线程也不继承进程的亲和性
pthread_attr_setschedpolicy(&attr, SCHED_FIFO); // 设置调度策略为SCHED_FIFO
param.sched_priority = sched_get_priority_max(get_thread_policy(&attr)); // 配置优先级最高
pthread_attr_setschedparam(&attr, ¶m);
#endif
ret = pthread_create(&pid, &attr, read_data, (void*)&handle);
if(ret < 0)
{
printf("pthread create failed!\n");
return -1;
}
// 主循环
while(run)
{
.........
}
}