Linux CPU Affinity

一,背景

 在服务器压力特别大,心跳经常丢失从而造成服务超时。经过分析发现网络没有问题,心跳网络包都发过来了而且也正常进入了dispatch队列,但是由于dispatch在处理别的request的时候耗时过长,而且要命的是它还hold着一把全局的锁,导致队列里面的其他queue也无法正常被dispatch。所以我们想到可以利用设置CPU亲和性来保证核心进程/线程得到足够的时间片,从而不让服务超时。

二,CPU隔离

 说到亲和性,如果我们不隔离CPU,那么就只能减少CPU切换,提高cpu cache的命中率,从而减少内存访问损耗,提高程序的速度。但是这样做只能保证自己不被调度到的别的CPU,却不能阻止其他线程不来我这个CPU。这样就成了“我的是公共的,别人的我不能用!”,岂不是我得到的时间片更少了?所以为了防止这样的窘境,我们还得先“隔核”再“绑核”。

隔离CPU核心

我们可以用如下命令来查看自己的服务器有多少个核心:

[root@xt1 ~]# cat /proc/cpuinfo |grep "model name"
model name  : Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz
model name  : Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz
model name  : Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz
model name  : Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz
model name  : Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz
model name  : Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz

隔核方法: 修改grub
环境: CentOS7
具体步骤: (隔离4,5核心)

  • 找到内核启动项,在其后面添加 “isolcpus=4,5
linux16 /boot/vmlinuz-3.10.0-514.26.2.el7.x86_64 root=UUID=8c3205a6-2433-4799-8413-c74e4fe97f5c ro crashkernel=auto rhgb quiet LANG=en_US.UTF-8 systemd.debug isolcpus=4,5
  • reboot 重启服务器
  • 检查启动项是否设置成功
[root@xt1 ~]# cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-3.10.0-514.26.2.el7.x86_64 root=UUID=8c3205a6-2433-4799-8413-c74e4fe97f5c ro crashkernel=auto rhgb quiet LANG=en_US.UTF-8 systemd.debug isolcpus=4,5
  • 检查隔离是否生效
// 如果设置成功,则落在该核心的线程会很少
ps -eLo pid,user,lwp,psr  | awk '{if($4==4) print $0}'  //检查核心4
ps -eLo pid,user,lwp,psr  | awk '{if($4==5) print $0}'  //检查核心5

// 也可以用top命令来查看
执行top,按数字1,就可以调出每个核心的使用状态,然后按下f按键,向下找到"P= Last Used Cpu (SMP)"这一行,按下回车,再按q按键返回,就可以看到每个线程具体落在哪个核心上面

top - 16:28:40 up 5 days, 22:03,  2 users,  load average: 0.02, 0.08, 0.44
Tasks: 260 total,   2 running, 258 sleeping,   0 stopped,   0 zombie
%Cpu0  :  0.0 us,  0.3 sy,  0.0 ni, 99.3 id,  0.0 wa,  0.0 hi,  0.3 si,  0.0 st
%Cpu1  :  0.0 us,  0.3 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu2  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu3  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu4  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu5  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  7840224 total,   738160 free,   506176 used,  6595888 buff/cache
KiB Swap:  9727996 total,  9250224 free,   477772 used.  6992616 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                              P
 2894 root      20   0   51192  18928   2884 S   1.0  0.2  74:29.74 etcd                                                                 1
 2836 root      20   0 1855044  50728   3296 S   0.7  0.6  25:13.31 ceph-osd                                                             3
 4648 root      20   0  146304   2200   1436 R   0.7  0.0   0:00.10 top                                                                  1
 1813 root      20   0  554324  64492   9676 S   0.3  0.8   0:12.40 ceph-mds                                                             1
 3041 root      20   0       0      0      0 S   0.3  0.0   4:10.88 xfsaild/dm-8                                                         1
 3120 root      20   0 1818688  97464   2580 S   0.3  1.2  24:13.09 ceph-osd                                                             2
30405 root      20   0       0      0      0 S   0.3  0.0   0:01.33 kworker/0:2                                                          0
    1 root      20   0   43476   2472   1340 S   0.0  0.0   0:30.26 systemd                                                              3

三,sched_setaffinity/sched_getaffinity

更改一个进程的CPU affinity mask,可以设置一个进程在某个CPU核心上执行,也可以设置该进程在除了某CPU 核心之外的其他CPU核心上执行。如果第一个参数pid为0,则设置当前进程的mask。
 其实这个mask是一个针对线程组内的线程属性,可以被独立调节。所以我们可以用gettid()的参数作为第一个参数,同样如果第一个参数为0,则设置当前线程。如果传的getpid的返回值,则设置该线程组的主线程mask属性。

int sched_setaffinity(pid_t pid, size_t cpusetsize,
                      cpu_set_t *mask);

int sched_getaffinity(pid_t pid, size_t cpusetsize,
                      cpu_set_t *mask);
  • 实验1
#include <stdio.h>
#define __USE_GNU
#include <stdlib.h>
#include <sched.h>
#include <pthread.h>
#include <unistd.h>

void * fn(void *arg)
{
    cpu_set_t set;
    int i = 0;

    CPU_ZERO(&set);
    CPU_SET(1, &set);

    printf("child thread is arriving .../n");

    sched_setaffinity(0, sizeof(cpu_set_t), &set);
    while (i<100000000)
    {
        i++;
        if (i%5000 == 0)
        {
            printf ("i is %d\n", i);
            sleep(1);
        }
    }
}


int main()
{
    int isolated = 1;

    pthread_t tid;
    pthread_create(&tid, NULL, fn, NULL);

    pthread_join(tid, NULL);
    return 0;
}

四,pthread_setaffinity_np/pthread_getaffinity_np

更改一个线程的CPU affinity mask, 同样也是可以设置一个线程在某个CPU核心上执行,也可以设置该线程在除了某CPU 核心之外的其他CPU核心上执行。这两个接口在底层也是调用sched_setaffinity/sched_getaffinity。当第一个参数为0时,就是设置当前thread的mask。

#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.
  • 实验2
#include <stdio.h>
#define __USE_GNU
#include <stdlib.h>
#include <sched.h>
#include <pthread.h>
#include <unistd.h>

void self_sleep()
{
    int i = 0;
    while (i<100000000)
    {
            i++;
            if (i%5000 == 0)
            {
                    printf ("i is %d\n", i);
                    sleep(1);
            }
    }
}

void * fn(void *arg)
{
    cpu_set_t set;
    pthread_attr_t tattr;

    pthread_attr_init(&tattr);


    CPU_ZERO(&set);
    CPU_SET(1, &set);

    printf("child thread is arriving .../n");

    pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &set);

    self_sleep();
}


int main()
{
    int isolated = 1;

    pthread_t tid;
    pthread_create(&tid, NULL, fn, NULL);

    pthread_join(tid, NULL);
    return 0;
}

五,属性继承

上面我们介绍了两种设置CPU affinity mask的接口,但是无论是哪一种接口有个共同的特性:创建出来的子线程默认会继承父亲线程的CPU affinity mask。在复杂的系统中有时并不希望这个属性的出现,因为可能会导致某个隔离出来的cpu上还是运行了很多的thread。所以我就想了一个解决方案来解决这个问题。

让所有的线程创建出来之后都跑在除了隔离出来的核心之外的其他核心上面,然后在单独为需要绑定核心的线程来设置CPU affinity mask

  • 实验3
#include <stdio.h>
#define __USE_GNU
#include <stdlib.h>
#include <sched.h>
#include <pthread.h>
#include <unistd.h>

void self_sleep()
{
    int i = 0;
    while (i<100000000)
    {
            i++;
            if (i%5000 == 0)
            {
                    printf ("i is %d\n", i);
                    sleep(1);
            }
    }
}

void * fn2(void *arg)
{
    printf("child2 thread is arriving .../n");
    self_sleep();
}

void * fn(void *arg)
{
    int i, cpus;
    cpu_set_t set;
    pthread_t tid;
    pthread_attr_t tattr;

    pthread_attr_init(&tattr);

    cpus = sysconf(_SC_NPROCESSORS_CONF);

    printf("have %d cpus\n", cpus);
    CPU_ZERO(&set);

    for (i=0; i<cpus; i++)
    {
        if (i == 1)
            continue;
        CPU_SET(i, &set);
    }

    printf("child thread is arriving .../n");
    printf("set child thread cpu to 0 .../n");

    pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &set);

    pthread_create(&tid, NULL, fn2, NULL);

    self_sleep();
    pthread_join(tid, NULL);
}


int main()
{
    int isolated = 1;

    cpu_set_t set;

    CPU_ZERO(&set);
    CPU_SET(1, &set);

    pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &set);

    pthread_t tid;
    pthread_create(&tid, NULL, fn, NULL);

    pthread_join(tid, NULL);
    return 0;
}

参考文档

<1> http://man7.org/linux/man-pages/man3/pthread_setaffinity_np.3.html
<2> https://linux.die.net/man/2/sched_setaffinity

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

推荐阅读更多精彩内容