Linux进程、线程与cpu的亲和性(affinity)

2024-09-03 04:32

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

一、什么是cpu亲和性(affinity)

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

软亲和性(affinity)

就是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,Linux 内核进程调度器天生就具有被称为 软 CPU 亲和性(affinity) 的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,因为进程迁移的频率小就意味着产生的负载小。

硬亲和性(affinity)

简单来说就是利用linux内核提供给用户的API,强行将进程或者线程绑定到某一个指定的cpu核运行。

解释

在linux内核中,所有的进程都有一个相关的数据结构,称为 task_struct。这个结构非常重要,原因有很多;其中与 亲和性(affinity)相关度最高的是 cpus_allowed 位掩码。这个位掩码由 n 位组成,与系统中的 n 个逻辑处理器一一对应。 具有 4 个物理 CPU 的系统可以有 4 位。如果这些 CPU 都启用了超线程,那么这个系统就有一个 8 位的位掩码。 如果为给定的进程设置了给定的位,那么这个进程就可以在相关的 CPU 上运行。因此,如果一个进程可以在任何 CPU 上运行,并且能够根据需要在处理器之间进行迁移,那么位掩码就全是 1。实际上,这就是 Linux 中进程的缺省状态;(这部分内容在这个博客中有提到一点:http://www.cnblogs.com/wenqiang/p/4802619.html)
cpus_allowed用于控制进程可以在哪里处理器上运行
sched_set_affinity() (用来修改位掩码)
sched_get_affinity() (用来查看当前的位掩码)

进程与cpu的绑定

sched_setaffinity可以将某个进程绑定到一个特定的CPU。你比操作系统更了解自己的程序,为了避免调度器愚蠢的调度你的程序,或是为了在多线程程序中避免缓存失效造成的开销,你可能会希望这样做。

在进行进程与cpu的绑定前,我们先了解编写程序需要准备的知识点。

SCHED_SETAFFINITY(2)                                                                      
Linux Programmer's Manual                                                                     
SCHED_SETAFFINITY(2)NAMEsched_setaffinity, sched_getaffinity - set and get a process's CPU affinity maskSYNOPSIS#define _GNU_SOURCE             /* See feature_test_macros(7) */#include <sched.h>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上运行.*/int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);/*该函数获得pid所指示的进程的CPU位掩码,并将该掩码返回到mask所指向的结构中.*即获得指定pid当前可以运行在哪些CPU上.*同样,如果pid的值为0.也表示的是当前进程*/RETURN VALUEOn success, sched_setaffinity() and sched_getaffinity() return 0.  On error, -1 is returned, and errno is set appropriately.

设置cpu affinity还需要用到一下宏函数

void CPU_ZERO (cpu_set_t *set)
/*这个宏对 CPU 集 set 进行初始化,将其设置为空集。*/
void CPU_SET (int cpu, cpu_set_t *set)
/*这个宏将 指定的 cpu 加入 CPU 集 set 中*/
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 的一员,这个宏就返回一个非零值(true),否则就返回零(false)。*/

下面下一个具体的例子:将当前进程绑定到0、1、2、3号cpu上

#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>/* sysconf( _SC_NPROCESSORS_CONF ) 查看cpu的个数;打印用%ld长整。* sysconf( _SC_NPROCESSORS_ONLN ) 查看在使用的cpu个数;打印用%ld长整 */
int main(int argc, char **argv)
{int cpus = 0;int  i = 0;cpu_set_t mask;cpu_set_t get;cpus = sysconf(_SC_NPROCESSORS_CONF);printf("cpus: %d\n", cpus);CPU_ZERO(&mask);    /* 初始化set集,将set置为空*/CPU_SET(0, &mask);  /* 依次将0、1、2、3号cpu加入到集合,前提是你的机器是多核处理器*/CPU_SET(1, &mask);CPU_SET(2, &mask);CPU_SET(3, &mask);/*设置cpu 亲和性(affinity)*/if (sched_setaffinity(0, sizeof(mask), &mask) == -1) {printf("Set CPU affinity failue, ERROR:%s\n", strerror(errno));return -1; }   usleep(1000); /* 让当前的设置有足够时间生效*//*查看当前进程的cpu 亲和性*/CPU_ZERO(&get);if (sched_getaffinity(0, sizeof(get), &get) == -1) {printf("get CPU affinity failue, ERROR:%s\n", strerror(errno));return -1; }   /*查看运行在当前进程的cpu*/for(i = 0; i < cpus; i++) {if (CPU_ISSET(i, &get)) { /*查看cpu i 是否在get 集合当中*/printf("this process %d of running processor: %d\n", getpid(), i); }    }sleep(3); //让程序停在这儿,方便top命令查看return 0;
}

运行结果如下:

[root@localhost test]# ./test    
cpus: 24
this process 2848 of running processor: 0
this process 2848 of running processor: 1
this process 2848 of running processor: 2
this process 2848 of running processor: 3

上面代码当中用到了syscall这个函数,顺便也在这里做一下说明

syscall是执行一个系统调用,根据指定的参数number和所有系统调用的接口来确定调用哪个系统调用,用于用户空间跟内核之间的数据交换

下面是syscall函数原型及一些常用的number

//syscall - indirect system call
SYNOPSIS#define _GNU_SOURCE         /* See feature_test_macros(7) */#include <unistd.h>#include <sys/syscall.h>   /* For SYS_xxx definitions */int syscall(int number, ...);/* sysconf( _SC_PAGESIZE );  此宏查看缓存内存页面的大小;打印用%ld长整型。sysconf( _SC_PHYS_PAGES ) 此宏查看内存的总页数;打印用%ld长整型。sysconf( _SC_AVPHYS_PAGES ) 此宏查看可以利用的总页数;打印用%ld长整型。sysconf( _SC_NPROCESSORS_CONF ) 查看cpu的个数;打印用%ld长整。sysconf( _SC_NPROCESSORS_ONLN ) 查看在使用的cpu个数;打印用%ld长整。(long long)sysconf(_SC_PAGESIZE) * (long long)sysconf(_SC_PHYS_PAGES) 计算内存大小。sysconf( _SC_LOGIN_NAME_MAX ) 查看最大登录名长度;打印用%ld长整。sysconf( _SC_HOST_NAME_MAX ) 查看最大主机长度;打印用%ld长整。sysconf( _SC_OPEN_MAX )  每个进程运行时打开的文件数目;打印用%ld长整。sysconf(_SC_CLK_TCK) 查看每秒中跑过的运算速率;打印用%ld长整。*/

三、线程与cpu的绑定

线程于进程的绑定方法大体一致,需要注意的是线程绑定于进程的区别是所用函数不一样

线程绑定用到下面两个函数,跟进程类似就不做详细说明,下面直接贴出函数原型:

NAMEpthread_setaffinity_np, pthread_getaffinity_np - set/get CPU affinity of a threadSYNOPSIS#define _GNU_SOURCE             /* See feature_test_macros(7) */#include <pthread.h>int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,const cpu_set_t *cpuset);int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize,cpu_set_t *cpuset);Compile and link with -pthread.DESCRIPTIONThe  pthread_setaffinity_np()  function  sets the CPU affinity mask of the thread thread to the CPU set pointed to by cpuset.  If the call is successful, and the thread is notcurrently running on one of the CPUs in cpuset, then it is migrated to one of those CPUs.The pthread_getaffinity_np() function returns the CPU affinity mask of the thread thread in the buffer pointed to by cpuset.For more details on CPU affinity masks, see sched_setaffinity(2).  For a description of a set of macros that can be used to manipulate and inspect CPU sets, see CPU_SET(3).The argument cpusetsize is the length (in bytes) of the buffer pointed to by cpuset.  Typically, this argument would be specified as sizeof(cpu_set_t).  (It may be some  othervalue, if using the macros described in CPU_SET(3) for dynamically allocating a CPU set.)RETURN VALUEOn success, these functions return 0; on error, they return a nonzero error number

下面同样是个具体的例子:将当前线程绑定到0、1、2、3号cpu上

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>void *testfunc(void *arg)
{int i, cpus = 0;cpu_set_t mask;cpu_set_t get;cpus = sysconf(_SC_NPROCESSORS_CONF);printf("this system has %d processor(s)\n", cpus);CPU_ZERO(&mask);for (i = 0; i < 4; i++) { /*将0、1、2、3添加到集合中*/CPU_SET(i, &mask);}   /* 设置cpu 亲和性(affinity)*/if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) {fprintf(stderr, "set thread affinity failed\n");}   /* 查看cpu 亲和性(affinity)*/CPU_ZERO(&get);if (pthread_getaffinity_np(pthread_self(), sizeof(get), &get) < 0) {fprintf(stderr, "get thread affinity failed\n");}   /* 查看当前线程所运行的所有cpu*/for (i = 0; i < cpus; i++) {if (CPU_ISSET(i, &get)) {printf("this thread %d is running in processor %d\n", (int)pthread_self(), i); }   }   sleep(3); //查看pthread_exit(NULL);
}int main(int argc, char *argv[])
{pthread_t tid;if (pthread_create(&tid, NULL, (void *)testfunc, NULL) != 0) {fprintf(stderr, "thread create failed\n");return -1; }   pthread_join(tid, NULL);return 0;
}

运行结果如下:

1 [root@localhost thread]# ./test                      
2 this system has 24 processor(s)
3 this thread 2812323584 is running in processor 0
4 this thread 2812323584 is running in processor 1
5 this thread 2812323584 is running in processor 2
6 this thread 2812323584 is running in processor 3

这篇关于Linux进程、线程与cpu的亲和性(affinity)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

Java中如何正确的停掉线程

《Java中如何正确的停掉线程》Java通过interrupt()通知线程停止而非强制,确保线程自主处理中断,避免数据损坏,线程池的shutdown()等待任务完成,shutdownNow()强制中断... 目录为什么不强制停止为什么 Java 不提供强制停止线程的能力呢?如何用interrupt停止线程s

linux系统上安装JDK8全过程

《linux系统上安装JDK8全过程》文章介绍安装JDK的必要性及Linux下JDK8的安装步骤,包括卸载旧版本、下载解压、配置环境变量等,强调开发需JDK,运行可选JRE,现JDK已集成JRE... 目录为什么要安装jdk?1.查看linux系统是否有自带的jdk:2.下载jdk压缩包2.解压3.配置环境

Linux搭建ftp服务器的步骤

《Linux搭建ftp服务器的步骤》本文给大家分享Linux搭建ftp服务器的步骤,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录ftp搭建1:下载vsftpd工具2:下载客户端工具3:进入配置文件目录vsftpd.conf配置文件4:

python 线程池顺序执行的方法实现

《python线程池顺序执行的方法实现》在Python中,线程池默认是并发执行任务的,但若需要实现任务的顺序执行,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋... 目录方案一:强制单线程(伪顺序执行)方案二:按提交顺序获取结果方案三:任务间依赖控制方案四:队列顺序消

Linux实现查看某一端口是否开放

《Linux实现查看某一端口是否开放》文章介绍了三种检查端口6379是否开放的方法:通过lsof查看进程占用,用netstat区分TCP/UDP监听状态,以及用telnet测试远程连接可达性... 目录1、使用lsof 命令来查看端口是否开放2、使用netstat 命令来查看端口是否开放3、使用telnet

SysMain服务可以关吗? 解决SysMain服务导致的高CPU使用率问题

《SysMain服务可以关吗?解决SysMain服务导致的高CPU使用率问题》SysMain服务是超级预读取,该服务会记录您打开应用程序的模式,并预先将它们加载到内存中以节省时间,但它可能占用大量... 在使用电脑的过程中,CPU使用率居高不下是许多用户都遇到过的问题,其中名为SysMain的服务往往是罪魁

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优