前言
嵌入式里面我们会使用到多核的cpu,随着产品芯片性能提升,我们也会有很多功能,以及很多进程产生运行,这个时候我们在任务调度调优的时候,把一些进程绑定到固定cpu运行,下面就来分享一下cpu绑定运行的过程:
首先运行的环境需要多核,大家可以按照下面命令进行查询对应设备的cpu数量
查看cpu有几个核
使用cat /proc/cpuinfo
查看cpu信息,如下两个信息:
processor,指明第几个cpu处理器
cpu cores,指明每个处理器的核心数
基本概念
cpu亲和性(affinity)
CPU的亲和性, 就是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,也称为CPU关联性;再简单的点的描述就将指定的进程或线程绑定到相应的cpu上;在多核运行的机器上,每个CPU本身自己会有缓存,缓存着进程使用的信息,而进程可能会被OS调度到其他CPU上,如此,CPU cache命中率就低了,当绑定CPU后,程序就会一直在指定的cpu跑,不会由操作系统调度到其他CPU上,性能有一定的提高。
- 软亲和性(affinity)
就是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,Linux 内核进程调度器天生就具有被称为 软 CPU 亲和性(affinity) 的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。
- 硬亲和性(affinity)
简单来说就是利用linux内核提供给用户的API,强行将进程或者线程绑定到某一个指定的cpu核运行。
代码绑定实现
上面我们使用cat /proc/cpuinfo
命令查询了自己设备的CPU,以我为例,我的电脑从0~7一共有8核。
下面是代码的demo:
#include <stdio.h>
#include <unistd.h>
#include <thread>
void thread_func1()
{
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(1, &mask); //指定该线程使用的CPU
if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) {
perror("pthread_setaffinity_np");
}
int count = 0;
while(1)
{
count ++;
sleep(1);
printf("fun 1 cnt :%d \n",count);
for(int i = 0; i < 8; i++) {
if (CPU_ISSET(i, &mask)) //查看cpu i 是否在get 集合当中
{
printf("1 this process %d of running processor: %d\n", getpid(), i);
}
}
}
}
void thread_func2()
{
int count = 0;
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(5, &mask);
if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) {
perror("pthread_setaffinity_np");
}
while(1)
{
usleep(1000*1000);
count ++;
printf("fun 2 cnt :%d \n",count);
for(int i = 0; i < 8; i++) {
if (CPU_ISSET(i, &mask)) //查看cpu i 是否在get 集合当中
{
printf("2 this process %d of running processor: %d\n", getpid(), i);
}
}
}
}
int main(int argc, char *argv[])
{
int cpus = 0;
cpus = sysconf(_SC_NPROCESSORS_CONF);
printf("cpus: %d\n", cpus); //查看cpu的个数;
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(7, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask) < 0) {
perror("sched_setaffinity");
}
std::thread t1(thread_func1);
std::thread t2(thread_func2);
usleep(1000); /* 让当前的设置有足够时间生效*/
while(1)
{
/*查看运行在当前进程的cpu*/
sleep(1); /* 让当前的设置有足够时间生效*/
printf("fun main \n");
for(int i = 0; i < cpus; i++) {
if (CPU_ISSET(i, &mask)) //查看cpu i 是否在get 集合当中
{
printf("3 this process %d of running processor: %d\n", getpid(), i);
}
}
}
t1.join();
t2.join();
}
使用此命令编译:
g++ test_select_cpu.cpp -pthread --std=c++11
上面一共运行了三个线程,一个是main 主线程,还有两个是自己定义的thread。
最重要的设置代码就是下面所示:设置cpu 亲和
cpu_set_t mask;
CPU_ZERO(&mask);/* 初始化set集,将set置为空*/
CPU_SET(5, &mask);/* 将对应的cpu序号加入到集合*/
if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) /*设置cpu 亲和性(affinity)*/
{
perror("pthread_setaffinity_np");
}
执行代码 ./a.out
代码绑定查看
使用ps -ef | grep a.out
命令查看对应的PID
使用 top命令查看对应pid的线程详细信息 top -p 54056
在进入top命令后,继续输入 f
使用上下
移动高亮到p
空格
选中
再按q
退出显示
输入大写H
就可以看到对应线程数据了
最右边就是对应的线程绑定的CPU序号。
作者:良知犹存,白天努力工作,晚上原创公号号主。公众号内容除了技术还有些人生感悟,一个认真输出内容的职场老司机,也是一个技术之外丰富生活的人,摄影、音乐 and 篮球。关注我,与我一起同行。
‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧
推荐阅读
【3】CPU中的程序是怎么运行起来的 必读
本公众号全部原创干货已整理成一个目录,回复[ 资源 ]即可获得。