正在阅读:

linux服务器性能调优-线程进程绑核策略

476

一. 目的

总结linux高性能服务器调优手段 - 线程进程绑核。

  1. 提高进程的处理优先级
  2. 从SMP系统中,专门划拨出某一个CPU用于运行该程序。 而将其他进程划拨到其他的CPU上进行运行。

CPU affinity:  中文唤作 "CPU亲和力"

目前主流的服务器配置都是SMP架构,在SMP的环境下,

每个CPU本身自己会有缓存,缓存着进程使用的信息,

而进程可能会被kernel调度到其他CPU上(即所谓的core migration),

如此,CPU cache命中率就低了。设置CPU亲缘性,程序就会一直在指定的cpu运行,

防止进程在多SMP的环境下的core migration,从而避免因切换带来的CPU的L1/L2 cache失效。

从而进一步提高应用程序的性能

什么是CPU亲缘性

所谓CPU亲缘性可以分为两大类:软亲缘性和硬亲缘性。

Linux 内核进程调度器天生就具有被称为 CPU 软亲缘性(soft affinity) 的特性,

这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,

因为进程迁移的频率小就意味着产生的负载小。但不代表不会进行小范围的迁移。

CPU 硬亲缘性是指通过Linux提供的相关CPU亲缘性设置接口,

显示的指定某个进程固定的某个处理器上运行。本文所提到的CPU亲缘性主要是指硬亲缘性

二. 总结

1.  获取线程核数

a. 方法1

sysconf(_SC_NPROCESSORS_ONLN)

返回值真正的代表了系统当前可用的核数

sysconf(_SC_NPROCESSORS_CONF);

返回系统可以使用的核数,但是其值会包括系统中禁用的核的数目,

因此该值并不代表当前系统中可用的核数

b. 方法2

#include <sys/sysinfo.h>

int  get_nprocs_conf (void);/* 可用核数 */

int  get_nprocs (void);/* 真正的反映了当前可用核数 */

c. 方法3

通过读取系统文件cat /proc/cpuinfo 获取相应信息

/proc/cpuinfo - CPU 的信息 (型号, 家族, 缓存大小等)

/proc/meminfo - 物理内存、交换空间等的信息

/proc/mounts - 已加载的文件系统的列表

/proc/devices - 可用设备的列表

/proc/filesystems - 被支持的文件系统

/proc/modules - 已加载的模块

/proc/version - 内核版本

/proc/cmdline - 系统启动时输入的内核命令行参数

d. 方法4

用命令判断几个物理CPU,几个核等:

逻辑CPU个数:

# cat /proc/cpuinfo | grep 'processor' | wc -l

物理CPU个数:

# cat /proc/cpuinfo | grep 'physical id' | sort | uniq | wc -l

每个物理CPU中Core的个数:

# cat /proc/cpuinfo | grep 'cpu cores' | wc -l

是否为超线程?

如果有两个逻辑CPU具有相同的”core id”,那么超线程是打开的。

每个物理CPU中逻辑CPU(可能是core, threads或both)的个数:

# cat /proc/cpuinfo | grep 'siblings'

2.  线程绑核操作

主要使用pthread_setaffinity_np和sched_setaffinity两个函数函数

#define __USE_GNU  要设置这个宏

CPU_ZERO() 清空一个集合

CPU_SET() 将一个给定的CPU号加到一个集合

CPU_CLR() 从一个集合中去掉.

CPU_ISSET()检查一个CPU号是否在这个集合中

策略: 接收RX 和 TX发送线程一般创建为在线的CPU核数的两倍。

a. 方法1

s32 rc;

pthread_attr_t attr;

pthread_attr_init(&attr);

cpu_set_t cpu_info;

__CPU_ZERO(&cpu_info);

__CPU_SET(cpu_id, &cpu_info);

rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpu_info);

if (rc != 0)

{

printf("set affinity failed\n");

return -1;

}

b. 方法2

s32 rc;

cpu_set_t cpu_info;

__CPU_ZERO(&cpu_info);

__CPU_SET(cpu_id, &cpu_info);

rc = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpu_info);

for (s32 j = 0; j < get_nprocs_conf(); j++)

if (CPU_ISSET(j, &cpu_info))

{

printf("CPU %d\n", j);

}

return rc;

c. 方法3

使用 taskset命令, cpuset命令可以设置CPU独占

显示进程运行的CPU: taskset -p pid

注意,此命令返回的是十六进制的,转换成二进制后,

每一位对应一个逻辑CPU,低位是0号CPU,依次类推.

如果每个位置上是1,表示该进程绑定了该CPU。

例如,0101就表示进程绑定在了0号和3号逻辑CPU上

a. 掩码形式绑核: taskset -p mask pid

b.按CPU数直接绑核

taskset -cp cpu-list pid 或者 taskset -c cpu-list command

通过top -Hp pid查看

注意: 子进程会继承父进程的affinity属性

3.  进程绑核操作

函数sched_setaffinity可以将某个进程绑定到一个特定的CPU

设置进程号为pid的进程运行在mask所设定的CPU上

第二个参数cpusetsize是mask所指定的数的长度, 通常设定为sizeof(cpu_set_t)

如果pid的值为0,则表示指定的是当前进程

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

获得pid所指示的进程的CPU位掩码,并将该掩码返回到mask所指向的结构中

#define _GNU_SOURCE /* See feature_test_macros(7) */

#include <sched.h>

#include <pthread.h>

cpu_set_t mask;

cpu_set_t get;

CPU_ZERO(&mask);

CPU_SET(cpu_id, &mask);

if (sched_setaffinity(0, sizeof(mask), &mask) == -1)

{

printf("warning: could not set CPU affinity, continuing...\n");

}

CPU_ZERO(&get);

if (sched_getaffinity(0, sizeof(get), &get) == -1)

{

printf("warning: cound not get thread affinity, continuing...\n");

}

for (int i = 0; i < srv->max_cores; i++)

{

if (CPU_ISSET(i, &get))//判断线程与哪个CPU有亲和力

{

printf("this thread %d is running processor : %d\n", i, i);

}

}

留下脚印,证明你来过。

*

*

流汗坏笑撇嘴大兵流泪发呆抠鼻吓到偷笑得意呲牙亲亲疑问调皮可爱白眼难过愤怒惊讶鼓掌
关闭