linux c进程调度-CPU亲和力

2018-06-14 11:50:07

进程切换 CPU 时对性能会有一定的影响:如果在原来的 CPU 的高速缓冲器中存在进程的数据,那么为了将进程的一行数据加载进新 CPU 的高速缓冲器中,首先必须使这行数据失效(即在没被修改的情况下丢弃数据,在被修改的情况下将数据写入内存)。(为防止高速缓冲器不一致,多处理器架构在某个时刻只允许数据被存放在一个 CPU 的高速缓冲器中。)这个使数据失效的过程会消耗时间。由于存在这个性能影响, Linux 2.6 内核尝试了给进程保证软 CPU 亲和力--在条件允许的情况下进程重新被调度到原来的 CPU 上运行。

在多处理系统上,有时候需要为进程设置CPU亲和力,这样就能显式的将其限制在可用的CPU中的一个或一组CPU上运行。之所以这样做,原因如下:

1.可以避免由于高速缓存中的数据失效所带来的性能的影响。
2.如果多个线程或进程访问同样的数据,那么将他们限制在同样的CPU上的话可能会带来性能的提升。因为他们无需竞争数据并且也不存在由此而产生的高速缓冲区未命中。
3.对于时间关键的应用程序来讲,可能需要为此应用程序预留一个或更多的CPU,而将系统上大多数CPU限制在其他CPU上。

设置CPU亲和力

#define _GNU_SOURCE
#include <sched.h>

int sched_setaffinity(pid_t pid, size_t len, cpu_set_t *set);

//Returns 0 on success, or –1 on error

实际上CPU亲和力是一个线程级特性,如果想要设定多线程进程中某个特定线程的CPU亲和力的话,可以将pid设定为线程中 gettid() 返回值,将 pid设为0代表当前线程。gettid()是内核给线程(轻量级进程)分配的进程id,全局(所有进程中)唯一。

set 为一个位掩码,操作可以使用下面的宏。

#define _GNU_SOURCE
#include <sched.h>
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);
int CPU_ISSET(int cpu, cpu_set_t *set);

//Returns true (1) if cpu is in set, or false (0) otherwise

CPU_ZERO 将set初始化为空。
CPU_SET 将 cpu添加到 set中。
CPU_CLR 从 set中移除cpu。
CPU_ISSET cpu是set的一个成员时返回1。

CPU集合从0开始编号。

例子

#define _GNU_SOURCE
#include <sched.h>
#include <sys/types.h>  /* Type definitions used by many programs */
#include <stdio.h>      /* Standard I/O functions */
#include <stdlib.h>
#include <unistd.h>     /* Prototypes for many system calls */
#include <errno.h>      /* Declares errno and defines error constants */
#include <string.h>     /* Commonly used string-handling functions */

#include <pthread.h>

static long a;

static void * threadFunc(void *arg){
    long b;
    long i = 10000000000;
    long j = 0;
    for(; j < i; j++){
        b = j;
    }
}

int main(int argc, char *argv[]){
    pid_t pid;
    cpu_set_t set;
    int cpu;
    unsigned long mask;

    if (argc != 3 || strcmp(argv[1], "--help") == 0){
        printf("%s pid mask\n", argv[0]);
        return 0;
    }
        

    pid = atoi(argv[1]);
    //mask = atoi(argv[2], GN_ANY_BASE, "octal-mask");
    mask = strtoul(argv[2], NULL, 2);

    CPU_ZERO(&set);

    for (cpu = 0; mask > 0; cpu++, mask >>= 1)
        if (mask & 1){
            CPU_SET(cpu, &set);
            printf("%d\n", cpu);
        }
            

    if (sched_setaffinity(pid, sizeof(set), &set) == -1)
        perror("sched_setaffinity");

    pthread_t t1;
    void *res;
    int s;

    s = pthread_create(&t1, NULL, threadFunc, "Hello world\n");
    if (s != 0)
        perror("pthread_create");

    s = pthread_detach(t1);
    if (s != 0)
        perror("pthread_detach");

    long i = 10000000000;
    long j = 0;
    for(; j < i; j++){
        a = j;
    }
    
    exit(EXIT_SUCCESS);
}
#限制在第2,3cpu上
[root@localhost c]# ./a.out 0 0110
Tasks: 145 total,   2 running, 143 sleeping,   0 stopped,   0 zombie
%Cpu0  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu2  :100.0 us,  0.0 sy,  0.0 ni,  0.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


获取CPU亲和力

#define _GNU_SOURCE
#include <sched.h>

int sched_getaffinity(pid_t pid, size_t len, cpu_set_t *set);

//Returns 0 on success, or –1 on error

例子

#define _GNU_SOURCE
#include <sched.h>
#include <sys/types.h>  /* Type definitions used by many programs */
#include <stdio.h>      /* Standard I/O functions */
#include <stdlib.h>     /* Prototypes of commonly used library functions,
                           plus EXIT_SUCCESS and EXIT_FAILURE constants */
#include <unistd.h>     /* Prototypes for many system calls */
#include <errno.h>      /* Declares errno and defines error constants */
#include <string.h>     /* Commonly used string-handling functions */

int main(int argc, char *argv[]){
    pid_t pid;
    cpu_set_t set;
    size_t s;
    int cpu;

    if (argc != 2 || strcmp(argv[1], "--help") == 0){
        printf("%s pid\n", argv[0]);
        return 0;
    }

    pid = atoi(argv[1]);

    s = sched_getaffinity(pid, sizeof(cpu_set_t), &set);
    if (s == -1)
        perror("sched_getaffinity");

    printf("CPUs:");
    //这里的 CPU_SETSIZE 是一个比cpu_set_t能代表的cpu数还要大的常量
    //为1024,在这也可以把CPU_SETSIZE替换成当前系统最大cpu数
    //比如我的系统是4核的,可以写成这样for (cpu = 0; cpu < 4; cpu++)
    for (cpu = 0; cpu < CPU_SETSIZE; cpu++)
        if (CPU_ISSET(cpu, &set))
            printf(" %d", cpu);
    printf("\n");

    exit(EXIT_SUCCESS);
}
[root@localhost c]# ./a.out 0
CPUs: 0 1 2 3


备注
1.编译器版本gcc4.8,运行环境centos7 64位
2.原文地址http://www.freecls.com/a/2712/57  

©著作权归作者所有
收藏
推荐阅读
简介
天降大任于斯人也,必先苦其心志。