バスエラーとセグメンテーションエラーの違いは?プログラムが初めてセグメンテーション違反を発生して停止し、次にバスエラーを発生して終了する可能性がありますか?
私が使用したほとんどのアーキテクチャでは、違いは次のとおりです。
SIGBUS
は、ファイル mmap()
を使用している場合にも発生し、ファイルの終わりを超えてマップされたバッファの一部にアクセスしようとするだけでなく、スペース不足などのエラー条件。 sigaction()
を使用してシグナルハンドラを登録し、SA_SIGINFO
、プログラムで障害のあるメモリアドレスを調べて、メモリマップファイルのエラーのみを処理することができます。
たとえば、プログラムがハードウェアバスでサポートされていないことを実行しようとすると、バスエラーが発生する可能性があります。たとえば SPARCs では、奇数アドレスからマルチバイト値(int、32ビットなど)を読み取ろうとすると、バスエラーが発生しました。
セグメンテーション違反は、たとえば、セグメンテーションルールに違反するアクセスを行った場合、つまり、所有していないメモリを読み書きしようとした場合に発生します。
あなたは、Posixによって定義されたSIGSEGV
およびSIGBUS
信号について話していると思います。
SIGSEGV
は、プログラムが無効なアドレスを参照したときに発生します。 SIGBUS
は、実装定義のハードウェア障害です。これら2つのシグナルのデフォルトのアクションは、プログラムを終了することです。
プログラムはこれらのシグナルをキャッチし、無視することもできます。
プログラムが初めてセグメンテーション違反を発生して停止し、2回目にバスエラーが発生して終了する可能性がありますか?
はい、同じバグでも同じです:これは、決定的な方法で配列の境界の外側のインデックスによってセグメンテーションフォールト(SIGSEGV)とバスエラー(SIGBUS)の両方を生成する可能性があるmacOSからの深刻ですが単純な例です。上記の未調整のアクセスは、macOSの問題ではありません。 (この例では、デバッガー内で実行した場合、SIGBUSは発生しません。私の場合はlldb
です!)
bus_segv.c:
_#include <stdlib.h>
char array[10];
int main(int argc, char *argv[]) {
return array[atol(argv[1])];
}
_
この例では、コマンドラインから整数を取得します。これは、配列のインデックスとして機能します。信号を発生させないいくつかのインデックス値(配列の外でも)があります。 (指定されたすべての値は、標準のセグメント/セクションサイズによって異なります。HighSierra macOS 10.13.5、i5-4288U CPU @ 2.60GHzでバイナリを生成するためにclang-902.0.39.1を使用しました。)
77791より上で-4128より下のインデックスは、セグメンテーション違反(SIGSEGV)を引き起こします。 24544はバスエラー(SIGBUS)を引き起こします。ここに完全なマップ:
_$ ./bus_segv -4129
Segmentation fault: 11
$ ./bus_segv -4128
...
$ ./bus_segv 24543
$ ./bus_segv 24544
Bus error: 10
...
$ ./bus_segv 28639
Bus error: 10
$ ./bus_segv 28640
...
$ ./bus_segv 45023
$ ./bus_segv 45024
Bus error: 10
...
$ ./bus_segv 53215
Bus error: 10
$ ./bus_segv 53216
...
$ ./bus_segv 69599
$ ./bus_segv 69600
Bus error: 10
...
$ ./bus_segv 73695
Bus error: 10
$ ./bus_segv 73696
...
$ ./bus_segv 77791
$ ./bus_segv 77792
Segmentation fault: 11
_
逆アセンブルされたコードを見ると、バスエラーのある範囲の境界は、インデックスが表示されるほど奇妙ではないことがわかります。
$ otool -tv bus_segv
_bus_segv:
(__TEXT,__text) section
_main:
0000000100000f60 pushq %rbp
0000000100000f61 movq %rsp, %rbp
0000000100000f64 subq $0x10, %rsp
0000000100000f68 movl $0x0, -0x4(%rbp)
0000000100000f6f movl %edi, -0x8(%rbp)
0000000100000f72 movq %rsi, -0x10(%rbp)
0000000100000f76 movq -0x10(%rbp), %rsi
0000000100000f7a movq 0x8(%rsi), %rdi
0000000100000f7e callq 0x100000f94 ## symbol stub for: _atol
0000000100000f83 leaq 0x96(%rip), %rsi
0000000100000f8a movsbl (%rsi,%rax), %eax
0000000100000f8e addq $0x10, %rsp
0000000100000f92 popq %rbp
0000000100000f93 retq
_
leaq 0x96(%rip), %rsi
により、rsiは配列の開始アドレスの(PCで相対的に決定された)アドレスになります。
_rsi = 0x100000f8a + 0x96 = 0x100001020
rsi - 4128 = 0x100000000 (below segmentation fault)
rsi + 24544 = 0x100007000 (here and above bus error)
rsi + 28640 = 0x100008000 (below bus error)
rsi + 45024 = 0x10000c000 (here and above bus error)
rsi + 53216 = 0x10000e000 (below bus error)
rsi + 69600 = 0x100012000 (here and above bus error)
rsi + 73696 = 0x100013000 (below bus error)
rsi + 77792 = 0x100014000 (here and above segmentation fault)
_
lldb
は、おそらく異なるページ制限でプロセスをセットアップします。デバッグセッションでバスエラーを再現できませんでした。そのため、デバッガーは、バイナリーを吐き出すバス・エラーの回避策になる可能性があります。
アンドレアス
質問を(おそらく誤って)「SIGSEGVまたはSIGBUSが断続的に取得されます。なぜ一貫性がないのですか」と解釈すると、CまたはC++標準では、ポインタを使用して危険なことを行うと、CまたはC++標準で保証されないことに注意してください。 segfault;それは単なる「未定義の行動」であり、私が教授として一度言ったのは、代わりにワニが床板から現れてあなたを食べるかもしれないということです。
つまり、2つのバグがあり、最初のバグときどきがSIGSEGVを引き起こし、2番目のバグ(segfaultが発生せず、プログラムがまだ実行中の場合)がSIGBUSを引き起こしている可能性があります。
デバッガーを使用してステップスルーし、ワニを探すことをお勧めします。
これは バスエラーとは何ですか? の重複になります。
プログラムが初めてセグメンテーション違反を発生して停止し、次にバスエラーを発生して終了する可能性がありますか?
質問の一部。ここにある情報を使用して、これに自分で答えられるはずです。
狂気:同じことを何度も繰り返し、異なる結果を期待しています。
- アルバート・アインシュタイン
もちろん、文字通り質問に答える...
#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
int main() {
srand(time(NULL));
if (Rand() % 2)
kill(getpid(), SIGBUS);
else
kill(getpid(), SIGSEGV);
return 0;
}
多田、ある実行でセグメンテーション違反で終了し、別の実行でバスエラーで終了するプログラム。