cpu亲和性

 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-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 |grepnginx

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  

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



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 #include //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 */voidCPU_ZERO(cpu_set_t *set);

        voidCPU_ZERO_S(size_t setsize, cpu_set_t *set);


        /* Add CPU cpu to set */voidCPU_SET(intcpu, cpu_set_t *set);

        voidCPU_SET_S(intcpu, size_t setsize, cpu_set_t *set);


        /* Remove CPU cpu from set */voidCPU_CLR(intcpu, cpu_set_t *set);

        voidCPU_CLR_S(intcpu, size_t setsize, cpu_set_t *set);


        /* Test to see if CPU cpu is a member of set */intCPU_ISSET(intcpu, cpu_set_t *set);

        intCPU_ISSET_S(intcpu, size_t setsize, cpu_set_t *set);


        /* Return the number of CPUs in set */voidCPU_COUNT(cpu_set_t *set);

        voidCPU_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). */voidCPU_AND(cpu_set_t *destset, cpu_set_t *srcset1, cpu_set_t *srcset2);

        voidCPU_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). */voidCPU_OR(cpu_set_t *destset, cpu_set_t *srcset1, cpu_set_t *srcset2);

        voidCPU_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). */voidCPU_XOR(cpu_set_t *destset, cpu_set_t *srcset1, cpu_set_t *srcset2);

        voidCPU_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. */intCPU_EQUAL(cpu_set_t *set1, cpu_set_t *set2);

        intCPU_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(). */voidCPU_FREE(cpu_set_t *set);/* API *//* Set the CPU affinity for a task */intsched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);

    /* Get the CPU affinity for a task */intsched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);


    /* set CPU affinity attribute in thread attributes object */intpthread_attr_setaffinity_np(pthread_attr_t *attr, size_t cpusetsize,constcpu_set_t *cpuset);

    /* get CPU affinity attribute in thread attributes object */intpthread_attr_getaffinity_np(constpthread_attr_t *attr, size_t cpusetsize, cpu_set_t *cpuset);


    /* set CPU affinity of a thread */intpthread_setaffinity_np(pthread_t thread, size_t cpusetsize,constcpu_set_t *cpuset);

    /* get CPU affinity of a thread */intpthread_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 #include /* sysconf */#include /* exit */#include intmain(void)

{

    inti, nrcpus;cpu_set_t mask;    unsignedlongbitmask =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|= (unsignedlong)0x01<< i;

            printf("processor #%d is set\n", i);

        }

    }

    printf("bitmask = %#lx\n", bitmask);

    exit(EXIT_SUCCESS);

}/*----------------------------------------------------------------*/#define_GNU_SOURCE#include #include /* sysconf */#include /* exit */#include intmain(void)

{

    inti, nrcpus;cpu_set_t *pmask;    size_t cpusize;    unsignedlongbitmask =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|= (unsignedlong)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 #include /* sysconf */#include /* exit */#include intmain(void)

{

    int i, nrcpus;

    cpu_set_t mask;

    unsigned longbitmask =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 |= (unsignedlong)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 //不用再包含<sched.h>#include #include #include #definehandle_error_en(en, msg) \do{ errno = en; perror(msg); exit(EXIT_FAILURE); }while(0)intmain(intargc,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 #include #include // syscallintmain(void){pid_t tid;int i, nrcpus;

    cpu_set_t mask;

    unsigned longbitmask =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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容