说明

线程的CPU相似性掩码确定了可以在其上运行的CPU的集合。在多处理器系统上,可以使用设置CPU亲和力掩码来获得性能优势。例如,通过将一个CPU专用于特定线程(即,将该线程的亲和力掩码设置为指定一个CPU,并设置所有其他线程的亲和力掩码以排除该CPU),可以确保最高执行速度对于那个线程。限制线程在单个CPU上运行还可以避免由于缓存失效而导致的性能损失,当线程停止在一个CPU上执行,然后重新开始在另一个CPU上执行时,缓存无效。

CPU亲和力掩码由cpu_set_t结构表示,即掩码所指向的" CPU集"。 CPU_SET(3)中介绍了一组用于操纵CPU集的宏。

sched_setaffinity()将ID为pid的线程的CPU亲和力掩码设置为mask指定的值。如果pid为零,则使用调用线程。参数cpusetsize是mask指向的数据的长度(以字节为单位)。通常,此参数将指定为sizeof(cpu_set_t)。

如果由pid指定的线程当前未在mask中指定的CPU之一上运行,则该线程将迁移到mask中指定的CPU之一。

sched_getaffinity()将ID为pid的线程的亲和力掩码写入mask所指向的cpu_set_t结构中。 cpusetsize参数指定掩码的大小(以字节为单位)。如果pid为零,则返回调用线程的掩码。

示例

下面的程序创建一个子进程。然后,父级和子级各自将其分配给指定的CPU,并执行相同的循环,这会占用一些CPU时间。在终止之前,父母等待孩子完成。该程序采用三个命令行参数:父代的CPU编号,子代的CPU编号以及两个进程都应执行的循环迭代次数。

如下面的示例所示,运行程序时消耗的实际时间和CPU时间将取决于内核内的缓存效果以及进程是否使用相同的CPU。

我们首先使用lscpu(1)来确定此(x86)系统具有两个内核,每个内核具有两个CPU:

$ lscpu | egrep -i 'core.*:|socket'
Thread(s) per core:    2
Core(s) per socket:    2
Socket(s):             1

然后,针对三种情况,对示例程序的操作进行计时:两个进程都在同一CPU上运行;两个进程都在同一内核的不同CPU上运行;两个进程都在不同内核上的不同CPU上运行。

$ time -p ./a.out 0 0 100000000
real 14.75
user 3.02
sys 11.73
$ time -p ./a.out 0 1 100000000
real 11.52
user 3.98
sys 19.06
$ time -p ./a.out 0 3 100000000
real 7.89
user 3.29
sys 12.07

Program source

#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)

int
main(int argc, char *argv[])
{
    cpu_set_t set;
    int parentCPU, childCPU;
    int nloops, j;

    if (argc != 4) {
        fprintf(stderr, "Usage: %s parent-cpu child-cpu num-loops\n",
                argv[0]);
        exit(EXIT_FAILURE);
    }

    parentCPU = atoi(argv[1]);
    childCPU = atoi(argv[2]);
    nloops = atoi(argv[3]);

    CPU_ZERO(&set);

    switch (fork()) {
    case -1:            /* Error */
        errExit("fork");

    case 0:             /* Child */
        CPU_SET(childCPU, &set);

        if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
            errExit("sched_setaffinity");

        for (j = 0; j < nloops; j++)
            getppid();

        exit(EXIT_SUCCESS);

    default:            /* Parent */
        CPU_SET(parentCPU, &set);

        if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
            errExit("sched_setaffinity");

        for (j = 0; j < nloops; j++)
            getppid();

        wait(NULL);     /* Wait for child to terminate */
        exit(EXIT_SUCCESS);
    }
}

返回值

成功时,sched_setaffinity()和sched_getaffinity()返回0(但请参见下面的" C库/内核差异",这说明基础sched_getaffinity()的返回值有所不同)。如果出错,则返回-1,并正确设置errno。

另外参见

lscpu(1),nproc(1),taskset(1),clone(2),getcpu(2),getpriority(2),gettid(2),nice(2),sched_get_priority_max(2),sched_get_priority_min(2), sched_getscheduler(2),sched_setscheduler(2),setpriority(2),CPU_SET(3),get_nprocs(3),pthread_setaffinity_np(3),sched_getcpu(3),功能(7),cpuset(7),sched(7), numactl(8)

错误说明

EFAULT
提供的内存地址无效。
EINVAL
相似性位掩码掩码不包含当前在系统上物理上并根据cpuset cgroups或cpuset(7)中描述的" cpuset"机制可能施加的任何限制而允许线程使用的处理器。
EINVAL
(sched_getaffinity(),在2.6.9之前的内核中为sched_setaffinity())cpusetsize小于内核使用的相似性掩码的大小。
EPERM
(sched_setaffinity())调用线程没有适当的特权。调用方需要一个有效用户ID,该用户ID必须等于pid标识的线程的实际用户ID或有效用户ID,或者它必须在线程pid的用户名称空间中具有CAP_SYS_NICE能力。
ESRCH
找不到ID为pid的线程。

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/

名称

sched_setaffinity,sched_getaffinity-设置并获取线程的CPU亲和力掩码

备注

调用sched_setaffinity()之后,线程将在其上实际运行的CPU组是mask参数中指定的组与系统上实际存在的CPU组的交集。如果正在使用cpuset(7)中描述的" cpuset"机制,则系统可能进一步限制线程在其上运行的CPU的集合。这些对线程将在其上实际运行的CPU的实际限制是由内核默默施加的。

确定系统上可用CPU数量的方法有多种,包括:检查/ proc / cpuinfo的内容;使用sysconf(3)获得_SC_NPROCESSORS_CONF和_SC_NPROCESSORS_ONLN参数的值;并检查/ sys / devices / system / cpu /下的CPU目录列表。

sched(7)描述了Linux调度方案。

相似性掩码是每个线程的属性,可以针对线程组中的每个线程分别进行调整。调用gettid(2)返回的值可以在pid参数中传递。将pid指定为0将为调用线程设置属性,并将从调用返回的值传递给getpid(2)将为线程组的主线程设置属性。 (如果使用的是POSIX线程API,请使用pthread_setaffinity_np(3)而不是sched_setaffinity()。)

isolcpus引导选项可用于在引导时隔离一个或多个CPU,这样就不会在这些CPU上调度任何进程。使用此引导选项后,将进程调度到隔离的CPU上的唯一方法是通过sched_setaffinity()或cpuset(7)机制。有关更多信息,请参见内核源文件Documentation / admin-guide / kernel-parameters.txt。如该文件中所述,isolcpus是隔离CPU的首选机制(相对于手动设置系统上所有进程的CPU亲和力的替代方法)。

通过fork(2)创建的子级继承其父级的CPU关联掩码。亲和力掩码在execve(2)中保留。

C library/kernel differences

本手册页介绍了CPU相似性调用的glibc接口。实际的系统调用接口略有不同,掩码被键入为unsigned long *,反映出CPU集的基础实现是一个简单的位掩码的事实。

成功后,原始sched_getaffinity()系统调用将返回复制到掩码缓冲区的已放置字节数;这将是cpusetsize和内核内部用于表示CPU设置位掩码的cpumask_t数据类型的大小(以字节为单位)的最小值。

Handling systems with large CPU affinity masks

底层系统调用(将CPU掩码表示为unsigned long *类型的位掩码)不对CPU掩码的大小施加任何限制。但是,glibc使用的cpu_set_t数据类型的固定大小为128字节,这意味着可以表示的最大CPU数量为1023。如果内核CPU亲和力掩码大于1024,则调用形式为:

sched_getaffinity(pid,sizeof(cpu_set_t),&mask);

由于错误EINVAL失败,对于cpusetsize中指定的掩码大小小于内核使用的亲和力掩码的大小的情况,底层系统调用会产生错误。 (取决于系统CPU拓扑,内核相似性掩码可能会大大大于系统中活动CPU的数量。)

在具有较大内核CPU亲和力掩码的系统上工作时,必须动态分配mask参数(请参阅CPU_ALLOC(3))。当前,执行此操作的唯一方法是使用sched_getaffinity()调用,并使用增加的掩码大小来探查所需掩码的大小(直到调用不会因错误EINVAL而失败)。

请注意,CPU_ALLOC(3)可能会分配比请求稍大的CPU集(因为将CPU集实现为以sizeof(long)为单位分配的位掩码)。因此,sched_getaffinity()可以设置超出请求的分配大小的位,因为内核会看到一些额外的位。因此,调用方应遍历返回集中的位,对已设置的位进行计数,并在达到CPU_COUNT(3)返回的值时停止(而不是遍历请求分配的位数)。

遵循规范

这些系统调用是特定于Linux的。

SCHED_SETAFFINITY - Linux手册页

Linux程序员手册 第2部分
更新日期: 2020-06-09

版本

CPU亲和力系统调用是在Linux内核2.5.8中引入的。系统调用包装器是在glibc 2.3中引入的。最初,glibc接口包含一个cpusetsize参数,类型为unsigned int。在glibc 2.3.3中,删除了cpusetsize参数,但随后在glibc 2.3.4中使用size_t类型对其进行了恢复。

语法

#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <sched.h>

int sched_setaffinity(pid_t pid, size_t cpusetsize,
                      const cpu_set_t *mask);

int sched_getaffinity(pid_t pid, size_t cpusetsize,
                      cpu_set_t *mask);
日期:2019-08-20 17:59:18 来源:oir作者:oir