C 言語で sched_setaffinity 関数を使用する
この記事では、C 言語で sched_setaffinity
関数を使用する方法のいくつかの方法について説明します。
sched_setaffinity
関数を使用して、プロセスの実行を特定の CPU に制限する
今日、マルチコアハードウェアはユビキタスであり、オペレーティングシステムはこれらのコアで同時に実行される複数のプロセスを管理する必要があります。プロセス/スレッドの実行の管理を処理するオペレーティングシステムの部分は、スケジューラと呼ばれます。スケジューラーは、既存のすべてのプロセス/スレッドを使用可能なコア全体に効率的に分散し、それに応じてタイムスライスを割り当てようとします。スケジューリングは、特定のシステムの主要なパフォーマンス保証であるため、オペレーティングシステムで最も難しい設計上の問題の 1つです。スケジューラと対話するための標準の C インターフェイスはありませんが、特定の OS は、いくつかのプロセススケジューリングパラメータを変更するためのシステムコールを提供します。
sched_setaffinity
は GNUC ライブラリの一部であり、ほとんどが Linux 固有の機能に基づいています。この関数は、プロセスが実行に適格である CPU コアのセットを示すいわゆる CPU アフィニティマスクを設定します。sched_setaffinity
は最初の引数として PID 値を取り、2 番目の引数として sizeof(cpu_set_t)
を取ります。3 番目の引数はタイプ cpu_set_t
であり、これは不透明な構造であり、<sched.h>
ヘッダーの事前定義されたマクロを使用して操作する必要があります。ただし、これらの関数とマクロを使用できるようにするには、_GNU_SOURCE
マクロを定義する必要があることに注意してください。次の例では、ユーザーから 3つの整数をコマンドライン引数として受け取り、それらを格納して、それぞれ親/子プロセスの 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
を使用して子を待機します。実証された動作を観察したい場合は、Linux システムで広く利用可能な htop
コマンドラインユーティリティを使用してシステムプロセスを監視できます。
#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);
}
}