前言

现在计算机上的CPU大多都是多核的,有4核甚至是8核的。但是一个计算机启动之后其进程数是远远多于CPU核数的,因为操作系统会给自动调度这些进程在CPU核上轮流运行。但是对于应用程序或者进程,其性能要求较高时,可能有必要绑定该进程到指定的CPU核来运行,避免调度带来的额外开销。我自己也是因为最近的项目上有需要进程运行在指定的CPU核上的要求,所以了解了一下这项技术,并且将过程和总结记录于此。

CPU亲和性

在学习这项新技术之前,我们先来了解一下什么是CPU亲和性?所谓亲和性,就是把进程在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,也称为CPU关联性;再简单的点的描述就将制定的进程或线程绑定到相应的cpu上;在多核运行的机器上,每个CPU本身自己会有缓存,缓存着进程使用的信息,而进程可能会被OS调度到其他CPU上,如此,CPU cache命中率就低了,当绑定CPU后,程序就会一直在指定的cpu跑,不会由操作系统调度到其他CPU上,性能有一定的提高。

预备知识

在编写测试程序之前,我们先来了解一下CPU相关的宏和函数。

1.首先要想使用CPU系列函数及相关的宏,需要声明下面的宏,以告诉编译器启用这些函数

#define _GNU_SOURCE

2.声明一个cpu_set_t,然后用 CPU_ZERO()宏来初始化数据:

cpu_set_t mask;

CPU_ZERO(&mask);

3.再下来就是要指定绑定的CPU核心,用CPU_SET()宏来指定,例如:

CPU_SET(1, &mask);//绑定CPU核心1

4.下面这个函数是进程绑定CPU核心最关键函数,也是实际绑定CPU核的操作。其原型即参数说明如下:

int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);

该函数设置进程为pid的这个进程,让它运行在mask所设定的CPU上.如果pid的值为0,则表示指定的是当前进程,使当前进程运行在mask所设定的那些CPU上.第二个参数cpusetsize是mask所指定的数的长度.通常设定为sizeof(cpu_set_t).如果当前pid所指定的进程此时没有运行在mask所指定的任意一个CPU上,则该指定的进程会从其它CPU上迁移到mask的指定的一个CPU上运行。

用法:

sched_setaffinity(0, sizeof(cpu_set_t), &mask);//设置完mask之后,通过该函数完成实际绑定功能

5.补充:通过上面的4步就可以完成进程绑定CPU核心了,即被绑定的进程会一直运行在指定的CPU核上,不会被操作系统调度走。在这里,我们再介绍一下另外两个宏:

void CPU_CLR (int cpu, cpu_set_t *set)

这个宏将 指定的 cpu 从 CPU 集 set 中删除。

int CPU_ISSET (int cpu, const cpu_set_t *set)

检查 cpu 是否是 CPU 集 set 的一员,如果在CPU集中这个宏就返回一个非零值(true),否则就返回零(false)。

还有一个函数其功能是获取当前进程运行在哪一个CPU核:

int sched_getcpu();

测试程序

接下来,我们就用一个测试程序来看一下进程是否被绑定在一个CPU核上。

#define _GNU_SOURCE

#include

#include

#include

#include

int main()

{

cpu_set_t mask;

CPU_ZERO(&mask);

CPU_SET(1, &mask);//把main进程绑定到CPU1

sched_setaffinity(0, sizeof(cpu_set_t), &mask);//实际的绑定函数

printf("cpu = %d\n",sched_getcpu());//打印当前运行的CPU核,看是否绑定成功

int i,j,k;

for(i = 0;i < 40;i++)

{//循环40亿次

printf("i = %d\n",i);

for(j = 0;j < 10000;j++)

{

for(k = 0;k < 10000;k++)

{

if(sched_getcpu() != 1)//如果当前CPU核不是1,说明被操作系统给调度走了,绑定失败!并且打印当前运行的CPU核

{

printf("main process: cpu = %d\n",sched_getcpu());

}

}

}

}

printf("main end\n");

return 0;

}

编译测试程序:g++ cpu.cpp -o cpu

运行程序:./cpu

结果如下:

可以看到,打印的CPU核正是我们绑定的CPU,并且没有打印程序中测试绑定失败的相关log,说明循环了40亿次,main进程一直都在CPU1上运行的。

进阶测试

上面的测试,我们只是做到了被绑定的进程确实运行在了指定的CPU上,但是期间是否有其他进程被操作系统调度进来运行呢?被绑定的进程是否独享指定的CPU呢?现在我们就通过测试程序来测试看看。这里我们还是需要上面刚写的测试程序来辅助测试,这里暂且称上面的程序为测试程序1吧。下面我们要写第二个测试程序,其功能就是fork5个子进程,并且对这5个子进程不做绑定,任由操作系统默认调度,测试程序2如下:

#define _GNU_SOURCE

#include

#include

#include

#include

void run(int c, int n,int cpu) {

int lastcpu = cpu;

// if(c == 0)

// {

// cpu_set_t mask;

// CPU_ZERO(&mask);

// CPU_SET(n, &mask);

// sched_setaffinity(0, sizeof(cpu_set_t), &mask);

// printf("c= %d,cpu = %d\n",c,sched_getcpu());

// }

int i,j,k;

for (i = 0; i != 60; i++) //循环60亿次

{

printf("c = %d,i = %d\n",c,i);

for(j = 0; j != 10000; j++)

{

for(k = 0;k != 10000;k++)

{

if(sched_getcpu() != lastcpu)//如果当前的CPU核和上次的CPU核不一样,说明该进程被调度了,这样只要进程发生了调度我们就可以知道该进程是从CPU的哪个核调度到哪个核

{

printf("c = %d,i = %d,lastcpu = %d, nowcpu = %d\n",c,i,lastcpu,sched_getcpu());

lastcpu = sched_getcpu(); //更新lastcpu的值

}

}

}

}

printf("c = %d ,i = %d, j = %d,is end\n",c,i,j);

}

int main()

{

int i,initcpu;

for (i = 0; i != 5; i++) {//循环5次,产生了5个子进程

int pid = fork();

if (pid == 0) {//fork返回值为0表明是子进程,if判断里面只有子进程会跑

initcpu = sched_getcpu();//获取各个进程被分配到哪个CPU核

printf("i = %d,initcpu = %d\n",i,initcpu);

run(i, i,initcpu);//每一个子进程都会跑run函数

exit(0);

}

}

return 0;

}

好了,现在我们有一个绑定了CPU1的测试程序1,以及任由操作系统调度的测试程序2.

测试方法就是先执行测试程序1,目的是让测试程序1绑定CPU1,然后再运行测试程序2,由于测试程序2中的子进程都是操作系统默认调度的,所以可以看看测试程序1还在运行期间,测试程序2中的进程是否会被调度到CPU1上。

执行结果:

由于运行结果log较多,这里不一一上传,但是我们从上面的结果中可以看到,在测试程序1执行期间,测试程序2中的这些被系统自动调度的进程并没有一个进程被调度到CPU1上,直到程序1执行完了之后测试程序2的进程才被调度到CPU1上。

线程绑定CPU

线程绑定CPU的函数如下:

int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset);

绑定线程与进程的过程和方法都类似,这里就不详细介绍了,读者可以在了解了进程绑定之后再亲自尝试一下线程绑定。

总结

经过上面测试程序的测试我们可以得出以下结论:

1.经过sched_setaffinity函数绑定了CPU之后,进程在其运行期间是独享该核心的,其他被操作系统自动调度的进程不会在该CPU核心上运行。

2.只有 在被绑定的进程运行结束之后,该核心就会被释放,供操作系统自动调度。

linux指定cpu运行程序,进程/线程绑定到特定CPU核的linux实现(有代码有实例)相关推荐

  1. 为什么要把进程/线程绑定到特定cpu核上运行?(cpu core id coreIdx)opdevsdk_sys_bindThreadCoreId()

    看海康hikflow_demo代码,在线程处理函数里调用了绑定函数,把这个线程绑定到某个cpu核上,不知为何要这么做? 原因 答1 现在大家使用的基本上都是多核cpu,一般是4核的.平时应用程序在运行 ...

  2. linux下把进程绑定到特定cpu核上运行

    现在大家使用的基本上都是多核cpu,一般是4核的.平时应用程序在运行时都是由操作系统管理的.操作系统对应用进程进行调度,使其在不同的核上轮番运行. 对于普通的应用,操作系统的默认调度机制是没有问题的. ...

  3. linux上根据运行程序的进程号,查看程序所在的绝对路径。linux查看进程启动的时间

    1 linux上根据运行程序的进程号,查看程序所在的绝对路径 1.如下,我想查看python 25_run_train_tripletloss_sknet_8w_offline_c_plus.py 这 ...

  4. CPU核心数线程数、程序进程线程、并发并行的简单理解

    CPU核心数线程数.程序进程线程.并发并行.简单理解和区分 这篇文章是对上述感念的简单理解,想深入研究可以看看<计算机组成原理> CPU的核心数 线程数 当我们买电脑的时候,会看到CPU的 ...

  5. linux的进程/线程/协程系列3:查看linux内核源码——vim+ctags/find+grep

    linux的进程/线程/协程系列3:查看linux内核源码--vim+ctags/find+grep 前言 摘要: 1. 下载linux内核源码 2. 打标签方法:vim+ctags 2.1 安装vi ...

  6. linux c进程线程的面试问题,linux 多线程面试题_linux进程线程_linux 线程 pthread_create...

    你写了一个简单的mandelbrot set程序,也就是说用ps命令行是可以看见多个线程,win32里同一个进程里各个线程之间是共享数据段的,win32的进程管理方式与unix上有着很大区别.adob ...

  7. 在Red Hat Linux中自动运行程序

    在Red Hat Linux中自动运行程序 1.开机启动时自动运行程序 Linux加载后, 它将初始化硬件和设备驱动, 然后运行第一个进程init.init根据配置文件继续引导过程,启动其它进程.通常 ...

  8. 02 线程简介 多任务 多线程 普通方法调用和多线程 程序.进程.线程 Proces与Thread 核心概念

    线程简介 任务,进程,线程,多线程 多任务 多任务处理是指用户可以在同一时间内运行多个应用程序,每个应用程序被称作一个任务 多线程 原来是一条路,慢慢因为车太多了,道路堵塞,效率极低. 为了提高使用的 ...

  9. Linux命令--定时运行程序(脚本)--方法/实例

    原文网址:Linux命令--定时运行程序(脚本)--方法/实例_IT利刃出鞘的博客-CSDN博客 简介 说明         本文介绍Linux如何定时运行程序. 概述 at命令是一次性定时计划任务, ...

最新文章

  1. 曾经我也迷茫,你还在迷茫吗?写给像我一样的在校计算机专业学生作者:Cat_Lee 来源:博客园 发布时间:2009-05-30 20:25 阅读:1104 次 原文链接 [收藏]
  2. MySQL5.6 选项和变量整理
  3. Linux设置Oracle开机自启动
  4. 社区论坛小程序带订阅功能
  5. java生成随机十位数_随机10位字符串生成
  6. ajax实战用法详解
  7. linux安装界面意思,为linux安装图形化界面
  8. L1-041 寻找250 (10 分)—团体程序设计天梯赛
  9. (六)Value Function Approximation-LSPI code (3)
  10. 16位图xxxxxxxxxxxx
  11. 【游戏开发实战】Unity实现类似GitHub地球射线的效果(LineRenderer | 贝塞尔曲线)
  12. VC++即时通+视频会议源码
  13. 运营小技能:订阅号文章排版教程(添加图片超链接、推文采集、往期推荐)
  14. 论坛刷访客神器-Header自定义工具
  15. 用 GNS3 做CCNA网络实验(4)
  16. 异步和同步数据备份的差别_备份和同步数据的最佳文章
  17. Keil5使用AC6编译器
  18. 老闪创业那些事儿(14)——测试老白变身记
  19. 从数字城市迈向智能城市
  20. 遥感图像处理:最小噪声分离变换(Minimum Noise Fraction Rotation,MNF Rotation)

热门文章

  1. mysql workbench 存储过程_MySQL Workbench创建存储过程教程示例
  2. docker mysql5.7 主从_使用Docker部署MySQL 5.78.0主从集群的方法步骤
  3. r 64位连接mysql_R与Mysql数据库连接的两种方法
  4. 常见的通配符_技术干货 | 常见的mysql注入语句
  5. python读大文件方法_使用Python读取大文件的方法
  6. java 获取组件大小_java - 如何初始化取决于组件大小的图像抓取? - 堆栈内存溢出...
  7. mysql 删除表时外键约束_MySQL删除表的时候忽略外键约束的简单实现
  8. 在chrome里查询修改html代码,我需要在网页中直接编辑CSS?打开Web代码检查器
  9. linux中用户 机器名,Python 在linux下获得当前工作目录,主机名,用户名,操作系统平台等信息...
  10. retrofit框架学习(一)----基本用法