関数仕様
書式
1 2 3 4 5 6 7 8 9 10 |
#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); void CPU_CLR(int cpu, cpu_set_t *set); int CPU_ISSET(int cpu, cpu_set_t *set); void CPU_SET(int cpu, cpu_set_t *set); void CPU_ZERO(cpu_set_t *set); |
引数
pid | プロセスの PID、スレッドの thread id、または 0(呼び出し元スレッド) |
cpusetsize | mask が指すデータ長 (バイト)。通常は sizeof(cpu_set_t) を指定 |
mask | CPU affinity を指定する mask(CPUコア番号の集合) |
cpu | CPU のコア番号 |
set | mask をセット・チェックする cpu_set_t へのポインタ |
戻り値
0 | 正常に完了 |
-1 | エラーが発生(errno にエラー番号がセットされています) |
代表的なエラー
- EPERM
呼び出し元のスレッドに適切な権限がない - ESRCH
pid が指すスレッドが見つからない
機能
- sched_setaffinity
スレッドの実行を許可する CPU affinity(親和度)マスクを設定する - sched_getaffinity
スレッドに設定されている CPU affinity マスクを取得する
メリット
- スレッドの実行速度を最大化
特定のスレッドを1つのCPUコアに割り当てることにより、
確実にそのスレッドの実行速度を最大にすることができます - CPUのキャッシュ無効化を抑止
実行したスレッドが前回と異なる CPU で実行される場合、キャッシュが無効化されます
実行する CPU を固定とすることで、キャッシュ無効化による性能劣化を防げます
CPU affinity マスクは「CPU 集合」を表す cpu_set_t で表現され、そのポインタで mask を指定します。
CPU 集合を操作するために、CPU_CLR, CPU_ISSET, CPU_SET, CPU_ZERO のマクロ群を使用します。

sched_setaffinity
スレッド ID が 0 の場合、呼び出し元スレッドに設定されます。
指定したスレッドが mask で指定された CPU のいずれかで現在実行されていない場合、
そのスレッドは mask で指定された CPU へ即座に移動されます。
sched_getaffinity
スレッド ID が pid のスレッドの affinity マスクを mask へ書き込みます。
注意すること
注意ポイント①
スレッドを特定の CPU に割り当て固定するだけでは、実行性能を最大化できない
sched_setaffinity の man ページにも記載されていますが、特定のスレッドの実行性能を最大化したい場合、
その他のスレッド全てに割り当てたい CPU 以外のマスクを設定する必要があります。
例えば、CPUコアが2つある場合で、スレッドA に CPU1 を固定的に割り当てたい場合、
その他の全てのスレッドに CPU0 を割り当てる必要があります。
これには、Kernel の割り込みスレッド、Kernel スレッドを含みます。
そうしないと、他のスレッドは CPU0 と CPU1 を自由に使える状態ですが、
スレッドA は CPU1 しか使えなくなり、かえって性能が悪くなってしまいます。

そのため、スレッドA に設定するだけでは、別のスレッドに簡単に割り込まれます。
注意ポイント②
スレッドの実行性能を最大化しても、リアルタイム性を保証できるわけではない
CPU を固定的に占有することで、リアルタイム性を保証できると誤解している方が多いようです。
そもそも Linux Kernel は、リアルタイムOSではありません。
そのため、リアルタイムOSと呼ばれる uITRON とは、根本的に作りが異なります。
uITRON は、タイマーの割り込みにより実行するタスクを切り替えるコンテキストスイッチを行います。
Linux は、アプリが システムコールを呼び出したりして Kernel 空間に実行が戻った際、
実行待ちの全スレッドを再スケジューリングして実行するスレッドを切り替えます。
shced_setaffinity でスレッドの実行性能を最大化したとしても、OS自体がリアルタイム性を保証しません。
割り込み処理、Buffer/Cache の確保・解放、Kernel スレッド処理、ユーザ空間での処理時間など、
様々な要因でリアルタイム制が損なわれます。
サンプルプログラム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#define _GNU_SOURCE #include <sched.h> #include <stdio.h> #include <unistd.h> int main(void) { cpu_set_t cur_mask, new_mask; size_t len = sizeof(cur_mask); pid_t tid = gettid(); /* 現在の設定値を取得・表示 */ if (sched_getaffinity(0, len, &cur_mask)) { perror("sched_getaffinity"); return -1; } printf("tid %d's old affinity: %08lx\n", tid, *(unsigned long*)cur_mask.__bits); /* mask を CPU1 のみに設定 */ CPU_ZERO(&new_mask); CPU_SET(1, &new_mask); /* mask 値を反映 */ if (sched_setaffinity(0, len, &new_mask)) { perror("sched_setaffinity"); return -1; } printf("tid %d's new affinity: %08lx\n", tid, *(unsigned long*)new_mask.__bits); return 0; } |
実行結果
tid 2467's old affinity: 0000000f
tid 2467's new affinity: 00000002
実行環境は 4 core CPU を搭載しているため、起動直後の mask は 0xF(全CPU許可)となっています。
CPU_SET の引数 cpu を変更して複数回呼び出すことで、マスクする CPU を範囲で指定可能です。