前言

提示:这里可以添加本文要记录的大概内容:

CPU绑定指的是在多核CPU的系统中将进程或线程绑定到指定的CPU核上去执行。在Linux中,我们可以利用CPU affinity属性把进程绑定到一个或多个CPU核上。

CPU Affinity是进程的一个属性,这个属性指明了进程调度器能够把这个进程调度到哪些CPU上。该属性要求进程在某个指定的CPU上尽量长时间地运行而不被迁移到其他处理器。

CPU Affinity分为2种:soft affinity和hard affinity。soft affinity只是一个建议,如果不可避免,调度器还是会把进程调度到其它的CPU上去执行;hard affinity则是调度器必须遵守的规则, 2.6以上版本的Linux内核可以让开发人员可以编程实现hard affinity


一、进程绑核C语言实现

#include <sched.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
 
int main(int argc, char *argv[])
{
    cpu_set_t set;
    int parentCPU, childCPU;
    int j;
    int cpu_num = -1;
    if (argc != 3) {
        fprintf(stderr, "Usage: %s parent-cpu child-cpu\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    parentCPU = atoi(argv[1]);
    childCPU = atoi(argv[2]);
    CPU_ZERO(&set);
    
    switch (fork()) {
        case -1: { /* Error */
            fprintf(stderr, "fork error\n");
            exit(EXIT_FAILURE);
        }
        case 0: { /* Child */
            CPU_SET(childCPU, &set);
            if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) {
                fprintf(stderr, "child sched_setaffinity error\n");
                exit(EXIT_FAILURE);
            }
            sleep(1);
            if (-1 != (cpu_num = sched_getcpu())) {
                fprintf(stdout, "The child process is running on cpu %d, the process id is %d\n", cpu_num, getpid());
            }
            usleep(1000 * 1000 * 60);
            exit(EXIT_SUCCESS);
        }
        default: { /* Parent */
            CPU_SET(parentCPU, &set);
            if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) {
                fprintf(stderr, "parent sched_setaffinity error\n");
                exit(EXIT_FAILURE);
            }
            if (-1 != (cpu_num = sched_getcpu())) {
                fprintf(stdout, "The parent process is running on cpu %d, the process id is %d\n", cpu_num, getpid());
            }
	    printf("Wait for child to terminate \n");
            wait(NULL); /* Wait for child to terminate */
            exit(EXIT_SUCCESS);
        }
    }
    return 0;
}

二、线程绑核C语言实现

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>

#define handle_error_en(en, msg) \
	do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
int cpu_num;

struct thread_info {
	pthread_t thread_id;
	int thread_num;
};
 
static void *thread_start(void *arg) {
    struct thread_info *tinfo = (struct thread_info *)arg;
    pthread_t thread = tinfo->thread_id;
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(tinfo->thread_num, &cpuset);
    int s = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
    if (s != 0) {
        handle_error_en(s, "pthread_setaffinity_np");
    } 
    CPU_ZERO(&cpuset);
    s = pthread_getaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
    if (s != 0) {
        handle_error_en(s, "pthread_getaffinity_np");
    }
    
    for (int j = 0; j < cpu_num; j++) {
        if (CPU_ISSET(j, &cpuset)) { //如果当前线程运行在CPU j上,则输出信息
            printf(" thread %d is running on cpu %d\n", tinfo->thread_num, j);
        }
    }
    pthread_exit(NULL);
}
 
int main(int argc, char *argv[])
{
    cpu_num = sysconf(_SC_NPROCESSORS_CONF); //获取系统的CPU数量
    struct thread_info *tinfo = (struct thread_info*)calloc(cpu_num, sizeof(struct thread_info));
    
    if (tinfo == NULL) {
        handle_error_en(0, "calloc");
    }
    
    for (int j = 0; j < cpu_num; j++) { //有多少个CPU就创建多少个线程
        tinfo[j].thread_num = j;
        int s = pthread_create(&tinfo[j].thread_id, NULL, thread_start, &tinfo[j]);
        if (s != 0) {
            handle_error_en(s, "pthread_create");
        }
    }
    
    for (int j = 0; j < cpu_num; j++) {
        int s = pthread_join(tinfo[j].thread_id, NULL);
        if (s != 0) {
            handle_error_en(s, "pthread_join");
        }
    }
    return 0;
}


三、 shell命令绑核

任务集 (taskset) 是一个 Linux 命令,用于设置进程的 CPU 亲和度 (affinity),它可以将一个进程绑定到某个特定的 CPU 核心或一组 CPU 核心。

下面是 taskset 命令的具体使用:

  1. 绑定一个进程到单个 CPU 核心:
taskset -c <core_id> <command>

其中,<core_id> 是一个 CPU 核心的编号,从 0 开始; 是要执行的命令。
例如,要将进程绑定到第 1 个 CPU 核心上,可以使用以下命令:

taskset -c 0 command
  1. 绑定一个进程到多个 CPU 核心:
taskset -c <core_id_list> <command>

其中,<core_id_list> 是一个 CPU 核心编号的列表,用逗号分隔; 是要执行的命令。

例如,要将进程绑定到第 1、2、4 个 CPU 核心上,可以使用以下命令:

taskset -c 0,1,3 command
  1. 显示一个进程当前的 CPU 亲和度:
taskset -p <pid>

其中, pid是进程的 ID。

例如:

taskset -p 12345
  1. 修改一个正在运行的进程的 CPU 亲和度:
taskset -cp <core_id_list> <pid>

其中,<core_id_list> 是一个 CPU 核心编号的列表,用逗号分隔; 是正在运行的进程的 ID。

例如,要将进程 12345 绑定到第 1、2、4 个 CPU 核心上,可以使用以下命令:

taskset -cp 0,1,3 12345
# 查看某个进程在哪个cpu 上运行
ps -eLo ruser,pid,ppid,psr,args

# 查看cpu个数
cat /proc/cpuinfo |grep -c processor

# 查看进程所使用的CPU
taskset -cp  25718

# 调整进程所使用的CPU
taskset -p  25718
taskset -p  25718
taskset -c 1  25718

# 使用-p选项指定需要查询的进程号,默认打印的是一个十六进制数,如果使用-cp选项打印的是一个cpu列表,表示相应的cpu核

参考文档
如何将进程、线程与CPU核进行绑定
Linux top 查看CPU、内存、线程等信息

Logo

更多推荐