Explicación detallada del hilo del proceso y la programación de Linux

Hilo y programación de procesos de Linux

1 Concepto de proceso

1.1 La definición de proceso e hilo La
definición clásica en el sistema operativo:
proceso: unidad de asignación de recursos.
Tema: unidad de programación.
El sistema operativo utiliza PCB (bloque de control de proceso, bloque de control de proceso) para describir el proceso. El PCB en Linux es la estructura task_struct.

1.2 Ciclo de vida del proceso
1.2.1 Estado del proceso
R, TASK_RUNNING: estado listo o en ejecución, el proceso está listo para ejecutarse, pero no necesariamente ocupando la CPU
S, TASK_INTERRUPTIBLE: sueño superficial, esperando recursos, puede responder a señales, generalmente el proceso en reposo activo Entró en el estado
D, TASK_UNINTERRUPTIBLE: sueño profundo, esperando recursos, sin responder a las señales, un escenario típico es que el proceso tiene el semáforo bloqueado
Z, TASK_ZOMBIE: estado zombi, el proceso ha salido o finalizado, pero el proceso padre aún no lo sabe, el estado cuando no hay reciclaje
T, TASK_STOPED: detener, estado de depuración, el proceso se cuelga después de recibir la señal SIGSTOP
Inserte la descripción de la imagen aquí

1.2.2 Creación de procesos y API relacionadas con la muerte
1) system ()
inicia un nuevo proceso llamando al shell
2) exec () inicia un nuevo proceso
reemplazando la imagen del proceso actual
3) fork ()
comienza copiando la imagen del proceso actual Para un nuevo proceso, fork () en el proceso hijo devuelve 0, y fork () en el proceso padre devuelve el ID del proceso hijo.
4) esperar () El
proceso padre se suspende, esperando el final del proceso hijo.
5) Proceso huérfano y proceso zombi Proceso
huérfano: un proceso padre sale mientras uno o más de sus procesos secundarios todavía están en ejecución, entonces esos procesos secundarios se convertirán en procesos huérfanos. El proceso de huérfano será adoptado por el proceso de inicio (el número de proceso es 1), y el proceso de inicio completará el trabajo de recopilación de estado para ellos. El proceso huérfano no desperdicia recursos.
Proceso zombi: un proceso usa fork para crear un proceso hijo. Si el proceso hijo sale y el proceso padre no llama a wait o waitpid para obtener la información de estado del proceso hijo, el descriptor del proceso hijo todavía se almacena en el sistema. Este tipo de proceso se denomina proceso zombie. El proceso zombie desperdicia recursos del sistema (el descriptor de proceso task_struct existe, los recursos ocupados por el proceso se reciclan, no hay pérdida de memoria, de hecho, los recursos del sistema básicamente no se desperdician, consulte el curso de Song Baohua).
Evite los procesos zombies: el
proceso zombi causa:
1. Una vez que finaliza el proceso hijo, envíe una señal SIGCHLD al proceso padre, y el proceso padre lo ignorará de forma predeterminada;
2. El proceso padre no llama a wait () o waitpid () a espere el final del proceso hijo.
Métodos para evitar procesos zombies:
1. El proceso padre llama a wait () o waitpid () para esperar el final del proceso hijo De esta manera, el proceso padre generalmente se bloquea en espera y no puede manejar otras cosas.
2. Capture la señal SIGCHLD y llame a la función de espera en la función de procesamiento de señal. Este procesamiento puede evitar el problema descrito en 1.
3. Bifurcar dos veces, el proceso padre crea el proceso hijo, el proceso hijo crea un proceso nieto, y luego el proceso hijo se suicida y el proceso nieto se convierte en un proceso huérfano y es adoptado por el proceso init.
1.3 Comunicación entre procesos
1) Señales Las
señales aquí se refieren a eventos. Por ejemplo, presionar la combinación de teclas CTRL-C enviará una señal SIGINT, que se puede capturar en el proceso y procesar en consecuencia.
2) Pipeline PIPE
es un archivo y la operación de la tubería es similar a la de un archivo.
La función popen () es similar a la función fopen () y devuelve un puntero de objeto.
La función pipe () es similar a la función open () y devuelve un descriptor de objeto.
La canalización es para la transmisión de datos entre procesos relativos (procesos relacionados creados por el mismo proceso padre).
3) Tubo con nombre FIFO El tubo con
nombre se puede utilizar para la comunicación entre procesos sin parentesco.
mkfifo () / mknod () creará un archivo con la ruta y el nombre en el sistema de archivos. Simplemente use este archivo de tubería como un archivo ordinario, y luego se puede realizar la comunicación entre procesos.
4) Semáforo El
semáforo, la cola de mensajes y la memoria compartida son mecanismos de IPC del Sistema V.
Área crítica: un área de código a la que solo puede acceder un proceso en cualquier momento.
Semáforo: la mayoría de las comunicaciones entre procesos solo requieren semáforos binarios, por lo que aquí solo se tratan los semáforos binarios. Antes de ingresar a la sección crítica, realice la operación P (si el semáforo es mayor que 1, reste 1 e ingrese a la sección crítica, de lo contrario se suspenderá el proceso); al salir de la sección crítica, realice la operación V (si algún proceso es esperando ser suspendido, despiértelo, de lo contrario Semáforo más 1).
Mutex: El semáforo Mutex es un subconjunto del semáforo binario.
5) Cola de mensajes
Similar a las tuberías con nombre, pero no tiene que considerar las complicadas operaciones de abrir / cerrar tuberías. La cola de mensajes existe independientemente del proceso.
6) Memoria compartida
Los procesos que necesitan comunicarse comparten una parte de la memoria para el intercambio de datos.

[Beneficios del artículo] Materiales de aprendizaje para arquitectos de servidores C / C ++ Linux más el grupo 832218493 (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í

2 La esencia de la realización del hilo de proceso

El planificador de Linux realmente reconoce task_struct para la planificación.
Independientemente del subproceso del proceso, la capa inferior corresponde a un task_struct. La diferencia entre un proceso y un subproceso es la cantidad de recursos compartidos. Dos procesos no comparten recursos en absoluto, y todos los recursos se comparten entre dos subprocesos.

2.1 Después de que fork ()
ejecuta la bifurcación, el task_struck del proceso padre se copia al proceso hijo. Inicialmente, los recursos de los procesos padre e hijo son exactamente iguales, pero son dos copias diferentes, por lo que cualquier cambio provocará la dos para dividir.
Inserte la descripción de la imagen aquí

Los procesos padre e hijo usan tecnología COW (Copy-On-Write, copy-on-write) para administrar los recursos de memoria (mm):

1 \ Antes de la bifurcación, una parte del área de memoria corresponde a una dirección física y una dirección virtual, y el permiso del área de memoria es RW;

2 \ Después de la bifurcación, la dirección virtual y la dirección física del área de memoria vista por los procesos padre e hijo son las mismas, y los procesos padre e hijo realmente usan la misma pieza de memoria física. No se produce ninguna copia de memoria y el proceso operativo el sistema cambiará los permisos de esta área de memoria a RO;

3 \ El proceso principal o secundario que escribe en el área de memoria activará PageFault. El sistema operativo copiará el área de memoria en este momento. La dirección virtual vista por el proceso principal y secundario sigue siendo la misma, pero la dirección física ya es diferente . El mapeo de direcciones virtuales a direcciones físicas de cada proceso es administrado por MMU (Unidad de Gestión de Memoria, unidad de gestión de memoria).

Inserte la descripción de la imagen aquí

Fork se ejecuta en una CPU con MMU.

2.2 vfork ()
Inserte la descripción de la imagen aquí

Para las CPU sin MMU, no se puede aplicar COW y no se admite la bifurcación.
Una CPU sin MMU usa vfork para crear un proceso, y el proceso padre se bloqueará hasta que el proceso hijo salga o ejecute.
La diferencia esencial entre vfork y fork es que los procesos padre e hijo en vfork comparten la misma área de memoria.

2.3 pthread_create ()

Inserte la descripción de la imagen aquí

Un hilo de Linux es esencialmente un proceso, pero es diferente de compartir recursos entre procesos. Todos los recursos se comparten entre hilos, como se muestra en la figura anterior.
Cada subproceso tiene su propia task_struct, por lo que la CPU puede programar cada subproceso. Los mismos recursos de proceso se comparten entre varios subprocesos. Estos dos puntos solo cumplen con la definición de hilo.
Así es como Linux implementa subprocesos con procesos, por lo que los subprocesos también se denominan procesos ligeros.

2.4 PID y TGID

Inserte la descripción de la imagen aquí

POSIX requiere que varios subprocesos del mismo proceso obtengan un ID de proceso para obtener un valor de ID único.
En el subproceso múltiple del mismo proceso en Linux, en la perspectiva del kernel, cada subproceso en realidad tiene un PID, pero en el espacio de usuario, getpid necesita devolver un valor único. Linux usa un pequeño truco para introducir el concepto de TGID, el TGID devuelto por el valor getpid ().
El comando superior desde la perspectiva del proceso:

El comando superior sin parámetros (predeterminado) muestra la utilización de la CPU de un solo núcleo por el proceso. Por ejemplo, hay tres subprocesos en un proceso, el subproceso principal crea el subproceso 1 y el subproceso 2, y tanto el subproceso 1 como el subproceso 2 llaman one while (1), para CPU de doble núcleo, el subproceso 1 y el subproceso 2 utilizan cada uno un núcleo, y la tasa de ocupación es del 100%. La tasa de utilización de la CPU del proceso visto por el comando superior es del 200%, y el ID del proceso es el PID del hilo principal (que es TGID).

El comando superior
desde la perspectiva del subproceso : el comando top -H muestra la tasa de ocupación de la CPU desde la perspectiva del subproceso. En el ejemplo anterior, mostrará que la tasa de ocupación del subproceso 1 es 100% y la tasa de ocupación del subproceso 2 es 100%.
El PID del hilo se refiere al ID del proceso en el espacio de usuario, y el valor es TGID; cuando se indica específicamente, el PID del hilo en el espacio del kernel se refiere al PID único del hilo en el task_struct en el kernel.

3 programación de procesos

Inserte la descripción de la imagen aquí

3.1 Programación de procesos en tiempo real
SCHED_FIFO: Diferentes prioridades se ejecutan para dormir según la prioridad más alta, y luego se ejecutan en la prioridad más baja; la misma prioridad es primero en entrar, primero en salir.
SCHED_RR: Diferentes prioridades se ejecutan para dormir de acuerdo con la prioridad más alta, y luego corren a la prioridad más baja; la misma prioridad rota.
Parche de Kernel RT: Los
siguientes dos parámetros
/ proc / sys / kernel / sched_rt_period_us
/ proc / sys / kernel / sched_rt_runtime_us
indican que RT solo puede ejecutarse en tiempo de ejecución en el período de tiempo.
3.2 Programación de procesos ordinarios
SCHED_OTHER:

3.2.1 Prioridad dinámica (principios de 2.6) El
proceso tiene dos parámetros de medición: consumo de E / S y consumo de CPU.
Prioridad alta significa: 1) obtener más intervalos de tiempo, 2) puede adelantarse a la prioridad baja al despertar. Los segmentos de tiempo giran.
El kernel almacena la prioridad estática y el usuario puede modificar la prioridad estática a través de nice.
La prioridad dinámica del proceso se calcula en tiempo real de acuerdo con la prioridad estática. El algoritmo de programación recompensa el consumo de E / S (aumentando la prioridad para aumentar el rendimiento en tiempo real) y penaliza el tipo de consumo de CPU (bajando la prioridad para reducir el tiempo real actuación)

3.2.2 CFS:
árbol rojo-negro de programación completamente justa (nuevo kernel) , el nodo izquierdo es más pequeño que el valor del nodo derecho.
Ejecute el proceso
con el menor tiempo de ejecución hasta el momento. Al mismo tiempo, CPU / IO y buen
siempre encuentre la programación de subprocesos con el menor tiempo de ejecución.
vruntime = pruntime / weight × 1024;
vruntime es el tiempo de ejecución virtual, pruntime es el tiempo de ejecución físico, y el peso está determinado por el valor agradable (cuanto menor es agradable, mayor es el peso), el hilo con el menor tiempo de ejecución y el Un valor agradable más bajo tendrá un tiempo de ejecución más pequeño. Obtenga la programación de prioridad. Este es un proceso que cambia dinámicamente con la operación.

Inserte la descripción de la imagen aquí

Herramientas chrt y renice:

设置SCHED_FIFO和50 RT优先级

chrt -f -a -p 50 10576
 设置nice 
 # renice -n -5 -g 9394 
 # nice -n 5 ./a.out

Supongo que te gusta

Origin blog.csdn.net/lingshengxueyuan/article/details/112615517
Recomendado
Clasificación