一部 (多く? すべて?) の 64 ビット1 Linux ディストリビューションでは、32 ビットと 64 ビットのライブラリ (libc を含む) の並列コレクションを出荷することで、32 ビット アプリケーションの実行を許可しています。そのため、32 ビット アプリケーションは 32 ビット ライブラリにリンクし、64 ビット カーネルで実行できます。
私は知りたい力学32 ビット アプリケーションが 64 ビット カーネル上でシステム コールを実行する方法について。答えは libc またはカーネル ソースのどこかにあると思いますが、どこを調べればよいかわからないため、ソースを調べるのは時間がかかります。
そしてもっと重要な質問は、パフォーマンスのオーバーヘッドはありますか?2論理的には、32 ビット アプリのシステム コールからの呼び出しは、64 ビットの内部カーネル環境に変換される必要があります。これはどのように、どこで実現されるのでしょうか。
1「32 ビット」 = IA-32、「64 ビット」 = AMD64
2回答では、それが重要であると仮定してください :)
ベストアンサー1
ユーザー空間側から見ると、その仕組みは 32 ビットのネイティブ カーネルで syscall を実行する場合と同じです。つまり、32 ビット glibc を含むすべてのユーザーモード コードが同じように動作します。
カーネル側では、ユーザー空間からの古い IA32 エントリ ポイント (例: int 0x80
) が、アセンブラ ルーチンを呼び出すように設定されますia32_syscall
。(カーネル空間への移行では、プロセッサがカーネルのコード セグメント セレクタをロードし、64 ビットの "long" モードに移行します)。
次に、ルーチンia32_syscall
は x86_64 システムコール呼び出し規則に一致するようにいくつかの引数をシャッフルします。
movl %edi,%r8d
.if \noebp
.else
movl %ebp,%r9d
.endif
xchg %ecx,%esi
movl %ebx,%edi
movl %edx,%edx /* zero extension */
次に、IA32 システム コール番号を使用して、テーブルを介して関数呼び出しを行います。ia32_sys_call_table
これは基本的に、IA32 システム コール番号をネイティブ システム コール実装と一致させます (システム コール番号は、IA32 と x86_64 で大きく異なります)。このテーブルの最初の部分は次のようになります。
ia32_sys_call_table:
.quad sys_restart_syscall
.quad sys_exit
.quad stub32_fork
.quad sys_read
.quad sys_write
ほとんどのシステムコールでは、 のように x86_64 実装を直接呼び出すことができるようになりましたexit()
。 などの他のシステムコールではfork()
、期待される IA32 セマンティクスを正しく実装するラッパーが提供されます (特に、32 ビットから 64 ビットへの引数の符号拡張が必要な場合)。
ご覧のとおり、カーネル コードのオーバーヘッドは最小限です。レジスタ値に対するいくつかの些細な変更と、いくつかの関数に対する追加の関数呼び出しです。32 ビット モードから 64 ビット モードへの移行を引き起こすコード セグメント セレクターをロードすると、そうでない場合よりもプロセッサの実行が遅くなるかどうかはわかりません。プロセッサ アーキテクチャ マニュアルを確認してください。