sched_setaffinity(2) を使って任意のプログラムを任意のCPU上で動かす
Linux 2.6 には sched_setaffinity(2) というシステムコールがあり、これを利用して任意のスレッドを(マルチCPU環境下で)特定の CPU で実行させることができます。http://www-06.ibm.com/jp/developerworks/linux/051028/j_l-affinity.shtml によるとリアルタイムプロセスでマネージャとなるスレッドをこのシステムコールで特定の CPU に固定する...といった応用が考えられるそうです。
へえ、と思ったのでちょっと遊んでみました。LD_PRELOAD を使って任意のプログラムを任意の CPU に固定して動かしてみます。GCC の __attribute__)((constructor))(
で sched_setaffinitiy(2) を呼びます。(参考: http://0xcc.net/blog/archives/000091.html)
#include <sched.h> #include <errno.h> #include <stdlib.h> #include <stdio.h> void __attribute__((constructor)) bind_cpu() { char *p = getenv("BIND_CPU_ID"); if (p == NULL) p = "0"; int cpu_id = atoi(p); cpu_set_t mask; __CPU_ZERO(&mask); __CPU_SET(cpu_id, &mask); if (sched_setaffinity(0, sizeof(mask), &mask) == -1) { perror("Failed to set CPU affinity"); exit(-1); } fprintf(stderr, "Bind main thread to CPU %d\n", cpu_id); }
これを
% gcc -shared -fPIC -o bind_cpu.so bind_cpu.c
と共有オブジェクトとしてコンパイルして
% BIND_CPU_ID=1 LD_PRELOAD=./bind_cpu.so hostname
とすると hostname が必ず CPU ID 1 (2個目の CPU (コア)) を使って動きます。
本当に指定した ID の CPU が使われているか試してみます。ビジーループする perl のワンライナーを上記のオブジェクトにリンクさせて動かします。
% BIND_CPU_ID=1 LD_PRELOAD=./bind_cpu.so perl -e 'while (1) { $i++ }'; Bind main thread to CPU 1
正しく CPU 1 に固定できていれば、ID 1 の CPU にだけ負荷がかかっているはずです。sar で確認してみます。
% sar -P ALL 1 1000 Linux 2.6.19.2-103.hatena.centos5 (kyobashira.hatena.ne.jp) 08/24/07 17:51:28 CPU %user %nice %system %iowait %steal %idle 17:51:29 all 50.00 0.00 0.00 0.00 0.00 50.00 17:51:29 0 0.00 0.00 0.00 0.00 0.00 100.00 17:51:29 1 100.00 0.00 0.00 0.00 0.00 0.00 17:51:29 CPU %user %nice %system %iowait %steal %idle 17:51:30 all 50.25 0.00 0.00 0.00 0.00 49.75 17:51:30 0 0.00 0.00 0.00 0.00 0.00 100.00 17:51:30 1 100.00 0.00 0.00 0.00 0.00 0.00
確かに CPU 1 だけ負荷が 100% になっています。おもしろ。
僕もバイナリアンになりたいです。