Linux中CPU亲和性(affinity)

0、准备知识

超线程技术(Hyper-Threading):就是利用特殊的硬件指令,把两个逻辑内核(CPU core)模拟成两个物理芯片,

    让单个处理器都能使用线程级并行计算,进而兼容多线程操作系统和软件,减少了CPU的闲置时间,提高的CPU的运行效率。

    我们常听到的双核四线程/四核八线程指的就是支持超线程技术的CPU.

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

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

逻辑CPU数量 = 物理CPU数量 x CPU cores x 2(如果支持并开启HT) //前提是CPU的型号一致,如果不一致只能一个一个的加起来,不用直接乘以物理CPU数量
//比如你的电脑安装了一块4核CPU,并且支持且开启了超线程(HT)技术,那么逻辑CPU数量 = 1 × 4 × 2 = 8

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

# 查看物理CPU个数
cat /proc/cpuinfo|grep "physical id"|sort|uniq|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 nginx9073 nginx: master process /usr/   19074 nginx: worker process         09075 nginx: worker process         19076 nginx: worker process         29077 nginx: worker process         313857 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
(1)ps -efL | grep prog_name
(2)ls /proc/pid/task            //文件夹名即TID

1、CPU亲和性(亲和力)

1.1 基本概念

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

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

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

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

1.2 表示方法

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

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

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

0x00000001
    is processor #0

0x00000003
    is processors #0 and #1

0xFFFFFFFF
    is all processors (#0 through #31)

2、taskset命令

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

# 命令行形式
taskset [options] mask command [arg]...taskset [options] -p [mask] pid

PARAMETER    mask : 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, --help
              display usage information and exit
       -V, --version
              output version information and exit

USAGE

    1) 使用指定的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 1

    3) 改变已经运行进程的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亲和性.

taskset命令其实就是使用sched_getaffinity()和sched_setaffinity()接口实现的,相信看完了第3节你也能自己实现一个taskset命令.

有兴趣的可以看一下其源代码:ftp://ftp.kernel.org/pub/linux/utils/util-linux/vX.YZ/util-linux-X.YZ-xxx.tar.gz   /schedutils/taskset.c

3、编程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概念。

3.1 例子一:使用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):

 执行结果

3.2 例子二:设置进程的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);
}

3.3 例子三:设置线程的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);
}

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

#define _GNU_SOURCE
#include <sched.h>
#include <stdlib.h>
#include <sys/syscall.h> // syscall

int 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);
}

---------------------------------------------------------------------------------------------------------------------

参考文献:

http://www.yboren.com/posts/44412.html?utm_source=tuicool

http://www.ibm.com/developerworks/cn/linux/l-affinity.html

http://saplingidea.iteye.com/blog/633616

http://blog.csdn.net/ttyttytty12/article/details/11726569

https://en.wikipedia.org/wiki/Processor_affinity

http://blog.chinaunix.net/uid-23622436-id-3311579.html

http://www.cnblogs.com/emanlee/p/3587571.html

http://blog.chinaunix.net/uid-26651253-id-3342161.html

http://blog.csdn.net/delphiwcdj/article/details/8476547

http://www.man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html

http://www.man7.org/linux/man-pages/man3/pthread_attr_setaffinity_np.3.html

man CPU_SET taskset

时间: 2024-07-28 23:00:26

Linux中CPU亲和性(affinity)的相关文章

Linux中CPU与内存性能监测

在系统维护的过程中,随时可能有需要查看 CPU 使用率内存使用情况的需要,尤其是涉及到JVM,程序调优的情况,并根据相应信息分析系统状况的需要. top命令 top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器.运行 top 命令后,CPU 使用状态会以全屏的方式显示,并且会处在对话的模式 -- 用基于 top 的命令,可以控制显示方式等等.退出 top 的命令为 q (在 top 运行中敲 q 键一次). 可以直接使用top命令

Nginx中的进程亲和性 affinity

Nginx采用多进程Master/Worker结构,Worker进程数为CPU个数时工作效率最高,Nginx通过affinity为每个Worker进程绑定一个CPU,避免进程切换带来的消耗,同时能够保证缓存命中率. Nginx配置文件conf/nginx.conf中关于Worker进程个数,和affinity的配置命令: worker_processes 4; worker_cpu_affinity 1000 0100 0010 0001; 应用程序可以主动调用接口设定: Linux进程: in

在Linux中通过Top运行进程查找最高内存和CPU使用率

按内存使用情况查找前15个进程,在批处理模式下为“top” 使用top命令查看有关当前状态,系统使用情况的更详细信息:正常运行时间,负载平均值和进程总数. 分类:Linux命令操作系统 2016-07-27 00:00:00 类似于前面的技巧有关找出由RAM和CPU使用率最高的进程 ,还可以使用top命令来查看相同的信息. 也许有相比前一个这种方法的一个额外的优势:顶级的“头”,提供有关当前状态和使用该系统的额外信息:正常运行时间,平均负载和进程总数,仅举几例例子. 按顶部查找按内存使用的进程

cpu亲和性绑定

将进程与cpu绑定,最直观的好处就是减少cpu之间的cache同步和切换,提高了cpu cache的命中率,提高代码的效率.从cpu架构上,NUMA拥有独立的本地内存,节点之间可以通过互换模块做连接和信息交互,因此每个CPU可以访问整个系统的内存,但是访问远地内存访问效率大大降低,绑定cpu操作对此类系统运行速度会有较大提升,UMA架构下,多cpu通过系统总线访问存储模块.不难看出,NUMA使用cpu绑定时,每个核心可以更专注地处理一件事情,资源体系被充分使用,减少了同步的损耗. 简单地说,CP

关于CPU亲和性的测试

今天看到运维的同事在配置nginx的CPU亲和性时候,运维同事说他在所有的机器上都是按照8核的方式来配置worker进程的CPU亲和性的. 但我觉得就是有点不太对劲,就查了一下nginx的处理worker_cpu_affinity的源代码,发现nginx并不会在发现配置错误的时候拒绝启动worker进程,而是仅仅打印一条错误日志“sched_setaffinity() failed”. 如果设置亲和性失败则按照SMP负载策略进行处理,linux的SMP负载均衡是基于进程数的,每个cpu都有一个可

Java线程CPU亲和性工具

Thread Affinity 为什么需要线程的CPU亲和性 应用通过多线程的方式执行,多数情况下线程能够被合理的调度.但在某些情况下某个重要的线程被暂停,而时间片被分配给了一个无关重要的线程.当一个线程每次被暂停休眠,然后被唤醒之后,需要重新加载"cache line"(cpu L1/L2 cache).当线程的工作时间很短暂,需要被频繁的被唤醒,意味着整个流程执行都很慢,有可能比单线程情况下慢2-5倍. 应用的有些线程可能需要一直执行,不因CPU的调度而休眠,这需要使线程一直在某个

linux进程cpu资源分配命令nice,renice,taskset

进程cpu资源分配就是指进程的优先权(priority).优先权高的进程有优先执行权利.配置进程优先权对多任务环境的linux很有用,可以改善系统性能.还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能. 一.先看系统进程: PR 就是 Priority 的简写,而 NI 是 nice 的简写.这两个值决定了PR的值,PR越小,进程优先权就越高,就越"优先执行".换算公式为:PR(new) = PR(old) + NI --------

理解Linux中的load Averges

一.什么是load average? linux系统中的Load对当前CPU工作量的度量 (WikiPedia: the system load is a measure of the amount of work that a computer system is doing).也有简单的说是进程队列的长度. Load Average 就是一段时间 (1 分钟.5分钟.15分钟) 内平均 Load . 我们可以通过系统命令"w"查看当前load average情况 [[email p

Linux中的僵尸进程和孤儿进程

在UNIX里,除了进程0(即PID=0的交换进程,Swapper Process)以外的所有进程都是由其他进程使用系统调用fork创建的,这里调用fork创建新进程的进程即为父进程,而相对应的为其创建出的进程则为子进程,因而除了进程0以外的进程都只有一个父进程,但一个进程可以有多个子进程.        操作系统内核以进程标识符(Process Identifier,即PID)来识别进程.进程0是系统引导时创建的一个特殊进程,在其调用fork创建出一个子进程(即PID=1的进程1,又称init)