头图

前言

这段时间在跑一个业务性能时,发现很是不稳定。根据多任务操作系统的基本工作原理 我们可知现代操作系统支持多任务处理,这意味着多个程序或线程可以同时运行。操作系统会分配处理器时间片给不同的任务,使它们轮流执行。这种切换允许多个任务在同一台计算机上共享CPU资源,一个任务在不同的CPU上运行。在任务切换中,CPU会处理各种中断,例如硬件中断(设备IO、定时器等)和软件中断(系统调用、异常等)。这些中断会导致CPU从当前任务切换到中断处理程序,然后再返回到之前的任务。这些都无疑给CPU增加了负担。

CPU亲和性

CPU的亲和性, 就是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,在多核运行的机器上,每个CPU本身自己会有缓存,缓存着进程使用的信息,而进程可能会被OS调度到其他CPU上,如此,CPU cache命中率就低了,当绑定CPU后,程序就会一直在指定的cpu跑,不会由操作系统调度到其他CPU上,性能有一定的提高。
另外一种使用绑核考虑就是将重要的业务进程隔离开,对于部分实时进程调度优先级高,可以将其绑定到一个指定核上,既可以保证实时进程的调度,也可以避免其他CPU上进程被该实时进程干扰。

CPU核数查看

在linux系统中,可以通过shell命令获取当前系统的核数:

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

lscpu

nproc

C程序中获取:

// 内核中
pr_info("Cpu count %d", NR_CPUS);

//用户态

int sysconf(_SC_NPROCESSORS_CONF);/* 返回系统可以使用的核数,但是其值会包括系统中禁用的核的数目,因此该值并不代表当前系统中可用的核数 */
int sysconf(_SC_NPROCESSORS_ONLN);/* 返回值真正的代表了系统当前可用的核数 */

/* 以下两个函数与上述类似 */

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

CPU绑定

LINUx命令行工具

taskset 用于设置或查看进程的CPU亲和性,即限制程序运行的CPU核心。通过 taskset 命令,您可以将一个正在运行的进程或启动一个新进程绑定到指定的CPU核心上。

  • 将进程绑定到指定的CPU核心上:
    taskset -c <CPU列表> <命令>

    • cpu列表 是一个逗号分隔的CPU核心列表,例如:0、0,1、0-3。这个列表指定了要绑定到的CPU核心。
    • 命令 是要执行的命令或程序。该命令将在指定的CPU核心上运行。
  • 查看进程的CPU亲和性设置:
    taskset -p <进程ID>
  • 将一个已经运行的进程重新绑定到不同的CPU核心上:
    taskset -cp <CPU列表> <进程ID>

taskset 命令需要在具有足够权限的情况下运行(通常需要使用sudo或作为超级用户运行)。使用 taskset 可以帮助优化多核处理器上的任务分配,但需要小心使用,以避免过度绑定或影响系统性能。

用户态

设置获取进程的进程的CPU亲和性
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <unistd.h>

int main() {
    // 获取当前进程ID
    pid_t pid = getpid();
    
    // 获取系统中的CPU核心数量
    int numCores = sysconf(_SC_NPROCESSORS_ONLN);

    if (numCores == -1) {
        perror("sysconf");
        return 1;
    }

    printf("系统中CPU核心数量: %d\n", numCores);

    // 创建一个CPU集合并将进程绑定到其中一个CPU核心
    cpu_set_t mask;
    CPU_ZERO(&mask);
    CPU_SET(0, &mask); // 将进程绑定到第一个CPU核心

    if (sched_setaffinity(pid, sizeof(mask), &mask) == -1) {
        perror("sched_setaffinity");
        return 1;
    }

    printf("已将进程绑定到CPU核心 0\n");

    // 获取进程的CPU亲和性设置
    cpu_set_t affinityMask;
    CPU_ZERO(&affinityMask);

    if (sched_getaffinity(pid, sizeof(affinityMask), &affinityMask) == -1) {
        perror("sched_getaffinity");
        return 1;
    }

    printf("进程的CPU亲和性设置:");
    for (int i = 0; i < numCores; i++) {
        if (CPU_ISSET(i, &affinityMask)) {
            printf("%d ", i);
        }
    }
    printf("\n");

    return 0;
}
设置获取线程的CPU亲和性
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sched.h>

// 线程函数,用于打印线程所在的 CPU 核心编号
void *threadFunction(void *arg) {
    int coreId = sched_getcpu(); // 获取当前线程所在的 CPU 核心编号
    printf("线程在 CPU 核心 %d 上运行\n", coreId);

    // 获取线程的CPU亲和性设置
    cpu_set_t affinityMask;
    CPU_ZERO(&affinityMask);

    if (pthread_getaffinity_np(pthread_self(), sizeof(affinityMask), &affinityMask) == -1) {
        perror("pthread_getaffinity_np");
        return NULL;
    }

    printf("线程的CPU亲和性设置:");
    for (int i = 0; i < CPU_SETSIZE; i++) {
        if (CPU_ISSET(i, &affinityMask)) {
            printf("%d ", i);
        }
    }
    printf("\n");

    return NULL;
}

int main() {
    // 创建两个线程
    pthread_t thread1, thread2;

    // 创建线程属性对象
    pthread_attr_t attr1, attr2;
    pthread_attr_init(&attr1);
    pthread_attr_init(&attr2);

    // 设置线程属性,将第一个线程绑定到 CPU 核心 0,将第二个线程绑定到 CPU 核心 1
    cpu_set_t cpuSet1, cpuSet2;
    CPU_ZERO(&cpuSet1);
    CPU_ZERO(&cpuSet2);
    CPU_SET(0, &cpuSet1);
    CPU_SET(1, &cpuSet2);
    pthread_attr_setaffinity_np(&attr1, sizeof(cpu_set_t), &cpuSet1);
    pthread_attr_setaffinity_np(&attr2, sizeof(cpu_set_t), &cpuSet2);

    // 创建并启动两个线程
    if (pthread_create(&thread1, &attr1, threadFunction, NULL) != 0) {
        perror("pthread_create");
        return 1;
    }
    if (pthread_create(&thread2, &attr2, threadFunction, NULL) != 0) {
        perror("pthread_create");
        return 1;
    }

    // 等待线程结束
    if (pthread_join(thread1, NULL) != 0) {
        perror("pthread_join");
        return 1;
    }
    if (pthread_join(thread2, NULL) != 0) {
        perror("pthread_join");
        return 1;
    }

    // 销毁线程属性对象
    pthread_attr_destroy(&attr1);
    pthread_attr_destroy(&attr2);

    return 0;
}

优点

CPU 亲和性是计算机领域的一个重要概念,用于指定处理器核心与特定任务或线程之间的关系。它的存在有以下几个主要原因:

  • 性能优化: CPU 亲和性允许操作系统或应用程序将特定任务绑定到特定的处理器核心上。这可以提高性能,因为每个核心都有自己的缓存和执行单元,因此任务之间不会相互干扰。
  • 降低资源争用: 在多核处理器系统中,如果不使用亲和性,多个线程可能会竞争同一个核心的资源,如缓存和执行单元。通过指定亲和性,可以减少这种资源争用,提高系统的整体效率。
  • 实时任务: 对于一些需要实时响应的任务,如嵌入式系统或科学计算,CPU 亲和性可以确保任务按时完成。通过将实时任务绑定到一个或多个核心,可以避免其他任务干扰其执行。
  • 能源效率: 在移动设备和笔记本电脑上,CPU 亲和性还可以用于管理功耗。通过将任务分配给特定核心,可以更好地控制功耗,延长电池寿命。

总之,CPU 亲和性是一种有益的技术,可以在多核处理器系统中优化性能、资源利用和能源效率。它允许系统管理员和开发人员更精细地控制任务的分配方式,以满足特定应用程序的需求。

欢迎关注个人博客沟通交流

NULL
30 声望0 粉丝