Linux中CPU亲和性

2024-01-09 14:12
文章标签 linux cpu 亲和性

本文主要是介绍Linux中CPU亲和性,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Linux中CPU亲和性

超线程技术(Hyper-Threading):就是利用特殊的硬件指令,把两个逻辑内核(CPU core)模拟成两个物理芯片,让单个处理器都能使用线程级并行计算,进而兼容多线程操作系统和软件,减少了CPU的闲置时间,提高的CPU的运行效率。我们常听到的双核四线程/四核八线程指的就是支持超线程技术的CPU

物理CPU:机器上安装的实际CPU, 比如说你的主板上安装了一个8核CPU,那么物理CPU个数就是1个,所以物理CPU个数就是主板上安装的CPU个数

逻辑CPU:一般情况,我们认为一颗CPU可以有多核,加上intel的超线程技术(HT), 可以在逻辑上再分一倍数量的CPU core出来

绑定任务到特定CPU(CPU亲和性)

Linux下查看CPU相关信息, CPU的信息主要都在/proc/cupinfo中

# 查看物理CPU个数
cat /proc/cpuinfo|grep "physical id"|sort -u|wc -l# 查看每个物理CPU中core的个数(即核数)
cat /proc/cpuinfo|grep "cpu cores"|uniq# 查看逻辑CPU的个数
cat /proc/cpuinfo|grep "processor"|wc -l# 查看CPU的名称型号
cat /proc/cpuinfo|grep "name"|cut -f2 -d:|uniq

Linux查看某个进程运行在哪个逻辑CPU上

ps -eo pid,args,psr
#参数的含义:
pid  - 进程ID
args - 该进程执行时传入的命令行参数
psr  - 分配给进程的逻辑CPU例子:
[~]# ps -eo pid,args,psr | grep nginx
9073 nginx: master process /usr/   1
9074 nginx: worker process         0
9075 nginx: worker process         1
9076 nginx: worker process         2
9077 nginx: worker process         3
13857 grep nginx                   3

Linux查看线程的TID

TID就是Thread ID,他和POSIX中pthread_t表示的线程ID完全不是同一个东西.

Linux中的POSIX线程库实现的线程其实也是一个轻量级进程(LWP),这个TID就是这个线程的真实PID.

但是又不能通过getpid()函数获取,Linux中定义了gettid()这个接口,但是通常都是未实现的,所以需要使用下面的方式获取TID。

//program
#include <sys/syscall.h>  
pid_t tid;
tid = syscall(__NR_gettid);// or syscall(SYS_gettid)  //command-line 3种方法(推荐第三种方法)
(1)ps -efL | grep prog_name
(2)ls /proc/pid/task            //文件夹名即TID
(3)ps -To 'pid,lwp,psr,cmd' -p PID

基本概念

CPU affinity 是一种调度属性(scheduler property), 它可以将一个进程"绑定" 到一个或一组CPU上.

在SMP(Symmetric Multi-Processing对称多处理)架构下,Linux调度器(scheduler)会根据CPU affinity的设置让指定的进程运行在"绑定"的CPU上,而不会在别的CPU上运行.

Linux调度器同样支持自然CPU亲和性(natural CPU affinity): 调度器会试图保持进程在相同的CPU上运行, 这意味着进程通常不会在处理器之间频繁迁移,进程迁移的频率小就意味着产生的负载小。

因为程序的作者比调度器更了解程序,所以我们可以手动地为其分配CPU核,而不会过多地占用CPU0,或是让我们关键进程和一堆别的进程挤在一起,所有设置CPU亲和性可以使某些程序提高性能。

表示方法

CPU affinity 使用位掩码(bitmask)表示, 每一位都表示一个CPU, 置1表示"绑定".

最低位表示第一个逻辑CPU, 最高位表示最后一个逻辑CPU.

CPU affinity典型的表示方法是使用16进制,具体如下.

0x00000001is processor #00x00000003is processors #0 and #10xFFFFFFFFis all processors (#0 through #31)

taskset命令

taskset命名用于获取或者设定CPU亲和性

# 命令行形式
taskset [options] mask command [arg]...
taskset [options] -p [mask] pidPARAMETERmask : cpu亲和性,当没有-c选项时, 其值前无论有没有0x标记都是16进制的,当有-c选项时,其值是十进制的.command : 命令或者可执行程序arg : command的参数pid : 进程ID,可以通过ps/top/pidof等命令获取OPTIONS-a, --all-tasks (旧版本中没有这个选项)这个选项涉及到了linux中TID的概念,他会将一个进程中所有的TID都执行一次CPU亲和性设置.TID就是Thread ID,他和POSIX中pthread_t表示的线程ID完全不是同一个东西.Linux中的POSIX线程库实现的线程其实也是一个进程(LWP),这个TID就是这个线程的真实PID.-p, --pid操作已存在的PID,而不是加载一个新的程序-c, --cpu-list声明CPU的亲和力使用数字表示而不是用位掩码表示. 例如 0,5,7,9-11.-h, --helpdisplay usage information and exit-V, --versionoutput version information and exitUSAGE1) 使用指定的CPU亲和性运行一个新程序taskset [-c] mask command [arg]...举例:使用CPU0运行ls命令显示/etc/init.d下的所有内容 taskset -c 0 ls -al /etc/init.d/2) 显示已经运行的进程的CPU亲和性taskset -p pid举例:查看init进程(PID=1)的CPU亲和性taskset -p 13) 改变已经运行进程的CPU亲和力taskset -p[c] mask pid举例:打开2个终端,在第一个终端运行top命令,第二个终端中首先运行:[~]# ps -eo pid,args,psr | grep top #获取top命令的pid和其所运行的CPU号其次运行:[~]# taskset -cp 新的CPU号 pid       #更改top命令运行的CPU号最后运行:[~]# ps -eo pid,args,psr | grep top #查看是否更改成功PERMISSIONS一个用户要设定一个进程的CPU亲和性,如果目标进程是该用户的,则可以设置,如果是其他用户的,则会设置失败,提示 Operation not permitted.当然root用户没有任何限制.任何用户都可以获取任意一个进程的CPU亲和性.

编程API

下面是用用于设置和获取CPU亲和性相关的API.

#define _GNU_SOURCE
#include <sched.h>
#include <pthread.h> //for pthread functions(last 4) 注意<pthread.h>包含<sched.h>/* MACRO *//* The following macros are provided to operate on the CPU set set *//* Clears set, so that it contains no CPUs */void CPU_ZERO(cpu_set_t *set);void CPU_ZERO_S(size_t setsize, cpu_set_t *set);/* Add CPU cpu to set */void CPU_SET(int cpu, cpu_set_t *set);void CPU_SET_S(int cpu, size_t setsize, cpu_set_t *set);/* Remove CPU cpu from set */void CPU_CLR(int cpu, cpu_set_t *set);void CPU_CLR_S(int cpu, size_t setsize, cpu_set_t *set);/* Test to see if CPU cpu is a member of set */int CPU_ISSET(int cpu, cpu_set_t *set);int CPU_ISSET_S(int cpu, size_t setsize, cpu_set_t *set);/* Return the number of CPUs in set */void CPU_COUNT(cpu_set_t *set);void CPU_COUNT_S(size_t setsize, cpu_set_t *set);/* The following macros perform logical operations on CPU sets *//* Store the logical AND of the sets srcset1 and srcset2 in destset (which may be one of the source sets). */void CPU_AND(cpu_set_t *destset, cpu_set_t *srcset1, cpu_set_t *srcset2);void CPU_AND_S(size_t setsize, cpu_set_t *destset, cpu_set_t *srcset1, cpu_set_t *srcset2);/* Store the logical OR of the sets srcset1 and srcset2 in destset (which may be one of the source sets). */void CPU_OR(cpu_set_t *destset, cpu_set_t *srcset1, cpu_set_t *srcset2);void CPU_OR_S(size_t setsize, cpu_set_t *destset, cpu_set_t *srcset1, cpu_set_t *srcset2);/* Store  the logical XOR of the sets srcset1 and srcset2 in destset (which may be one of the source sets). */void CPU_XOR(cpu_set_t *destset, cpu_set_t *srcset1, cpu_set_t *srcset2);void CPU_XOR_S(size_t setsize, cpu_set_t *destset, cpu_set_t *srcset1, cpu_set_t *srcset2);/* Test whether two CPU set contain exactly the same CPUs. */int CPU_EQUAL(cpu_set_t *set1, cpu_set_t *set2);int CPU_EQUAL_S(size_t setsize, cpu_set_t *set1, cpu_set_t *set2);/* The following macros are used to allocate and deallocate CPU sets: *//* Allocate a CPU set large enough to hold CPUs in the range 0 to num_cpus-1 */cpu_set_t *CPU_ALLOC(int num_cpus);/* Return the size in bytes of the CPU set that would be needed to  hold  CPUs  in the  range 0 to num_cpus-1. This macro provides the value that can be used for the setsize argument in the CPU_*_S() macros */size_t CPU_ALLOC_SIZE(int num_cpus);/* Free a CPU set previously allocated by CPU_ALLOC(). */void CPU_FREE(cpu_set_t *set);/* API *//* Set the CPU affinity for a task */int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);/* Get the CPU affinity for a task */int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);/* set CPU affinity attribute in thread attributes object */int pthread_attr_setaffinity_np(pthread_attr_t *attr, size_t cpusetsize, const cpu_set_t *cpuset);/* get CPU affinity attribute in thread attributes object */int pthread_attr_getaffinity_np(const pthread_attr_t *attr, size_t cpusetsize, cpu_set_t *cpuset);/* set CPU affinity of a thread */int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, const cpu_set_t *cpuset);/* get CPU affinity of a thread */int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize, cpu_set_t *cpuset);

相关的宏通常都分为2种,一种是带_S后缀的,一种不是不带_S后缀的, 从声明上看带_S后缀的宏都多出一个参数 setsize.

从功能上看他们的区别是带_S后缀的宏是用于操作动态申请的CPU set(s),所谓的动态申请其实就是使用宏 CPU_ALLOC 申请,

参数setsize 可以是通过宏 CPU_ALLOC_SIZE 获得,两者的用法详见下面的例子.

相关的API只有6个, 前2个是用来设置进程的CPU亲和性,需要注意的一点是,当这2个API的第一个参数pid为0时,表示使用调用进程的进程ID;

后4个是用来设置线程的CPU亲和性。其实sched_setaffinity()也可以用来设置线程的CPU的亲和性,也就是taskset “-a”选项中提到的TID概念。

例子一:使用2种方式(带和不带_S后缀的宏)获取当前进程的CPU亲和性

#define _GNU_SOURCE
#include <sched.h>
#include <unistd.h> /* sysconf */
#include <stdlib.h> /* exit */
#include <stdio.h>int main(void)
{int i, nrcpus;cpu_set_t mask;unsigned long bitmask = 0;CPU_ZERO(&mask);/* Get the CPU affinity for a pid */if (sched_getaffinity(0, sizeof(cpu_set_t), &mask) == -1) {   perror("sched_getaffinity");exit(EXIT_FAILURE);}/* get logical cpu number */nrcpus = sysconf(_SC_NPROCESSORS_CONF);for (i = 0; i < nrcpus; i++){if (CPU_ISSET(i, &mask)){bitmask |= (unsigned long)0x01 << i;printf("processor #%d is set\n", i); }}printf("bitmask = %#lx\n", bitmask);exit(EXIT_SUCCESS);
}
/*----------------------------------------------------------------*/
#define _GNU_SOURCE
#include <sched.h>
#include <unistd.h> /* sysconf */
#include <stdlib.h> /* exit */
#include <stdio.h>int main(void)
{int i, nrcpus;cpu_set_t *pmask;size_t cpusize;unsigned long bitmask = 0;/* get logical cpu number */nrcpus = sysconf(_SC_NPROCESSORS_CONF);pmask = CPU_ALLOC(nrcpus);cpusize = CPU_ALLOC_SIZE(nrcpus);CPU_ZERO_S(cpusize, pmask);/* Get the CPU affinity for a pid */if (sched_getaffinity(0, cpusize, pmask) == -1) {perror("sched_getaffinity");CPU_FREE(pmask);exit(EXIT_FAILURE);}for (i = 0; i < nrcpus; i++){if (CPU_ISSET_S(i, cpusize, pmask)){bitmask |= (unsigned long)0x01 << i;printf("processor #%d is set\n", i); }}printf("bitmask = %#lx\n", bitmask);CPU_FREE(pmask);exit(EXIT_SUCCESS);
}

执行结果如下(4核CPU):

[cpu_affinity #1]$ gcc -g -Wall cpu_affinity.c
[cpu_affinity #2]$ taskset 1 ./a.out 
processor #0 is set
bitmask = 0x1
[cpu_affinity #3]$ taskset 1 ./a.out 
processor #0 is set
bitmask = 0x1
[cpu_affinity #4]$ taskset 2 ./a.out 
processor #1 is set
bitmask = 0x2
[cpu_affinity #5]$ taskset 3 ./a.out 
processor #0 is set
processor #1 is set
bitmask = 0x3
[cpu_affinity #6]$ taskset 4 ./a.out 
processor #2 is set
bitmask = 0x4
[cpu_affinity #7]$ taskset 5 ./a.out 
processor #0 is set
processor #2 is set
bitmask = 0x5
[cpu_affinity #8]$ taskset 6 ./a.out 
processor #1 is set
processor #2 is set
bitmask = 0x6
[cpu_affinity #9]$ taskset 7 ./a.out 
processor #0 is set
processor #1 is set
processor #2 is set
bitmask = 0x7
[cpu_affinity #10]$ taskset 8 ./a.out 
processor #3 is set
bitmask = 0x8
[cpu_affinity #11]$ taskset 9 ./a.out 
processor #0 is set
processor #3 is set
bitmask = 0x9
[cpu_affinity #12]$ taskset A ./a.out 
processor #1 is set
processor #3 is set
bitmask = 0xa
[cpu_affinity #13]$ taskset B ./a.out 
processor #0 is set
processor #1 is set
processor #3 is set
bitmask = 0xb
[cpu_affinity #14]$ taskset C ./a.out 
processor #2 is set
processor #3 is set
bitmask = 0xc
[cpu_affinity #15]$ taskset D ./a.out 
processor #0 is set
processor #2 is set
processor #3 is set
bitmask = 0xd
[cpu_affinity #16]$ taskset E ./a.out 
processor #1 is set
processor #2 is set
processor #3 is set
bitmask = 0xe
[cpu_affinity #17]$ taskset F ./a.out 
processor #0 is set
processor #1 is set
processor #2 is set
processor #3 is set
bitmask = 0xf
[cpu_affinity #18]$ taskset 0 ./a.out 
sched_setaffinity: Invalid argument
failed to set pid 0's affinity.

例子二:设置进程的CPU亲和性后再获取显示CPU亲和性

#define _GNU_SOURCE
#include <sched.h>
#include <unistd.h> /* sysconf */
#include <stdlib.h> /* exit */
#include <stdio.h>int main(void)
{int i, nrcpus;cpu_set_t mask;unsigned long bitmask = 0;CPU_ZERO(&mask);CPU_SET(0, &mask); /* add CPU0 to cpu set */CPU_SET(2, &mask); /* add CPU2 to cpu set *//* Set the CPU affinity for a pid */if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) == -1) {   perror("sched_setaffinity");exit(EXIT_FAILURE);}CPU_ZERO(&mask);/* Get the CPU affinity for a pid */if (sched_getaffinity(0, sizeof(cpu_set_t), &mask) == -1) {   perror("sched_getaffinity");exit(EXIT_FAILURE);}/* get logical cpu number */nrcpus = sysconf(_SC_NPROCESSORS_CONF);for (i = 0; i < nrcpus; i++){if (CPU_ISSET(i, &mask)){bitmask |= (unsigned long)0x01 << i;printf("processor #%d is set\n", i); }}printf("bitmask = %#lx\n", bitmask);exit(EXIT_SUCCESS);
}

例子三:设置线程的CPU属性后再获取显示CPU亲和性

这个例子来源于Linux的man page.

#define _GNU_SOURCE
#include <pthread.h> //不用再包含<sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>#define handle_error_en(en, msg) \do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)int
main(int argc, char *argv[])
{int s, j;cpu_set_t cpuset;pthread_t thread;thread = pthread_self();/* Set affinity mask to include CPUs 0 to 7 */CPU_ZERO(&cpuset);for (j = 0; j < 8; j++)CPU_SET(j, &cpuset);s = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);if (s != 0){handle_error_en(s, "pthread_setaffinity_np");}/* Check the actual affinity mask assigned to the thread */s = pthread_getaffinity_np(thread, sizeof(cpu_set_t), &cpuset);if (s != 0){handle_error_en(s, "pthread_getaffinity_np");}printf("Set returned by pthread_getaffinity_np() contained:\n");for (j = 0; j < CPU_SETSIZE; j++) //CPU_SETSIZE 是定义在<sched.h>中的宏,通常是1024{if (CPU_ISSET(j, &cpuset)){printf("    CPU %d\n", j);}}exit(EXIT_SUCCESS);
}

例子四:使用seched_setaffinity设置线程的CPU亲和性

#define _GNU_SOURCE
#include <sched.h>
#include <stdlib.h>
#include <sys/syscall.h> // syscallint main(void)
{pid_t tid;int i, nrcpus;cpu_set_t mask;unsigned long bitmask = 0;CPU_ZERO(&mask);CPU_SET(0, &mask); /* add CPU0 to cpu set */CPU_SET(2, &mask); /* add CPU2 to cpu set */// get tid(线程的PID,线程是轻量级进程,所以其本质是一个进程)tid = syscall(__NR_gettid); // or syscall(SYS_gettid);/* Set the CPU affinity for a pid */if (sched_setaffinity(tid, sizeof(cpu_set_t), &mask) == -1) {perror("sched_setaffinity");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);
}

这篇关于Linux中CPU亲和性的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/587400

相关文章

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

Linux服务器Java启动脚本

Linux服务器Java启动脚本 1、初版2、优化版本3、常用脚本仓库 本文章介绍了如何在Linux服务器上执行Java并启动jar包, 通常我们会使用nohup直接启动,但是还是需要手动停止然后再次启动, 那如何更优雅的在服务器上启动jar包呢,让我们一起探讨一下吧。 1、初版 第一个版本是常用的做法,直接使用nohup后台启动jar包, 并将日志输出到当前文件夹n

[Linux]:进程(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:Linux学习 贝蒂的主页:Betty’s blog 1. 进程终止 1.1 进程退出的场景 进程退出只有以下三种情况: 代码运行完毕,结果正确。代码运行完毕,结果不正确。代码异常终止(进程崩溃)。 1.2 进程退出码 在编程中,我们通常认为main函数是代码的入口,但实际上它只是用户级

【Linux】应用层http协议

一、HTTP协议 1.1 简要介绍一下HTTP        我们在网络的应用层中可以自己定义协议,但是,已经有大佬定义了一些现成的,非常好用的应用层协议,供我们直接使用,HTTP(超文本传输协议)就是其中之一。        在互联网世界中,HTTP(超文本传输协议)是一个至关重要的协议,他定义了客户端(如浏览器)与服务器之间如何进行通信,以交换或者传输超文本(比如HTML文档)。

如何编写Linux PCIe设备驱动器 之二

如何编写Linux PCIe设备驱动器 之二 功能(capability)集功能(capability)APIs通过pci_bus_read_config完成功能存取功能APIs参数pos常量值PCI功能结构 PCI功能IDMSI功能电源功率管理功能 功能(capability)集 功能(capability)APIs int pcie_capability_read_wo