"Los principios subyacentes de los procesos, subprocesos y descriptores de archivos de Linux.
Con diez años de experiencia en desarrollo, los arquitectos de Alibaba derriban los principios y documentos de práctica de Spring Boot
Artículo de los arquitectos de Alibaba: Principios básicos y prácticas de aplicación de Redis, lo guiarán para derribar Redis
Explicación detallada de los principios estructurales de Tomcat
Hablando de procesos, probablemente la pregunta más común en las entrevistas es la relación entre subprocesos y procesos, así que respondamos primero: en los sistemas Linux, Los procesos y subprocesos son casi iguales. No hay diferencia. La respuesta es que en Linux casi no hay diferencia entre procesos y subprocesos.
El proceso en Linux es en realidad una estructura de datos. Por cierto, puede aprender sobre los principios de funcionamiento subyacentes de los descriptores de archivos, las redirecciones y los comandos de canalización. Finalmente, analizamos por qué se utilizan los subprocesos desde la perspectiva. del sistema operativo Básicamente no hay diferencia entre los procesos.
En primer lugar, hablando de manera abstracta, nuestra computadora es esta cosa:
El rectángulo grande representa el espacio de memoria de la computadora, el rectángulo pequeño representa el proceso, el círculo en la parte inferior La esquina izquierda representa el disco y el pequeño rectángulo representa el Proceso, el círculo en la esquina inferior izquierda representa el disco y el gráfico en la esquina inferior derecha representa el dispositivo de entrada/salida, como mouse, teclado, monitor, etc. Además, tenga en cuenta que el espacio de la memoria se divide en dos partes: la mitad superior es el espacio del usuario y la mitad inferior es el espacio del kernel.
El espacio del usuario contiene recursos que el proceso del usuario necesita usar. Por ejemplo, si abre una matriz en el código del programa, entonces esta matriz debe existir en el espacio del kernel para almacenar los recursos del sistema que el proceso del kernel necesita. Algunos de estos recursos normalmente no son accesibles para los usuarios. Sin embargo, tenga en cuenta que algunos procesos de usuario accederán a ciertos recursos del espacio del kernel, como ciertas bibliotecas de enlaces dinámicos, etc.
Escribimos un programa hola en lenguaje C, lo compilamos para obtener un archivo ejecutable, lo ejecutamos en la línea de comando, imprimimos un hola mundo y luego el programa sale. A nivel del sistema operativo, este es un nuevo proceso que lee nuestro archivo ejecutable compilado en el espacio de la memoria, luego lo ejecuta y finalmente sale.
El ejecutable que compila es solo un archivo, no un proceso, y el ejecutable debe cargarse en la memoria y empaquetarse como un proceso para que realmente se ejecute. Los procesos dependen del sistema operativo para su creación y cada proceso tiene sus propios atributos inherentes, como el número de proceso (PID), el estado del proceso, los archivos abiertos, etc. Una vez creado el proceso, el sistema lo lee en su programa y luego ejecuta su programa.
Entonces, ¿cómo crea el sistema operativo un proceso? Para el sistema operativo, un proceso es una estructura de datos, así que veamos directamente el código fuente de Linux:
task_struct es la descripción del proceso del kernel de Linux, también conocida como "descriptor de proceso". El código fuente es bastante complejo, por lo que aquí sólo he seleccionado una pequeña selección de los más comunes.
Hablemos de punteros milimétricos y punteros de archivos. El puntero mm apunta a la memoria virtual del proceso, que es donde se cargan los recursos y los archivos ejecutables, mientras que el puntero de archivos apunta a una serie de punteros a todos los archivos abiertos por el proceso.
Comencemos con los archivos, que son una matriz de punteros de archivos. En general, un proceso lee entradas de archivos [0], escribe salidas en archivos [1] y escribe mensajes de error en archivos [2].
Por ejemplo, desde nuestra perspectiva, la función C printf imprime caracteres en la línea de comando, pero desde la perspectiva del proceso, escribe datos en el archivo [1] de manera similar, la función scanf es el intento del proceso de hacerlo; Leer datos de archivos de archivos [0].
Cuando se crea cada proceso, los primeros tres caracteres del archivo se rellenan con valores predeterminados, que apuntan al flujo de entrada estándar, al flujo de salida estándar y al flujo de error estándar, respectivamente.
Lo que llamamos "descriptor de archivo" es el índice de esta matriz de punteros de archivo, por lo que, de forma predeterminada, el descriptor de archivo del programa es 0 para entrada, 1 para salida y 2 para error.
Podemos volver a dibujar la imagen:
Para una computadora típica, el flujo de entrada es el teclado, el flujo de salida es la pantalla y el flujo de error es el monitor, así que ahora entre el proceso y el núcleo Hay tres cables conectados. Dado que todo el hardware es administrado por el kernel, nuestro proceso debe permitir que el proceso del kernel acceda a los recursos del hardware a través de "llamadas al sistema".
PD: No olvides que todo en Linux se abstrae como un archivo, y los dispositivos también son archivos y se pueden leer y escribir.
Si escribimos un programa que requiere otros recursos, como abrir un archivo para leerlo o escribirlo, es fácil realizar una llamada al sistema al kernel para abrir el archivo, y el archivo colocarse en el archivo El cuarto bit de, que corresponde al descriptor de archivo 3:
Al comprender este principio de funcionamiento, la redirección de entrada es fácil de entender.
La redirección de salida significa que cuando el programa quiere leer datos, archivos[0] apunta a un archivo, de modo que el programa leerá los datos del archivo en lugar del teclado:
De manera similar, la redirección de salida se refiere a apuntar archivos[1] a un archivo, de modo que la salida del programa no se escribirá en el monitor, sino al teclado:
La redirección de salida se refiere a apuntar archivos[1] ] Apunte a un archivo, de modo que la salida del programa no se escriba en el monitor, sino en el archivo:
Lo mismo ocurre con la redirección de errores, por lo que no entraré en detalles.
Una conexión de tubería es en realidad lo mismo, conecta el flujo de salida de un proceso con el flujo de entrada de otro proceso y pasa los datos en una "tubería", lo cual es muy inteligente:
Llegados a este punto, es posible que hayas comprendido la sutileza de la filosofía de diseño de que "todo en Linux es un archivo", ya sea un dispositivo, otro proceso, un socket o un archivo real, todos están en el mismo lugar . Ya sean dispositivos, otros procesos, sockets o archivos reales, todos se pueden leer y escribir, unificados en una simple matriz de archivos. El proceso accede a los recursos correspondientes a través de simples descriptores de archivos, dejando los detalles al sistema operativo, logrando así de manera efectiva el desacoplamiento y la eficiencia.
En primer lugar, debe quedar claro que el multiprocesamiento y el multiproceso son concurrentes y pueden mejorar la utilización del procesador, por lo que la clave ahora es la diferencia entre multiproceso y multiprocesamiento.
En Linux, básicamente no hay diferencia entre subprocesos y procesos. La razón es que desde la perspectiva del kernel de Linux, no trata los subprocesos y los procesos de manera diferente.
Sabemos que la llamada al sistema fork() creará un nuevo proceso hijo y la función pthread() creará un nuevo hilo. Pero tanto los subprocesos como los procesos están representados por estructuras task_struct, la única diferencia es el área de datos que disfrutamos mucho.
En otras palabras, el hilo no se ve diferente del proceso, excepto que algunas áreas de datos del hilo están habilitadas como el hilo principal, y el hilo secundario es solo una copia, no ** **-activado. Por ejemplo, las estructuras de mm y las estructuras de archivos están en subprocesos. Dibujaré algunos diagramas y entenderás lo que significa:
Por lo tanto, nuestro programa multiproceso debe utilizar un mecanismo de bloqueo para evitar que varios subprocesos escriban en la misma área al mismo tiempo, lo que resultará en en la discrepancia de datos.
Entonces puede preguntar, dado que los procesos y los subprocesos son básicamente los mismos, y los datos de múltiples procesos no son ****, es decir, no hay ningún problema de desalineación de datos, entonces, ¿por qué los subprocesos múltiples son más? común que multiproceso?
Debido a que la concurrencia es más común en el intercambio de datos del mundo real, por ejemplo, diez personas retiran diez dólares de una cuenta al mismo tiempo, queremos que el saldo de la cuenta compartida se reduzca correctamente en cien. dólares, en lugar de esperar que todos obtengan una copia de la cuenta y obtengan diez dólares de descuento en cada copia.
Por supuesto, es importante tener en cuenta que sólo Linux trata los subprocesos como procesos de datos que disfrutan y no los trata como procesos especiales, mientras que muchos otros sistemas operativos tratan los subprocesos y procesos de la misma manera. son diferentes y los subprocesos tienen sus propias estructuras de datos específicas, que personalmente creo que son inferiores a las utilizadas por Linux. Personalmente creo que no es tan simple como está diseñado Linux, lo que aumenta la complejidad del sistema.
La creación de nuevos subprocesos y procesos en Linux es muy eficiente. Linux utiliza una estrategia de copia en escritura para optimizar la copia de áreas de memoria al crear nuevos procesos, es decir, en realidad no copia el espacio de memoria. del proceso padre se copia cuando es necesario escribirlo. Entonces los nuevos procesos y nuevos hilos en Linux son muy rápidos.