Linux中CPU亲和性(affinity)——一篇文章搞定所有

来源:互联网 发布:java适配器模式 编辑:程序博客网 时间:2024/06/15 15:41

http://www.cnblogs.com/LubinLew/p/cpu_affinity.html


http://www.cnblogs.com/LubinLew/p/cpu_affinity.html


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 - 进程IDargs - 该进程执行时传入的命令行参数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 #00x00000003    is processors #0 and #10xFFFFFFFF    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)intmain(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> // 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);}
复制代码

 

 

 

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

参考文献:

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





阅读全文
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 淘宝店注册成功后怎么办 充电宝掉进水里怎么办 空光盘读不出来怎么办 苹果8plus丢了怎么办 苹果8plus掉了怎么办 淘宝账户被限制使用怎么办 飞利浦电脑显示器黑屏怎么办打开 微信忘记密码手机号停用怎么办 淘宝账号登录密码忘记了怎么办 淘宝支付密码输错了怎么办 淘宝支付密码忘记了怎么办 淘宝货品上架后显示过期怎么办 被淘宝客监控了怎么办 助学贷款支付宝账号忘记怎么办 离婚支付宝的钱怎么办 扫码加载不出来怎么办 淘宝店铺被投诉售假怎么办 刚开的新店没人怎么办 淘宝访客量突然下降怎么办 支付宝账号封了怎么办 苹果x手机黑屏打不开怎么办 淘宝支付密码被锁定怎么办 淘宝支付密码锁定了怎么办 很抱歉手机淘宝停止运行怎么办 苹果手机桌面淘宝图标不见了怎么办 提示网页上有错误怎么办 淘宝定制家具违约两个月怎么办 同行在买家评价下面评论怎么办 淘宝知识产权侵权被投诉了怎么办 闲鱼买家说是假货退货怎么办 日验证失败次数上限怎么办 淘宝卖家发货不全怎么办 子账号登录不上怎么办 淘宝宝贝曝光量少怎么办 淘宝买东西退款卖家拒绝怎么办 华为手机淘宝不能收藏宝贝怎么办 包裹发出不想要了怎么办 淘宝改登录密码忘记了怎么办 三星s8应用闪退怎么办 淘宝购物商家不发货怎么办 淘宝商家迟迟不发货怎么办