GNU/Linux >> Tutoriales Linux >  >> Linux

La llamada al sistema Linux más rápida

Uno que no existe y, por lo tanto, devuelve -ENOSYS rápidamente.

Desde arco/x86/entrada/entrada_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:

Utilice un número de llamada del sistema no válido para que el código de despacho simplemente regrese con
eax = -ENOSYS en lugar de enviar a una función de manejo de llamadas del sistema.

A menos que esto haga que el kernel use el iret ruta lenta en lugar de sysret / sysexit . Eso podría explicar las mediciones que muestran que un número no válido es 17 ciclos más lento que syscall(SYS_getpid) , porque el manejo de errores de glibc (estableciendo errno ) probablemente no lo explica. Pero a partir de mi lectura del código fuente del kernel, no veo ninguna razón por la que no siga usando sysret mientras devuelve -ENOSYS .

Esta respuesta es para sysenter , no syscall . La pregunta originalmente decía sysenter / sysret (lo cual fue raro porque sysexit va con sysenter , mientras que sysret va con syscall ). Respondí basado en sysenter para un proceso de 32 bits en un kernel x86-64.

Nativo de 64 bits syscall se maneja de manera más eficiente dentro del kernel. (Actualización; con los parches de mitigación de Meltdown/Spectre, todavía se envía a través de C do_syscall_64 en 4.16-rc2).

Mi ¿Qué sucede si usa la ABI de Linux int 0x80 de 32 bits en código de 64 bits? Preguntas y respuestas brinda una descripción general del lado del kernel de los puntos de entrada de llamadas al sistema desde el modo de compatibilidad en un kernel x86-64 (entry_64_compat.S ). Esta respuesta solo toma las partes relevantes de eso.

Los enlaces en esa respuesta y esto son a las fuentes de Linux 4.12, que no contiene la manipulación de la tabla de páginas de mitigación de Meltdown, por lo que será significativo gastos generales adicionales.

int 0x80 y sysenter tienen diferentes puntos de entrada. Estás buscando entry_SYSENTER_compat . AFAIK, sysenter siempre va allí, incluso si lo ejecuta en un proceso de espacio de usuario de 64 bits. El punto de entrada de Linux empuja una constante __USER32_CS como el valor de CS guardado, por lo que siempre volverá al espacio de usuario en modo de 32 bits.

Después de presionar registros para construir un struct pt_regs en la pila del kernel, hay un TRACE_IRQS_OFF gancho (no tengo idea de cuántas instrucciones equivale a eso), luego call do_fast_syscall_32 que está escrito en C. (Nativo de 64 bits syscall el envío se realiza directamente desde asm, pero las llamadas al sistema de compatibilidad de 32 bits siempre se envían a través de C).

do_syscall_32_irqs_on en arch/x86/entry/common.c es bastante liviano:solo verifique si se está rastreando el proceso (creo que así es como strace puede conectar llamadas al sistema a través de ptrace ), entonces

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

    syscall_return_slowpath(regs);
}

AFAIK, el kernel puede usar sysexit después de que esta función regrese.

Por lo tanto, la ruta de retorno es la misma, ya sea que EAX tenga o no un número de llamada de sistema válido, y obviamente regresar sin despachar nada es la ruta más rápida a través de esa función, especialmente en un kernel con mitigación de Spectre donde la rama indirecta en la tabla de punteros de función pasaría por un retpoline y siempre predeciría mal.

Si realmente desea probar sysenter/sysexit sin toda esa sobrecarga adicional, deberá modificar Linux para poner un punto de entrada mucho más simple sin verificar el seguimiento o empujar / abrir todos los registros.

Probablemente también desee modificar la ABI para pasar una dirección de retorno en un registro (como syscall lo hace por sí solo) en lugar de guardarlo en la pila de espacio de usuario que el sysenter actual de Linux ABI lo hace; tiene que get_user() para leer el valor de EIP al que debería volver.

Si toda esta sobrecarga es parte de lo que desea medir, definitivamente está listo con un eax que le brinda -ENOSYS; en el peor de los casos, se perderá una bifurcación adicional de la verificación de rango si los predictores de bifurcación están activos para esa bifurcación en función de las llamadas normales al sistema de 32 bits.


En este punto de referencia de Brendan Gregg (vinculado desde esta publicación de blog que es una lectura interesante sobre el tema) close(999) (o algún otro fd que no esté en uso).


Linux
  1. Cómo verificar la versión del Kernel en Linux

  2. Linux:¿métodos de invocación de llamadas al sistema en el nuevo kernel?

  3. ¿Dónde puedo encontrar el código fuente de la llamada al sistema?

  4. ¿Cómo pasar parámetros a la llamada del sistema Linux?

  5. ¿Por qué los números de llamada del sistema Linux en x86 y x86_64 son diferentes?

Requisitos del sistema Linux Kali

Comando de apagado de Linux

Comando Dmesg en Linux

Comando Sysctl en Linux

¿Linux es un sistema operativo o un kernel?

Núcleo de Linux vs. Núcleo de Mac