GNU/Linux >> Znalost Linux >  >> Linux

Nejrychlejší systémové volání Linuxu

Takový, který neexistuje, a proto rychle vrátí -ENOSYS.

Z 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:

Použijte neplatné systémové telefonní číslo, aby se kód pro odbavení vrátil pouze s
eax = -ENOSYS místo toho, aby se vůbec odesílalo do funkce zpracování systémových volání.

Pokud to nezpůsobí, že jádro použije iret pomalá cesta místo sysret / sysexit . To by mohlo vysvětlit, že měření ukazující neplatné číslo je o 17 cyklů pomalejší než syscall(SYS_getpid) , protože zpracování chyb glibc (nastavení errno ) to asi nevysvětluje. Ale podle mého čtení zdrojového kódu jádra nevidím žádný důvod, proč by stále nepoužíval sysret při návratu -ENOSYS .

Tato odpověď je pro sysenter , nikoli syscall . Otázka původně zněla sysenter / sysret (což bylo divné, protože sysexit jde s sysenter , zatímco sysret jde s syscall ). Odpověděl jsem na základě sysenter pro 32bitový proces na jádře x86-64.

Nativní 64bitová verze syscall je v jádře zpracováno efektivněji. (Aktualizace; se záplatami na zmírnění Meltdown / Spectre se stále odesílá přes C do_syscall_64 v 4.16-rc2).

Můj Co se stane, když použijete 32bitové int 0x80 Linux ABI v 64bitovém kódu? Otázky a odpovědi poskytují přehled o straně jádra vstupních bodů systémových volání z režimu compat do jádra x86-64 (entry_64_compat.S ). Tato odpověď pouze přebírá příslušné části.

Odkazy v této odpovědi a toto jsou na zdroje Linuxu 4.12, které neobsahují manipulaci s tabulkou stránek zmírňující Meltdown, takže to bude významné další režie.

int 0x80 a sysenter mají různé vstupní body. Hledáte entry_SYSENTER_compat . AFAIK, sysenter vždy tam jde, i když jej spustíte v 64bitovém procesu v uživatelském prostoru. Vstupní bod Linuxu tlačí konstantu __USER32_CS jako uloženou hodnotu CS, takže se vždy vrátí do uživatelského prostoru v 32bitovém režimu.

Po zatlačení registrů k vytvoření struct pt_regs v zásobníku jádra je TRACE_IRQS_OFF háček (nemám ponětí, kolik instrukcí to obnáší), pak call do_fast_syscall_32 který je napsán v C. (Nativní 64bitový syscall odesílání se provádí přímo z asm, ale volání 32bitového kompatibilního systému jsou vždy odesílána přes C).

do_syscall_32_irqs_on v arch/x86/entry/common.c je docela lehký:stačí zkontrolovat, zda je proces sledován (myslím, že takto strace může připojit systémová volání přes ptrace ), pak

   ...
    if (likely(nr < IA32_NR_syscalls)) {
        regs->ax = ia32_sys_call_table[nr]( ... arg );
    }

    syscall_return_slowpath(regs);
}

AFAIK, jádro může používat sysexit poté, co se tato funkce vrátí.

Návratová cesta je tedy stejná bez ohledu na to, zda má EAX platné číslo systémového volání či nikoli, a očividně návrat bez odeslání je nejrychlejší cestou přes tuto funkci, zejména v jádře se zmírněním Spectre, kde nepřímá větev na tabulce funkčních ukazatelů by prošel retpolínou a vždy špatně předpověděl.

Pokud chcete opravdu otestovat sysenter/sysexit bez všech těch dalších režií, budete muset upravit Linux tak, aby vložil mnohem jednodušší vstupní bod bez kontroly sledování nebo tlačení / otevírání všech registrů.

Pravděpodobně byste také chtěli upravit ABI tak, aby předávalo zpáteční adresu v registru (například syscall dělá sám o sobě) místo toho, aby se ukládal do zásobníku uživatelského prostoru, což je aktuální Linux sysenter ABI ano; musí být get_user() číst hodnotu EIP, ke které by se měla vrátit.

Pokud je celá tato režie součástí toho, co chcete změřit, určitě máte připraveno eax, které vám poskytne -ENOSYS; v nejhorším případě dostanete z kontroly dosahu jednu chybnou větev navíc, pokud jsou prediktory větve pro tuto větev horké na základě normálních 32bitových systémových volání.


V tomto benchmarku Brendana Gregga (odkaz na tento blogový příspěvek, který je zajímavým čtením na toto téma) close(999) (nebo jiné nepoužívané fd).


Linux
  1. Jak zvládnout paniku linuxového jádra

  2. Linux – metody vyvolání systémového volání v novém jádru?

  3. Tabulka systémových volání Linux nebo cheatsheet pro shromáždění

  1. Kde najdu zdrojový kód systémového volání?

  2. Jak předat parametry systémovému volání Linuxu?

  3. Fork vs Clone na 2.6 Kernel Linuxu

  1. Jak zkontrolovat verzi jádra v Linuxu

  2. x86_64 Assembly Linux System Call Zmatek

  3. Jsou různá jádra Linux/Unix zaměnitelná?