不正な命令ud2/ud2aがあるため、SIGILLが生成されました。http://asm.inightmare.org/opcodelst/index.php?op=UD2によると:
この命令により#UDが発生しました。Intelは、将来のIntelのCPUでこの命令が#UDを引き起こすことを保証しました。もちろん、以前のすべてのCPU(186+)は、このオペコードで#UDを引き起こしました。この命令は、ソフトウェア作成者が#UD例外サービスルーチンをテストするために使用します。
中を見てみましょう:
$ gcc-4.6.2 -fopenmp omp.c -o omp
$ gdb ./omp
...
(gdb) r
Program received signal SIGILL, Illegal instruction.
...
0x08048544 in main._omp_fn.0 ()
(gdb) x/i $pc
0x8048544 <main._omp_fn.0+28>: ud2a
(gdb) disassemble
Dump of assembler code for function main._omp_fn.0:
0x08048528 <main._omp_fn.0+0>: push %ebp
0x08048529 <main._omp_fn.0+1>: mov %esp,%ebp
0x0804852b <main._omp_fn.0+3>: sub $0x18,%esp
0x0804852e <main._omp_fn.0+6>: movl $0x2,(%esp)
0x08048535 <main._omp_fn.0+13>: call 0x80483f0 <GOMP_sections_start@plt>
0x0804853a <main._omp_fn.0+18>: cmp $0x1,%eax
0x0804853d <main._omp_fn.0+21>: je 0x8048548 <main._omp_fn.0+32>
0x0804853f <main._omp_fn.0+23>: cmp $0x2,%eax
0x08048542 <main._omp_fn.0+26>: je 0x8048546 <main._omp_fn.0+30>
0x08048544 <main._omp_fn.0+28>: ud2a
0x08048546 <main._omp_fn.0+30>: jmp 0x8048546 <main._omp_fn.0+30>
0x08048548 <main._omp_fn.0+32>: jmp 0x8048548 <main._omp_fn.0+32>
End of assembler dump.
アセンブラファイルにはすでにud2aがあります。
$ gcc-4.6.2 -fopenmp omp.c -o omp.S -S; cat omp.S
main._omp_fn.0:
.LFB1:
pushl %ebp
.LCFI4:
movl %esp, %ebp
.LCFI5:
subl $24, %esp
.LCFI6:
movl $2, (%esp)
call GOMP_sections_start
cmpl $1, %eax
je .L4
cmpl $2, %eax
je .L5
.value 0x0b0f
.value 0xb0f
ud2aのコードです
ud2aがgccの意図で挿入されたことを確認した後(初期のopenmpフェーズで)、コードを理解しようとしました。関数main._omp_fn.0
は並列コードの本体です。戻りコードを呼び出し _GOMP_sections_start
て解析します。コードが1に等しい場合、1つの無限ループにジャンプします。2の場合は、2番目の無限ループにジャンプします。ただし、それ以外の場合はud2aが実行されます。(理由はわかりませんが、Hristo Ilievによると、これはGCCバグ54017です。)
このテストは、CPUコアの数を確認するのに適していると思います。デフォルトでは、GCCのopenmpライブラリ(libgomp)は、システム内のすべてのCPUコアに対してスレッドを開始します(私の場合は4つのスレッドがありました)。そして、セクションは順番に選択されます:最初のスレッドの最初のセクション、2番目のセクション-2番目のスレッドなど。
プログラムを1つまたは2つのCPUで実行した場合、SIGILLはありません(タスクセットのオプションは16進数のCPUマスクです)。
$ taskset 3 ./omp
... running on cpu0 and cpu1 ...
$ taskset 1 ./omp
... running first loop on cpu0; then run second loop on cpu0...