在 C 语言中使用 sched_setaffinity 函数
本文将介绍几种如何在 C 语言中使用 sched_setaffinity
函数的方法。
使用 sched_setaffinity
函数将进程执行限制在特定的 CPU 上
如今,多核硬件无处不在,操作系统需要管理在这些核上同时运行的多个进程。操作系统中负责管理进程/线程执行的部分称为调度程序。调度程序尝试在可用内核之间有效地分配所有现有进程/线程,并相应地分配时间片。调度是操作系统中最困难的设计问题之一,因为它是给定系统的主要性能保证。没有与调度程序交互的标准 C 接口,但是某些 OS 提供了系统调用来修改多个流程调度参数。
sched_setaffinity
是 GNU C 库的一部分,它主要基于特定于 Linux 的功能。该函数设置所谓的 CPU 亲和力掩码,它表示可以在其上执行进程的 CPU 内核集。sched_setaffinity
将 PID 值作为第一个参数,并将 sizeof(cpu_set_t)
作为第二个参数。第三个参数是 cpu_set_t 类型,它是一个不透明的结构,需要使用 <sched.h>
头文件中的预定义宏进行操作。但是请注意,应定义 _GNU_SOURCE
宏以使这些功能和宏可用。在下面的示例中,我们实现了一个程序,该程序将来自用户的三个整数用作命令行参数,并将其存储以分别表示父/子进程 CPU 编号和几次循环迭代。然后,使用 CPU_ZERO
宏清除 cpu_set_t
变量,并调用 fork
产生一个子进程。
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.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, wstatus;
long nloops;
if (argc != 4) {
fprintf(stderr, "Usage: %s parent-cpu child-cpu num-loops\n", argv[0]);
exit(EXIT_FAILURE);
}
parentCPU = strtol(argv[1], NULL, 0);
childCPU = strtol(argv[2], NULL, 0);
nloops = strtol(argv[3], NULL, 0);
CPU_ZERO(&set);
switch (fork()) {
case -1:
errExit("fork");
case 0:
CPU_SET(childCPU, &set);
if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
errExit("sched_setaffinity");
for (int j = 0; j < nloops; j++) getpid();
exit(EXIT_SUCCESS);
default:
CPU_SET(parentCPU, &set);
if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
errExit("sched_setaffinity");
for (int j = 0; j < nloops; j++) getpid();
wait(NULL);
exit(EXIT_SUCCESS);
}
}
使用 CPU_SET
宏指示 CPU 内核将进程绑定到
sched_setaffinity
函数是每个进程或线程调用的;因此,一旦 fork
返回,我们就可以为父进程和子进程指定不同的 CPU 掩码。CPU_SET
宏用于修改先前归零的 cpu_set_t
结构体,然后将其传递给 sched_setaffinity
调用。请注意,每个进程执行一个循环,在其中它们调用 getpid
来占用 CPU 资源并简化示例的演示。父进程在上一个示例中使用 wait
调用来等待子级,在下一个示例中使用 waitpid
来等待子级。如果你想观察所演示的行为,则可以使用 htop
命令行实用程序来观察系统进程,该实用程序在 Linux 系统上广泛使用。
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.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, wstatus;
long nloops;
if (argc != 4) {
fprintf(stderr, "Usage: %s parent-cpu child-cpu num-loops\n", argv[0]);
exit(EXIT_FAILURE);
}
parentCPU = strtol(argv[1], NULL, 0);
childCPU = strtol(argv[2], NULL, 0);
nloops = strtol(argv[3], NULL, 0);
CPU_ZERO(&set);
pid_t c_pid = fork();
if (c_pid == -1) errExit("fork");
switch (c_pid) {
case -1:
errExit("fork");
case 0:
CPU_SET(childCPU, &set);
if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
errExit("sched_setaffinity");
for (int j = 0; j < nloops; j++) getpid();
exit(EXIT_SUCCESS);
default:
CPU_SET(parentCPU, &set);
if (sched_setaffinity(getpid(), sizeof(set), &set) == -1)
errExit("sched_setaffinity");
for (int j = 0; j < nloops; j++) getpid();
if (waitpid(c_pid, &wstatus, WUNTRACED | WCONTINUED) == -1)
errExit("waitpid");
exit(EXIT_SUCCESS);
}
}