GNU/Linux >> Linux の 問題 >  >> Linux

最速の Linux システム コール

存在しないため、すぐに -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 0x80sysenter エントリーポイントが異なります。 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) が推奨されます。


Linux
  1. Linuxでカーネルバージョンを確認する方法

  2. システムコールのソースコードはどこにありますか?

  3. パラメータをLinuxシステムコールに渡す方法は?

  1. Linux でシステム コール テーブルを変更する必要があるのはなぜですか?

  2. Linux のカーネルがモノリシックであるのに、なぜ Linux は Unix に似ているのですか?

  3. 異なる Linux/Unix カーネルは互換性がありますか?

  1. Linuxカーネルパニックを処理する方法

  2. Linux –新しいカーネルのシステムコール呼び出しメソッド?

  3. アセンブリの Linux システム コール テーブルまたはチートシート