为程序选择在哪个cpu上跑

首页 > Scheduler > CPU亲和性的使用与机制

CPU亲和性的使用与机制

Scheduler 2013-08-15

CPU的亲和性, 就是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,亲和性是从affinity翻译过来的,应该有点不准确,给人的感觉是亲和性就是有倾向的意思,而实际上是倒向的意思,称为CPU关联性更好,程序员的土话就是绑定CPU,绑核。

在多核运行的机器上,每个CPU本身自己会有缓存,缓存着进程使用的信息,而进程可能会被OS调度到其他CPU上,如此,CPU cache命中率就低了,当绑定CPU后,程序就会一直在指定的cpu跑,不会由操作系统调度到其他CPU上,性能有一定的提高。

另外一种使用绑核考虑就是将重要的业务进程隔离开,对于部分实时进程调度优先级高,可以将其绑定到一个指定核上,既可以保证实时进程的调度,也可以避免其他CPU上进程被该实时进程干扰。

1.CPU亲和性在用户态的使用

linux的CPU亲和性在用户态表现为一个cpu_set_t掩码的形式,用户可以调用两个函数设置和获取掩码:

#define _GNU_SOURCE /* See feature_test_macros(7) */ 
#include
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)
;

sched_setaffinity是设置指定pid亲和性掩码的,mask是传入的参数;sched_getaffinity则是获取指定pid亲和性掩码的,mask是获取的参数。

cpusetsize可以通过sizeof cpu_set_t算出来。

cpu_set_t 是一个掩码数组,一共有1024位,每一位都可以对应一个cpu核心,以下宏,都是对这个掩码进行操作的。如果需要,一个进程是可以绑定多个cpu的。

void CPU_ZERO(cpu_set_t set); 
void CPU_SET(int cpu, cpu_set_t *set);
void CPU_CLR(int cpu, cpu_set_t *set);

而mask的表现是如此的:如果是0X23,转换成二进制则为00100011,则表明进程绑定在0核、1核和5核上。

绑核需要注意是,子进程会继承父进程的绑核关系。

代码实例:

#define _GNU_SOURCE 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <pthread.h>
#include <sys/syscall.h>
#define gettid() syscall(__NR_gettid)
void *test_thread(void *arg)
{
cpu_set_t mask;
int loop = 0;
int cpu_num = 0;
cpu_num = sysconf(_SC_NPROCESSORS_CONF);
pthread_detach(pthread_self());
CPU_ZERO(&mask);
CPU_SET(1, &mask);
if(sched_setaffinity(0, sizeof(mask), &mask) == -1)
{
printf("set affinity failedn");
}
while(1)
{
CPU_ZERO(&mask);
if(sched_getaffinity(0, sizeof(mask), &mask) == -1)
{
printf("get failedn");
}
for(loop = 0; loop < cpu_num; loop++)
{
if(CPU_ISSET(loop, &mask))
{
printf("test thread %lu run on processor %dn",
gettid(), loop);
}
}
sleep(1);
}
}
void *child_thread(void *arg)
{
cpu_set_t mask;
int loop = 0;
int cpu_num = 0;
cpu_num = sysconf(_SC_NPROCESSORS_CONF);
pthread_detach(pthread_self());
while(1)
{
CPU_ZERO(&mask);
if(sched_getaffinity(0, sizeof(mask), &mask) == -1)
{
printf("get failedn");
}
for(loop = 0; loop < cpu_num; loop++)
{
if(CPU_ISSET(loop, &mask))
{
printf("child thread %lu run on processor %dn",
gettid(), loop);
}
}
sleep(1);
}
}
int main(int argc, char *argv[])
{
int cpu_num = 0;
pthread_t thread;
int cpuid = 0;
int ret = 0;
int loop = 0;
cpu_set_t mask_set;
cpu_set_t mask_get;
if(argc != 2)
{
printf("usage:cpu numn");
return -1;
}
cpuid = atoi(argv[1]);
/* 获取系统CPU的个数 */
cpu_num = sysconf(_SC_NPROCESSORS_CONF);
printf("system has %i processor.n", cpu_num);
/* 初始化mask_set */
CPU_ZERO(&mask_set);
CPU_SET(cpuid, &mask_set);
if(sched_setaffinity(0, sizeof(mask_set), &mask_set) == -1)
{
printf("Warning:set cpu %d affinity failedn", cpuid);
}
ret = pthread_create(&thread, NULL, child_thread, NULL);
if(ret)
{
printf("Error:pthread_create failedn");
return -1;
}
ret = pthread_create(&thread, NULL, test_thread, NULL);
if(ret)
{
printf("Error:pthread_create failedn");
return -1;
}
while(1)
{
CPU_ZERO(&mask_get);
if(sched_getaffinity(0, sizeof(mask_get), &mask_get) == -1)
{
printf("Warning:get cpu %d affinity failedn", cpuid);
}
for(loop = 0; loop < cpu_num; loop++)
{
if(CPU_ISSET(loop, &mask_get))
{
printf("this processor %lu is running on processor:
%dn"
, gettid(), loop);
}
}
sleep(1);
}
return 0;
}

执行之后根据打印和/proc stat的内容可以判断,status有

Cpus_allowed: 08
Cpus_allowed_list: 3

可以更清楚的看到进程绑核状态

但是如果进程已经在运行过程中,用户不能直接改动代码,就用taskset工具更改CPU亲和性关系。

taskset [options] -p [mask] pid

其中mask前面已说了,参看man手册更详细一点。

二、CPU亲和性在内核态机制

在内核进程结构体task_struct里面有一个参数,即为

cpumask_t cpus_allowed;

用来记住CPU的绑核关系。

内核尤其是调度的时候,可以保证让task不会被调度到其他CPU上

static inline 
int select_task_rq(struct task_struct *p, int sd_flags, int wake_flags)
{
int cpu = p->sched_class->select_task_rq(p, sd_flags, wake_flags);
/*
* In order not to call set_task_cpu() on a blocking task we need
* to rely on ttwu() to place the task on a valid ->cpus_allowed
* cpu.
*
* Since this is common to all placement strategies, this lives here.
*
* [ this allows ->select_task() to simply return task_cpu(p) and
* not worry about this generic constraint ]
*/

if (unlikely(!cpumask_test_cpu(cpu, &p->cpus_allowed) ||
!cpu_online(cpu)))
cpu = select_fallback_rq(task_cpu(p), p);
return cpu;
}

进程在选择CPU队列的时候,只选择被允许的CPU队列,使用cpumask_test_cpu进行测试。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值