GNU/Linux >> Tutoriels Linux >  >> Linux

Appel système Linux le plus rapide

Celui qui n'existe pas, et renvoie donc -ENOSYS rapidement.

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

Utilisez un numéro d'appel système invalide pour que le code de répartition revienne simplement avec
eax = -ENOSYS au lieu d'être envoyé à une fonction de gestion des appels système.

Sauf si cela amène le noyau à utiliser le iret chemin lent au lieu de sysret / sysexit . Cela pourrait expliquer les mesures montrant un nombre invalide étant 17 cycles plus lents que syscall(SYS_getpid) , car la gestion des erreurs de la glibc (réglage errno ) ne l'explique probablement pas. Mais d'après ma lecture de la source du noyau, je ne vois aucune raison pour laquelle il n'utiliserait toujours pas sysret en retournant -ENOSYS .

Cette réponse est pour sysenter , pas syscall . La question disait à l'origine sysenter / sysret (ce qui était bizarre car sysexit va avec sysenter , tandis que sysret va avec syscall ). J'ai répondu sur la base de sysenter pour un processus 32 bits sur un noyau x86-64.

syscall natif 64 bits est géré plus efficacement à l'intérieur du noyau. (Mise à jour ; avec les correctifs d'atténuation Meltdown / Spectre, il est toujours distribué via C do_syscall_64 en 4.16-rc2).

Que se passe-t-il si vous utilisez l'ABI Linux int 0x80 32 bits dans du code 64 bits ? Q&A donne un aperçu du côté noyau des points d'entrée des appels système du mode compat dans un noyau x86-64 (entry_64_compat.S ). Cette réponse ne prend que les parties pertinentes de cela.

Les liens dans cette réponse et celui-ci sont vers les sources Linux 4.12, qui ne contiennent pas la manipulation de table de page d'atténuation Meltdown, donc ce sera significatif surcharge supplémentaire.

int 0x80 et sysenter ont des points d'entrée différents. Vous recherchez entry_SYSENTER_compat . AFAIK, sysenter y va toujours, même si vous l'exécutez dans un processus d'espace utilisateur 64 bits. Le point d'entrée de Linux pousse une constante __USER32_CS comme valeur CS enregistrée, il reviendra donc toujours dans l'espace utilisateur en mode 32 bits.

Après avoir poussé les registres pour construire un struct pt_regs sur la pile du noyau, il y a un TRACE_IRQS_OFF crochet (aucune idée du nombre d'instructions que cela représente), puis call do_fast_syscall_32 qui est écrit en C. (natif 64 bits syscall la répartition est effectuée directement depuis asm, mais les appels système compatibles 32 bits sont toujours répartis via C).

do_syscall_32_irqs_on en arch/x86/entry/common.c est assez léger :il suffit de vérifier si le processus est tracé (je pense que c'est ainsi que strace peut accrocher les appels système via ptrace ), puis

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

    syscall_return_slowpath(regs);
}

AFAIK, le noyau peut utiliser sysexit après le retour de cette fonction.

Ainsi, le chemin de retour est le même, que EAX ait ou non un numéro d'appel système valide, et il est évident que le retour sans répartition est le chemin le plus rapide à travers cette fonction, en particulier dans un noyau avec atténuation Spectre où la branche indirecte sur la table des pointeurs de fonction passerait par une retpoline et ferait toujours une mauvaise prédiction.

Si vous voulez vraiment tester sysenter/sysexit sans toute cette surcharge supplémentaire, vous devrez modifier Linux pour mettre un point d'entrée beaucoup plus simple sans vérifier le traçage ou pousser/sauter tous les registres.

Vous voudriez probablement aussi modifier l'ABI pour passer une adresse de retour dans un registre (comme syscall fait tout seul) au lieu d'être enregistré sur la pile de l'espace utilisateur que le sysenter actuel de Linux ABI le fait ; il doit get_user() pour lire la valeur EIP à laquelle il doit revenir.

Si tous ces frais généraux font partie de ce que vous voulez mesurer, vous êtes définitivement prêt avec un eax qui vous donne -ENOSYS; au pire, vous obtiendrez une branche supplémentaire manquante à partir de la vérification de plage si les prédicteurs de branche sont chauds pour cette branche basée sur des appels système 32 bits normaux.


Dans ce benchmark de Brendan Gregg (lié à cet article de blog qui est une lecture intéressante sur le sujet) close(999) (ou un autre fd non utilisé) est recommandé.


Linux
  1. Comment gérer une panique du noyau Linux

  2. Comment vérifier la version du noyau sous Linux

  3. Linux - Méthodes d'appel système dans le nouveau noyau ?

  4. x86_64 Assemblage Linux System Call Confusion

  5. Comment passer des paramètres à l'appel système Linux ?

Configuration système requise pour Kali Linux

Commande d'arrêt de Linux

Commande Dmesg sous Linux

Commande Sysctl sous Linux

Linux est-il un système d'exploitation ou un noyau ?

Le noyau Linux contre. Mac noyau