存在しないため、すぐに -ENOSYS を返します。
arch/x86/entry/entry_64.S から:
#if __SYSCALL_MASK == ~0
cmpq $__NR_syscall_max, %rax
#else
andl $__SYSCALL_MASK, %eax
cmpl $__NR_syscall_max, %eax
#endif
ja 1f /* return -ENOSYS (already in pt_regs->ax) */
movq %r10, %rcx
/*
* This call instruction is handled specially in stub_ptregs_64.
* It might end up jumping to the slow path. If it jumps, RAX
* and all argument registers are clobbered.
*/
#ifdef CONFIG_RETPOLINE
movq sys_call_table(, %rax, 8), %rax
call __x86_indirect_thunk_rax
#else
call *sys_call_table(, %rax, 8)
#endif
.Lentry_SYSCALL_64_after_fastpath_call:
movq %rax, RAX(%rsp)
1:
無効なシステム コール番号を使用して、ディスパッチ コードが
eax = -ENOSYS
システムコール処理関数にディスパッチする代わりに。
これによりカーネルが iret
を使用しない限り sysret
の代わりに遅いパス / sysexit
.これは、無効な数値が syscall(SYS_getpid)
よりも 17 サイクル遅いことを示す測定値を説明している可能性があります。 、glibc エラー処理のため (設定 errno
) おそらくそれを説明していません。しかし、カーネル ソースを読んだ限りでは、まだ sysret
を使用しない理由がわかりません。 -ENOSYS
を返しながら .
これは sysenter
に対する回答です 、syscall
ではありません .質問はもともと sysenter
と言っていました / sysret
(sysexit
のため、これは奇妙でした sysenter
に対応 、 sysret
の間 syscall
に対応 )。 sysenter
に基づいて回答しました x86-64 カーネル上の 32 ビット プロセス用。
ネイティブ 64 ビット syscall
カーネル内でより効率的に処理されます。 (更新; Meltdown / Spectre 軽減パッチを適用しても、C do_syscall_64
経由でディスパッチされます) 4.16-rc2)
64 ビット コードで 32 ビット int 0x80 Linux ABI を使用するとどうなりますか? Q&A では、互換モードから x86-64 カーネル (entry_64_compat.S
) へのシステム コール エントリ ポイントのカーネル側の概要を説明します。 )。この回答は、関連する部分を抜粋しただけです。
その回答とこれのリンクは Linux 4.12 ソースへのものであり、メルトダウン軽減ページテーブル操作が含まれていないため、重要 余分なオーバーヘッド。
int 0x80
と sysenter
エントリーポイントが異なります。 entry_SYSENTER_compat
を探しています .私の知る限り、sysenter
64 ビットのユーザー空間プロセスで実行しても、常にそこに移動します。 Linux のエントリ ポイントは定数 __USER32_CS
をプッシュします 保存された CS 値として、常に 32 ビット モードでユーザー空間に戻ります。
struct pt_regs
を構築するためにレジスタをプッシュした後 カーネルスタックには TRACE_IRQS_OFF
があります フック (どれだけの命令になるかわかりません)、次に call do_fast_syscall_32
これは C で書かれています (ネイティブ 64 ビット syscall
ディスパッチは asm から直接行われますが、32 ビット互換システム コールは常に C を介してディスパッチされます)。
do_syscall_32_irqs_on
arch/x86/entry/common.c
で かなり軽量です:プロセスがトレースされているかどうかを確認するだけです (これが strace
の方法だと思います) ptrace
経由でシステム コールをフックできます )、その後
...
if (likely(nr < IA32_NR_syscalls)) {
regs->ax = ia32_sys_call_table[nr]( ... arg );
}
syscall_return_slowpath(regs);
}
私の知る限り、カーネルは sysexit
を使用できます この関数が戻った後。
そのため、EAX が有効なシステム コール番号を持っているかどうかに関係なく、リターン パスは同じです。明らかに、ディスパッチをまったく行わずにリターンするのが、その関数を通る最速のパスです。特に、関数ポインタのテーブルで間接的な分岐が行われる Spectre 軽減策を備えたカーネルでは、 retpoline を通過し、常に予測を誤っていました。
追加のオーバーヘッドなしで sysenter/sysexit を実際にテストしたい場合は、Linux を変更して、トレースをチェックしたり、すべてのレジスタをプッシュ/ポップしたりせずに、より単純なエントリ ポイントを配置する必要があります。
ABI を変更して、リターン アドレスをレジスタに渡すことも必要になるでしょう (syscall
など)。 Linux の現在の sysenter
であるユーザー空間スタックに保存されるのではなく、独自に行います)。 ABIはそうします。 get_user()
でなければなりません 返すべき EIP 値を読み取ります。
このすべてのオーバーヘッドが測定対象の一部である場合、-ENOSYS
を提供する eax で間違いなくすべて設定されています。;最悪の場合、通常の 32 ビット システム コールに基づいて分岐予測子がその分岐に対してホットである場合、範囲チェックから 1 つの余分な分岐ミスが発生します。
Brendan Gregg によるこのベンチマーク (このトピックに関する興味深い読み物であるこのブログ投稿からリンクされています) close(999)
(または使用されていない他の fd) が推奨されます。