GNU/Linux >> Linux Esercitazione >  >> Linux

Chiamata di sistema Linux più veloce

Uno che non esiste, e quindi restituisce rapidamente -ENOSYS.

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

Utilizza un numero telefonico di sistema non valido in modo che il codice di invio restituisca semplicemente con
eax = -ENOSYS invece di inviare a una funzione di gestione delle chiamate di sistema.

A meno che questo non faccia sì che il kernel utilizzi il iret percorso lento invece di sysret / sysexit . Ciò potrebbe spiegare le misurazioni che mostrano che un numero non valido è 17 cicli più lento di syscall(SYS_getpid) , perché la gestione degli errori di glibc (impostazione errno ) probabilmente non lo spiega. Ma dalla mia lettura del sorgente del kernel, non vedo alcun motivo per cui non dovrebbe ancora utilizzare sysret durante la restituzione di -ENOSYS .

Questa risposta è per sysenter , non syscall . La domanda originariamente diceva sysenter / sysret (che era strano perché sysexit va con sysenter , mentre sysret va con syscall ). Ho risposto basandomi su sysenter per un processo a 32 bit su un kernel x86-64.

syscall nativo a 64 bit viene gestito in modo più efficiente all'interno del kernel. (Aggiornamento; con le patch di mitigazione Meltdown / Spectre, invia ancora tramite C do_syscall_64 in 4.16-rc2).

My Cosa succede se si utilizza l'ABI Linux 0x80 int a 32 bit nel codice a 64 bit? Domande e risposte offre una panoramica del lato kernel dei punti di ingresso delle chiamate di sistema dalla modalità compat a un kernel x86-64 (entry_64_compat.S ). Questa risposta ne prende solo le parti rilevanti.

I collegamenti in quella risposta e questo sono alle fonti di Linux 4.12, che non contengono la manipolazione della tabella delle pagine di mitigazione di Meltdown, quindi sarà significativo costi aggiuntivi.

int 0x80 e sysenter hanno diversi punti di ingresso. Stai cercando entry_SYSENTER_compat . AFAIK, sysenter va sempre lì, anche se lo esegui in un processo in spazio utente a 64 bit. Il punto di ingresso di Linux invia un __USER32_CS costante come valore CS salvato, quindi tornerà sempre nello spazio utente in modalità a 32 bit.

Dopo aver spinto i registri per costruire un struct pt_regs nello stack del kernel, c'è un TRACE_IRQS_OFF hook (non ho idea di quante istruzioni equivalgano), quindi call do_fast_syscall_32 che è scritto in C. (Native 64-bit syscall l'invio viene effettuato direttamente da asm, ma le chiamate di sistema compatibili a 32 bit vengono sempre inviate tramite C).

do_syscall_32_irqs_on in arch/x86/entry/common.c è abbastanza leggero:solo un controllo se il processo viene tracciato (penso che questo sia il modo in cui strace può agganciare le chiamate di sistema tramite ptrace ), quindi

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

    syscall_return_slowpath(regs);
}

AFAIK, il kernel può usare sysexit dopo che questa funzione ritorna.

Quindi il percorso di ritorno è lo stesso indipendentemente dal fatto che EAX avesse o meno un numero di chiamata di sistema valido, e ovviamente il ritorno senza invio è il percorso più veloce attraverso quella funzione, specialmente in un kernel con mitigazione Spectre dove il ramo indiretto sulla tabella dei puntatori di funzione passerebbe attraverso una retpoline e prevederebbe sempre errori.

Se vuoi davvero testare sysenter/sysexit senza tutto quel sovraccarico extra, dovrai modificare Linux per mettere un punto di ingresso molto più semplice senza controllare la traccia o spingere/poppare tutti i registri.

Probabilmente vorrai anche modificare l'ABI per passare un indirizzo di ritorno in un registro (come syscall fa da solo) invece di essere salvato nello stack dello spazio utente che l'attuale sysenter di Linux L'ABI lo fa; deve get_user() per leggere il valore EIP a cui dovrebbe tornare.

Se tutto questo sovraccarico fa parte di ciò che vuoi misurare, sei decisamente pronto con un eax che ti dà -ENOSYS; nel peggiore dei casi otterrai un errore di ramo in più dal controllo dell'intervallo se i predittori di ramo sono attivi per quel ramo in base alle normali chiamate di sistema a 32 bit.


In questo benchmark di Brendan Gregg (collegato da questo post del blog che è una lettura interessante sull'argomento) close(999) (o qualche altro fd non in uso) è consigliato.


Linux
  1. x86_64 Assembly Linux System Call Confusion

  2. Chiama una funzione dello spazio utente dall'interno di un modulo del kernel Linux

  3. Come passare i parametri alla chiamata di sistema Linux?

  4. Perché è necessario modificare le tabelle delle chiamate di sistema in Linux?

  5. Perché Linux è simile a Unix se il suo kernel è monolitico?

Requisiti di sistema di Kali Linux

Comando di spegnimento di Linux

Comando Dmesg in Linux

Comando Sysctl in Linux

Linux è un sistema operativo o un kernel?

Kernel Linux vs. Kernel Mac