Linuxの汎用x86ユーザーランドアプリケーションでSIGBUS(バスエラー)を引き起こす原因は何ですか?私がオンラインで見つけることができたすべての議論は、メモリアライメントエラーに関するものです。これは、私が理解していることから、x86には実際には当てはまりません。
(私のコードは Geode で実行されています。関連するプロセッサ固有の癖がある場合に備えて。)
アラインされていないアクセストラップをオンにすると、アラインされていないアクセスからSIGBUSを取得できますが、通常、x86ではオフになっています。なんらかのエラーが発生した場合は、メモリマップドデバイスにアクセスして取得することもできます。
最善の策は、デバッガーを使用して障害のある命令(SIGBUSは同期)を識別し、それが何をしようとしていたかを確認することです。
SIGBUS
は、メモリアラインメントの障害以外のかなりの理由でLinuxで発生する可能性があります。たとえば、マップされたファイルの終わりを超えてmmap
領域にアクセスしようとした場合です。
mmap
、共有メモリ領域などを使用していますか?
X86(x86_64を含む)上のSIGBUSLinuxは珍しい獣です。 mmap
edファイルの終わりを超えてアクセスしようとした場合、またはPOSIXで説明されているその他の状況から表示される場合があります。
しかし、ハードウェアの障害からSIGBUSを入手するのは簡単ではありません。つまり、SIMDであるかどうかに関係なく、任意の命令からのアラインされていないアクセスは、通常、SIGSEGVになります。スタックオーバーフローはSIGSEGVになります。正規の形式ではないアドレスへのアクセスでさえ、SIGSEGVになります。これはすべて、#GPが発生したためです。これは、ほとんどの場合、SIGSEGVにマップされます。
ここで、CPU例外のためにSIGBUSを取得するいくつかの方法があります。
EFLAGS
のACビットを有効にしてから、メモリの読み取りまたは書き込み命令による非整列アクセスを実行します。詳細については、 このディスカッション を参照してください。
スタックポインタレジスタ(rsp
またはrbp
)を介して正規の違反を実行し、#SSを生成します。 GCCの例を次に示します(gcc test.c -o test -masm=intel
でコンパイル):
int main() { __ asm __( "mov rbp、0x400000000000000\n" "mov rax、[rbp]\n" "ud2\n"); }
そうそう、SIGBUSを入手するためのもう1つの奇妙な方法があります。
カーネルがメモリ不足のためにコードページでページングに失敗した場合(OOMキラーを無効にする必要があります)、または失敗した場合IO要求、SIGBUS。
これは、上記で「失敗したIO要求」」として簡単に説明されましたが、少し詳しく説明します。
よくあるケースは、ftruncateを使用してファイルを遅延成長させ、それをメモリにマップし、データの書き込みを開始してから、ファイルシステムのスペースが不足する場合です。マップされたファイルの物理スペースはページフォールトに割り当てられます。ページフォールトが残っていない場合、プロセスはSIGBUSを受け取ります。
このエラーからアプリケーションを正しく回復する必要がある場合は、fallocateを使用してmmapの前にスペースを明示的に予約することをお勧めします。 fallocate呼び出し後のerrnoでのENOSPCの処理は、特にマルチスレッドアプリケーションでは、シグナルを処理するよりもはるかに簡単です。
X86 Linuxでのバスエラーの一般的な原因は、実際にはポインターではないもの、またはワイルドポインターであるものを逆参照しようとしていることです。たとえば、ポインタの初期化に失敗したり、ポインタに任意の整数を割り当ててから逆参照しようとすると、通常、セグメンテーション違反またはバスエラーが発生します。
配置はx86に適用されます。 x86のメモリはバイトアドレス可能ですが(したがって、任意のアドレスへのcharポインタを持つことができます)、たとえば4バイト整数へのポインタがある場合は、そのポインタを整列させる必要があります。
Gdbでプログラムを実行し、問題を診断するためにバスエラーを生成しているポインタアクセスを特定する必要があります。
殴られた道から少し外れていますが、アライメントされていないSSE2(m128)ロードからSIGBUSを取得できます。
mmap
とMAP_HUGETLB
フラグを使用してhugepagesに裏打ちされたマッピングを要求した場合、カーネルが割り当てられた巨大ページを使い果たしてページフォールトを処理できない場合は、SIGBUS
を取得できます。
この場合、を介して割り当てられた巨大なページの数を増やす必要があります
/sys/kernel/mm/hugepages/hugepages-<size>/nr_hugepages
または/sys/devices/system/node/nodeX/hugepages/hugepages-<size>/nr_hugepages
。