在 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 <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, 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 <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, 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);
}
}
Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.
LinkedIn