Linux環境においてcpu数(core数)を数えたいときにgrep processor /proc/cpuinfo | wc -lとやっても良いんですけど、もうちょっとスマートなやり方は無いかな?と思ってたらnproc(1)があったのでそれがどうやって数を調べているのかソースを読んでみました。
その前にnprocコマンドの説明を。nprocコマンドは基本的に引数無しで実行すればOKです。
masami@saga:~$ nproc 8
さて、まずは読むべきソースがどのパッケージにあるのかを調べないといけないので以下を実行して、パッケージを調べる。
masami@saga:~$ yaourt -Qo `which nproc` /usr/bin/nproc is owned by coreutils 8.23-1
coreutilsはgitで管理されていて、webからも見れるのでそれを使います。あと、coreutilsの他にgnulibのコードも読む必要があってこちらもgitwebで見て行きましょう。
nproc(1)のソースはsrc/nproc.cにあります。引数無しで実行した場合は87行目、123行目だけ覚えてば良いという手軽さ。num_processors()の返り値がcpu数です。
75 int 76 main (int argc, char **argv) 77 { 〜略〜 87 enum nproc_query mode = NPROC_CURRENT_OVERRIDABLE; 〜略〜 123 nproc = num_processors (mode);
ということで、cpu数を数えるnum_processors()ですがこれはcoreutilsにはなくてgnulibにあります。なのでここからはgnulibのコードを見て行きましょう。
num_processors()はlib/nproc.cにあります。
まずは引数で渡ってきたmode(query)のチェックで、引数無しでnprocを実行した場合はif文の条件が真になるので中に入りますが、通常はOMP_NUM_THREADSという環境変数は設定されていないと思いますので、ここは単にqueryの値をNPROC_CURRENTに置き換えるだけになります。
199 unsigned long int 200 num_processors (enum nproc_query query) 201 { 202 if (query == NPROC_CURRENT_OVERRIDABLE) 203 { 204 /* Test the environment variable OMP_NUM_THREADS, recognized also by all 205 programs that are based on OpenMP. The OpenMP spec says that the 206 value assigned to the environment variable "may have leading and 207 trailing white space". */ 208 const char *envvalue = getenv ("OMP_NUM_THREADS"); 209 210 if (envvalue != NULL) 211 { 〜略〜 228 } 229 230 query = NPROC_CURRENT; 231 }
そして先に進んで248行目、先の処理でquery変数がNPROC_CURRENTになっているのでここは真となって中に入りnum_processors_via_affinity_mask()を読んでcpu数の取得をします。 ここでcpu数が0以上見つかれば終了です。
248 if (query == NPROC_CURRENT) 249 { 250 /* Try the modern affinity mask system call. */ 251 { 252 unsigned long nprocs = num_processors_via_affinity_mask (); 253 254 if (nprocs > 0) 255 return nprocs; 256 } 257 258 #if defined _SC_NPROCESSORS_ONLN 259 { /* This works on glibc, Mac OS X 10.5, FreeBSD, AIX, OSF/1, Solaris, 260 Cygwin, Haiku. */ 261 long int nprocs = sysconf (_SC_NPROCESSORS_ONLN); 262 if (nprocs > 0) 263 return nprocs; 264 } 265 #endif 266 }
num_processors_via_affinity_mask()はifdefで条件コンパイルをされているのでコードを見ただけだとちょっとどこが使われているのかわかりません(´・ω・`) なのでstraceしてみます。
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=2581856, ...}) = 0 mmap(NULL, 2581856, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f77e19d0000 close(3) = 0 sched_getaffinity(0, 128, {ff, 0}) = 16 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f77e220a000 write(1, "8\n", 28 ) = 2 close(1) = 0 munmap(0x7f77e220a000, 4096) = 0 close(2) = 0 exit_group(0) = ? +++ exited with 0 +++
sched_getaffinity()が呼ばれています。ソースを見てみるとsched_getaffinity(2)を使っているのはここでした。処理内容としてはsched_getaffinity()を使ってcpu affinity maskを取得して立っているbit数を数え、それを最終的に返してますね。ifdefが多いので見難いですがやってることは単純でした。
124 #elif HAVE_SCHED_GETAFFINITY_LIKE_GLIBC /* glibc >= 2.3.4 */ 125 { 126 cpu_set_t set; 127 128 if (sched_getaffinity (0, sizeof (set), &set) == 0) 129 { 130 unsigned long count; 131 132 # ifdef CPU_COUNT 133 /* glibc >= 2.6 has the CPU_COUNT macro. */ 134 count = CPU_COUNT (&set); 135 # else 136 size_t i; 137 138 count = 0; 139 for (i = 0; i < CPU_SETSIZE; i++) 140 if (CPU_ISSET (i, &set)) 141 count++; 142 # endif 143 if (count > 0) 144 return count; 145 } 146 } 147 #elif HAVE_SCHED_GETAFFINITY_NP /* NetBSD >= 5 */
新装改訂版 Linuxのブートプロセスをみる (アスキー書籍)
- 作者: 白崎博生
- 出版社/メーカー: KADOKAWA / アスキー・メディアワークス
- 発売日: 2014/10/02
- メディア: Kindle版
- この商品を含むブログ (1件) を見る