• 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 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
    (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
    ", i); 
            }
        }
        printf("bitmask = %#lx
    ", 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
    ", i); 
            }
        }
          printf("bitmask = %#lx
    ", 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
    ", i); 
            }
        }
        printf("bitmask = %#lx
    ", 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:
    ");
        for (j = 0; j < CPU_SETSIZE; j++) //CPU_SETSIZE 是定义在<sched.h>中的宏,通常是1024
        {
            if (CPU_ISSET(j, &cpuset))
            {
                printf("    CPU %d
    ", 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

  • 相关阅读:
    再说Play!framework http://hsfgo.iteye.com/blog/806974
    PlayFramework 1 自定义标签 -- FastTags http://unmi.cc/category/javajee/playframework/
    [译] 第三十天:Play Framework
    你可以使用 play framework 做5件很爽的事情http://www.anool.net/?p=629
    Play常用代码片段 http://www.anool.net/?p=625
    Play 内置模板标签(1.2.3版本)http://www.anool.net/?p=617
    for what? while 与 until 差在哪?-- Shell十三问<第十三问>
    你要 if 还是 case 呢?-- Shell十三问<第十二问>
    > 与 < 差在哪?-- Shell十三问<第十一问>
    && 与 || 差在哪?-- Shell十三问<第十问>
  • 原文地址:https://www.cnblogs.com/antflow/p/7374646.html
Copyright © 2020-2023  润新知