Hablar sobre mi comprensión de la corrutina

Análisis de video relacionado con la rutina:

Campamento de entrenamiento para la realización y análisis de principios
de corrutina bajo el sistema Linux (Parte 1) Campamento de entrenamiento para la realización y análisis de principios de corrutina bajo el sistema Linux (Parte 2)

¿Qué es una corrutina?

Las corrutinas se denominan "hilos ligeros" o "hilos en modo usuario". Recientemente, las corrutinas han brillado en el campo de la programación de alta concurrencia. Por ejemplo, Golang naturalmente admite corrutinas, y Lua y Python también admiten corrutinas. Pero de hecho, la corrutina no es una tecnología nueva que haya aparecido recientemente, al contrario, la corrutina es una tecnología antigua. Las versiones anteriores de Linux no admitían subprocesos. En este momento, apareció un subproceso ligero que reemplazó a los subprocesos-corutinas. Los más famosos son: GNU Pth y Libtask (obra de Russ Cox, uno de los autores del lenguaje Go). Expliquemos el principio de la siguiente corrutina.

concepto basico

Para comprender una corrutina, primero debe saber cómo funciona el programa. Entonces, primero hablemos de cómo se ejecuta el programa.

Sabemos que la misión de la CPU es ejecutar instrucciones en el programa, y ​​hay muchos registros que se utilizan para almacenar datos dentro de la CPU. Uno de los registros más importantes se llama registro EIP, que se utiliza para almacenar la siguiente instrucción a ser ejecutado. Además del registro EIP, hay un registro más importante llamado registro ESP, que se utiliza para almacenar la posición superior de la pila de programas. Además, la CPU tiene muchos otros registros para otros fines, tales como: registros generales EAX, EDX y registros de segmento CS, DS, etc.

Cuando se ejecuta un programa (llamado proceso), los valores de estos registros generalmente se modifican. Entonces, cuando desee cambiar la ejecución del proceso, solo necesita guardar el valor de estos registros y luego asignar el valor del nuevo registro de proceso a la CPU, luego el cambio de proceso se completa, generalmente llamamos a este contexto de proceso interruptor, corrutina El interruptor es similar.
Inserte la descripción de la imagen aquí

Principio de rutina

Como se discutió anteriormente, la corrutina se puede cambiar solo cambiando el contexto. El contexto es el valor del registro de la CPU, por lo que para crear una corrutina, primero debe crear un objeto que guarde el valor del registro de la CPU. En Libtask, la estructura mcontext se usa para guardar el valor del registro. La estructura de mcontext se define de la siguiente manera:

struct mcontext {
    
    
   int mc_gs;
   int mc_fs;
   int mc_es;
   int mc_ds;
   int mc_edi;
   int mc_esi;
   int mc_ebp;
   int mc_isp;
   int mc_ebx;
   int mc_edx;
   int mc_ecx;
   int mc_eax;
   int mc_trapno;
   int mc_err;
   int mc_eip;
   int mc_cs;
   int mc_eflags;
   int mc_esp;
   int mc_ss;
};

La estructura mcontext se utiliza para guardar el valor del registro. De los miembros de mcontext, puede ver que hay muchos registros para guardar, incluidos CS, DS, EIP, EAX, EBX, etc.

Principio de llamada de función C

Debido a que el cambio de corrutina generalmente se lleva a cabo llamando a una función C de swapcontext (), la función de esta función es guardar el contexto de la rutina anterior y reemplazar el contexto de la nueva rutina para cambiar la rutina, y el contexto de la rutina anterior y el nuevo es mediante C Los parámetros de la función se pasan, así que primero comprendamos el principio del proceso de llamada a la función C.

La función C pasa parámetros a través del espacio de la pila. Hay una comprensión perceptiva a través de la siguiente figura:
Inserte la descripción de la imagen aquí

En la figura anterior, la parte verde claro es poner los parámetros en la pila cuando se llama a la función. Al apilar, el lenguaje C comienza a apilarse de derecha a izquierda. Por ejemplo, cuando llamamos a la función swapcontext (antiguo, nuevo), primero colocaremos el nuevo parámetro en la pila y luego el antiguo parámetro en la pila.

Además, al llamar a una función, la CPU enviará automáticamente la siguiente instrucción de la instrucción actual a la pila. Por lo tanto, en la figura anterior, puede ver que hay una dirección de retorno después del parámetro. Almacenadas debajo de la dirección de retorno están las variables locales de la función.

Nota

El espacio de la pila crece desde la dirección alta de la memoria hasta la dirección

[Beneficios del artículo] Materiales de aprendizaje para arquitectos de servidores C / C ++ Linux más el grupo 812855908 (datos que incluyen C / C ++, Linux, tecnología golang, Nginx, ZeroMQ, MySQL, Redis, fastdfs, MongoDB, ZK, medios de transmisión, CDN, P2P, K8S, Docker, TCP / IP, corrutina, DPDK, ffmpeg, etc.)
Inserte la descripción de la imagen aquí

Interruptor de rutina

Ahora viene el punto culminante: el interruptor de la corrutina. El cambio de la corrutina se realiza guardando el contexto de la antigua corrutina y reemplazando el contexto de la nueva corrutina.

En la biblioteca Libtask, guardar el contexto de corrutina se implementa a través de getcontext (), y reemplazar el contexto de corrutina se implementa a través de setcontext (). Ambas funciones se implementan en lenguaje ensamblador. Entonces, para comprender estas dos funciones, debe haber una base para el ensamblaje. Echemos un vistazo a la implementación de estas dos funciones:

1getcontext()
gexcontext:
    movl    4(%esp), %eax
    movl    %fs, 8(%eax)
    movl    %es, 12(%eax)
    movl    %ds, 16(%eax)
    movl    %ss, 76(%eax)
    movl    %edi, 20(%eax)
    movl    %esi, 24(%eax)
    movl    %ebp, 28(%eax)
    movl    %ebx, 36(%eax)
    movl    %edx, 40(%eax)
    movl    %ecx, 44(%eax)
    movl    $1, 48(%eax)
    movl    (%esp), %ecx 
    movl    %ecx, 60(%eax)
    leal    4(%esp), %ecx
    movl    %ecx, 72(%eax)
    movl    44(%eax), %ecx
    movl    $0, %eax
    ret

El prototipo de la función getcontext () es el siguiente:

int getcontext (struct mcontext * ctx);

Su función es guardar el valor del registro actual en el parámetro ctx. El código de ensamblaje anterior no se explicará en detalle. Si está interesado, puede compararlo de acuerdo con el principio de transferencia de parámetros de la función C y será fácil de entender.

Una cosa que debe explicarse es que la línea de código de ensamblaje "movl 4 (% esp),% eax" se usa para colocar el parámetro ctx en el registro EAX, y las operaciones subsiguientes son asignadas por el desplazamiento de la estructura mcontext .

2setcontext()
setcontext:
    movl    4(%esp), %eax
    movl    8(%eax), %fs
    movl    12(%eax), %es
    movl    16(%eax), %ds
    movl    76(%eax), %ss
    movl    20(%eax), %edi
    movl    24(%eax), %esi
    movl    28(%eax), %ebp
    movl    36(%eax), %ebx
    movl    40(%eax), %edx
    movl    44(%eax), %ecx
    movl    72(%eax), %esp
    pushl   60(%eax)
    movl    48(%eax), %eax
    ret

La función setcontext () es el punto de conmutación del interruptor de corrutina, el prototipo es el siguiente:

int setcontext (struct mcontext * ctx);

Su función es reemplazar el valor del registro en el parámetro ctx con el valor del registro de la CPU para realizar el cambio.

Finalmente, podemos implementar la función swapcontext () a través de las dos funciones getcontext () y setcontext (). La implementación es muy simple:

int swapcontext(struct mcontext *new, struct mcontext *old)
{
    
    
    getcontext(old);
    setcontext(new);
    return 0;
}

En el futuro, podemos cambiar la co-rutina a través de la función swapcontext ().

para resumir

En este artículo, solo necesitamos explicar los principios básicos de las corrutinas, pero para implementar realmente una biblioteca de corrutinas que se pueda usar, es necesario realizar mucho trabajo detallado, como cambiar el espacio de pila de la corrutina (porque cada corrutina necesita su propio espacio de pila independiente no afectará su corrutina).

Además, una biblioteca completa de corrutinas también debe admitir funciones como temporizadores y corrutinas de conmutación automática de bloqueo de E / S. Actualizaciones de seguimiento sobre cómo implementar una biblioteca completa de corrutinas.

Supongo que te gusta

Origin blog.csdn.net/qq_40989769/article/details/113181782
Recomendado
Clasificación