Hilos vs Procesos en Linux


253

Recientemente escuché a algunas personas decir que en Linux, casi siempre es mejor usar procesos en lugar de hilos, ya que Linux es muy eficiente en el manejo de procesos y porque hay tantos problemas (como el bloqueo) asociados con los hilos. Sin embargo, sospecho, porque parece que los hilos podrían dar una ganancia de rendimiento bastante grande en algunas situaciones.

Entonces, mi pregunta es, cuando me enfrento a una situación en la que los subprocesos y los procesos podrían manejarse bastante bien, ¿debería usar procesos o subprocesos? Por ejemplo, si estuviera escribiendo un servidor web, ¿debería usar procesos o hilos (o una combinación)?


¿Hay alguna diferencia con Linux 2.4?
mouviciel

3
La diferencia entre procesos y subprocesos en Linux 2.4 es que los subprocesos comparten más partes de su estado (espacio de direcciones, identificadores de archivos, etc.) que los procesos, que generalmente no lo hacen. El NPTL bajo Linux 2.6 lo hace un poco más claro al darles "grupos de hilos" que son un poco como "procesos" en win32 y Solaris.
MarkR

66
La programación concurrente es difícil. A menos que necesite un rendimiento muy alto, el aspecto más importante en su compensación a menudo será la dificultad de depuración . Los procesos hacen la solución mucho más fácil a este respecto, porque toda la comunicación es explícita (fácil de verificar, registrar, etc.). Por el contrario, la memoria compartida de hilos crea miles de millones de lugares donde un hilo puede impactar erróneamente en otro.
Lutz Prechelt

1
@LutzPrechelt: la programación concurrente puede ser multiproceso o multiproceso. No veo por qué estás asumiendo que la programación concurrente es de subprocesos múltiples solamente. Puede deberse a algunas limitaciones de idioma en particular, pero en general puede ser a la vez.
iankit

2
Enlace Lutz simplemente declaró que la programación concurrente es difícil, cualquiera que sea el elegido (proceso o subprocesos), pero que la programación concurrente que utiliza procesos facilita la depuración en muchos casos.
user2692263

Respuestas:


322

Linux usa un modelo de subprocesos 1-1, con (para el núcleo) ninguna distinción entre procesos y subprocesos; todo es simplemente una tarea ejecutable. * *

En Linux, la llamada al sistema cloneclona una tarea, con un nivel de uso compartido configurable, entre los cuales se encuentran:

  • CLONE_FILES: comparte la misma tabla de descriptores de archivo (en lugar de crear una copia)
  • CLONE_PARENT: no configure una relación padre-hijo entre la nueva tarea y la antigua (de lo contrario, child getppid()= parent's getpid())
  • CLONE_VM: comparte el mismo espacio de memoria (en lugar de crear una copia COW )

fork()llamadas clone(menos compartidas )y pthread_create()llamadas clone(más compartidas ). ** **

forking cuesta un poquito más que pthread_createing debido a la copia de tablas y la creación de mapeos COW para la memoria, pero los desarrolladores del kernel de Linux han intentado (y han tenido éxito) minimizar estos costos.

Cambiar entre tareas, si comparten el mismo espacio de memoria y varias tablas, será un poco más barato que si no se comparten, porque los datos ya pueden estar cargados en caché. Sin embargo, el cambio de tareas sigue siendo muy rápido, incluso si no se comparte nada; esto es algo más que los desarrolladores del kernel de Linux intentan garantizar (y logran garantizar).

De hecho, si está en un sistema multiprocesador, no compartir puede ser realmente beneficioso para el rendimiento: si cada tarea se ejecuta en un procesador diferente, la sincronización de la memoria compartida es costosa.


* Simplificado CLONE_THREADhace que se comparta la entrega de señales (que necesita CLONE_SIGHAND, que comparte la tabla del controlador de señales).

** Simplificado Existen tanto SYS_forky SYS_clonellamadas al sistema, pero en el núcleo, el sys_forky sys_cloneson ambos muy finas envolturas alrededor de la misma do_forkfunción, que en sí es una envoltura delgada alrededor copy_process. Sí, los términos process, thready taskse usan indistintamente en el kernel de Linux ...


66
Creo que nos falta 1 punto. Si realiza múltiples procesos para su servidor web, entonces tiene que escribir otro proceso para abrir el socket y pasar 'trabajo' a diferentes hilos. El enhebrado ofrece un solo proceso, múltiples hilos, diseño limpio. En muchas situaciones, el hilo es simplemente natural y en otras situaciones, un nuevo proceso es simplemente natural. Cuando el problema cae en un área gris, las otras compensaciones, como se explica por ephemient, se vuelven importantes.
Saurabh

26
@Saurabh No realmente. Puede fácilmente socket, bind, listen, fork, y luego tener múltiples procesos acceptconexiones en el mismo socket de escucha. Un proceso puede dejar de aceptar si está ocupado, y el kernel enrutará las conexiones entrantes a otro proceso (si nadie está escuchando, el kernel se pondrá en cola o caerá, dependiendo de la listenacumulación). No tienes mucho más control sobre la distribución del trabajo que eso, ¡pero generalmente eso es lo suficientemente bueno!
Ephemient

2
@Bloodcount Todos los procesos / hilos en Linux son creados por el mismo mecanismo, que clona un proceso / hilo existente. Se pasan banderas para clone()determinar qué recursos se comparten. Una tarea también puede unshare()recursos en cualquier momento posterior.
Ephemient

44
@KarthikBalaguru Dentro del núcleo, hay una task_structpara cada tarea. Esto a menudo se denomina "proceso" en todo el código del kernel, pero corresponde a cada subproceso ejecutable. No existe process_struct; si un grupo de task_structs están vinculados por su thread_grouplista, entonces son el mismo "proceso" para el espacio de usuario. Hay un poco de manejo especial de "hilos", por ejemplo, todos los hilos hermanos se detienen en fork y exec, y solo aparece el hilo "principal" ls /proc. Sin /proc/pidembargo, se puede acceder a todos los hilos , ya sea que se enumeren /proco no.
Ephemient

55
@KarthikBalaguru El núcleo admite un continuo de comportamiento entre hilos y procesos; por ejemplo, clone(CLONE_THREAD | CLONE_VM | CLONE_SIGHAND))le daría un nuevo "hilo" que no comparte directorio de trabajo, archivos o bloqueos, mientras que clone(CLONE_FILES | CLONE_FS | CLONE_IO)le daría un "proceso" que sí lo hace. El sistema subyacente crea tareas mediante la clonación; fork()y pthread_create()son solo funciones de biblioteca que invocan de manera clone()diferente (como escribí en esta respuesta).
Ephemient

60

Linux (y de hecho Unix) le ofrece una tercera opción.

Opción 1 - procesos

Cree un ejecutable independiente que maneje una parte (o todas las partes) de su aplicación e invoque por separado para cada proceso, por ejemplo, el programa ejecuta copias de sí mismo para delegar tareas.

Opción 2 - hilos

Cree un ejecutable independiente que se inicie con un solo subproceso y cree subprocesos adicionales para realizar algunas tareas.

Opción 3 - tenedor

Solo disponible bajo Linux / Unix, esto es un poco diferente. Un proceso bifurcado realmente es su propio proceso con su propio espacio de direcciones: no hay nada que el niño pueda hacer (normalmente) para afectar el espacio de direcciones de sus padres o hermanos (a diferencia de un hilo), por lo que obtendrá mayor robustez.

Sin embargo, las páginas de memoria no se copian, son de copia en escritura, por lo que generalmente se usa menos memoria de la que pueda imaginar.

Considere un programa de servidor web que consta de dos pasos:

  1. Leer datos de configuración y tiempo de ejecución
  2. Servir solicitudes de página

Si usó hilos, el paso 1 se haría una vez, y el paso 2 en varios hilos. Si utilizó procesos "tradicionales", los pasos 1 y 2 tendrían que repetirse para cada proceso, y la memoria para almacenar la configuración y los datos de tiempo de ejecución duplicados. Si usó fork (), puede hacer el paso 1 una vez y luego fork (), dejando los datos de tiempo de ejecución y la configuración en la memoria, intactos, no copiados.

Entonces hay realmente tres opciones.


77
La bifurcación de @Qwertie no es tan genial, rompe muchas bibliotecas de manera sutil (si las usa en el proceso principal). Crea un comportamiento inesperado que confunde incluso a los programadores experimentados.
MarkR

2
@MarkR ¿podría dar algunos ejemplos o un enlace de cómo bifurcar rompe la biblioteca y crea un comportamiento inesperado?
Ehtesh Choudhury

18
Si un proceso se bifurca con una conexión mysql abierta, suceden cosas malas, ya que el socket se comparte entre dos procesos. Incluso si solo un proceso usa la conexión, el otro impide que se cierre.
MarkR

1
La llamada al sistema fork () está especificada por POSIX (lo que significa que está disponible en cualquier sistema Unix), si utilizó la API de Linux subyacente, que es la llamada al sistema clone (), en realidad tiene aún más opciones en Linux que solo los tres .
Lie Ryan

2
@MarkR El uso compartido del zócalo es por diseño. Además, cualquiera de los procesos puede cerrar el socket usando linux.die.net/man/2/shutdown antes de llamar a close () en el socket.
Lelanthran

53

Eso depende de muchos factores. Los procesos son más pesados ​​que los subprocesos y tienen un mayor costo de inicio y cierre. La comunicación entre procesos (IPC) también es más difícil y lenta que la comunicación entre hilos.

Por el contrario, los procesos son más seguros y seguros que los subprocesos, porque cada proceso se ejecuta en su propio espacio de direcciones virtuales. Si un proceso falla o tiene un desbordamiento del búfer, no afecta a ningún otro proceso en absoluto, mientras que si un subproceso se bloquea, elimina todos los otros subprocesos en el proceso, y si un subproceso tiene un desbordamiento del búfer, se abre Un agujero de seguridad en todos los hilos.

Por lo tanto, si los módulos de su aplicación pueden ejecutarse principalmente de forma independiente con poca comunicación, probablemente debería usar procesos si puede pagar los costos de inicio y cierre. El impacto de rendimiento de IPC será mínimo y estará un poco más seguro contra errores y agujeros de seguridad. Si necesita todo el rendimiento que puede obtener o tener muchos datos compartidos (como estructuras de datos complejas), use hilos.


99
La respuesta de Adam serviría bien como una sesión informativa ejecutiva. Para más detalles, MarkR y ephemient proporcionan buenas explicaciones. Puede encontrar una explicación muy detallada con ejemplos en cs.cf.ac.uk/Dave/C/node29.html, pero parece estar un poco anticuado en algunas partes.
CyberFonic

2
CyberFonic es cierto para Windows. Como dice Ephemient en Linux, los procesos no son más pesados. Y bajo Linux, todos los mecanismos disponibles para la comunicación entre subprocesos (futex, memoria compartida, canalizaciones, IPC) también están disponibles para procesos y se ejecutan a la misma velocidad.
Russell Stuart

IPC es más difícil de usar, pero ¿qué pasa si alguien usa "memoria compartida"?
abhiarora

11

Otros han discutido las consideraciones.

Quizás la diferencia importante es que en Windows los procesos son pesados ​​y caros en comparación con los subprocesos, y en Linux la diferencia es mucho menor, por lo que la ecuación se equilibra en un punto diferente.


9

Había una vez un Unix y en este viejo y bueno Unix había mucha sobrecarga para los procesos, por lo que lo que hicieron algunas personas inteligentes fue crear hilos, que compartirían el mismo espacio de direcciones con el proceso padre y solo necesitaban un contexto reducido cambiar, lo que haría que el cambio de contexto sea más eficiente.

En un Linux contemporáneo (2.6.x) no hay mucha diferencia en el rendimiento entre un cambio de contexto de un proceso en comparación con un hilo (solo el material MMU es adicional para el hilo). Existe el problema con el espacio de direcciones compartido, lo que significa que un puntero defectuoso en un hilo puede dañar la memoria del proceso principal u otro hilo dentro del mismo espacio de direcciones.

Un proceso está protegido por la MMU, por lo que un puntero defectuoso causará una señal 11 y no habrá corrupción.

En general, usaría procesos (no mucha sobrecarga de cambio de contexto en Linux, pero protección de memoria debido a MMU), pero pthreads si necesitara una clase de planificador en tiempo real, que es una taza de té diferente en conjunto.

¿Por qué crees que los hilos tienen una ganancia de rendimiento tan grande en Linux? ¿Tienes algún dato para esto, o es solo un mito?


1
Sí, tengo algunos datos. Ejecuté una prueba que crea 100,000 procesos y una prueba que crea 100,000 hilos. La versión del subproceso corrió aproximadamente 9 veces más rápido (17.38 segundos para los procesos, 1.93 para los subprocesos). Ahora, esto solo prueba el tiempo de creación, pero para tareas de corta duración, el tiempo de creación puede ser clave.
user17918

44
@ user17918 - ¿Es posible que usted comparta el código utilizado por usted para calcular los tiempos mencionados anteriormente?
codingfreak

una gran diferencia, con los procesos, el kernel crea una tabla de páginas para cada proceso y los cabezales usan solo tablas de una página, así que creo que es normal que los hilos sean más rápidos que los procesos
c4f4t0r

Otra forma simple de verlo es que TCB es bastante más pequeño que PCB y, por lo tanto, es obvio que el cambio de contexto de proceso que involucra PCB consumirá un poco más de tiempo que el cambio de hilos.
Karthik Balaguru

5

¿Qué tan estrechamente acopladas están tus tareas?

Si pueden vivir independientemente el uno del otro, entonces use procesos. Si dependen el uno del otro, entonces use hilos. De esa manera puede matar y reiniciar un proceso incorrecto sin interferir con el funcionamiento de las otras tareas.


4

Para complicar aún más las cosas, existe el almacenamiento local de subprocesos y la memoria compartida de Unix.

El almacenamiento local de subprocesos permite que cada subproceso tenga una instancia separada de objetos globales. La única vez que lo usé fue cuando construí un entorno de emulación en linux / windows, para el código de aplicación que se ejecutó en un RTOS. En el RTOS, cada tarea era un proceso con su propio espacio de direcciones, en el entorno de emulación, cada tarea era un hilo (con un espacio de direcciones compartido). Al usar TLS para cosas como singletons, pudimos tener una instancia separada para cada hilo, al igual que en el entorno RTOS 'real'.

La memoria compartida puede (obviamente) brindarle los beneficios de rendimiento de tener múltiples procesos que acceden a la misma memoria, pero con el costo / riesgo de tener que sincronizar los procesos correctamente. Una forma de hacerlo es hacer que un proceso cree una estructura de datos en la memoria compartida y luego envíe un identificador a esa estructura a través de la comunicación tradicional entre procesos (como una tubería con nombre).


1
Utilicé el almacenamiento local de subprocesos para una recopilación de estadísticas, la última vez que escribí un programa de redes de subprocesos: cada subproceso escribió en sus propios contadores, no se necesitan bloqueos, y solo cuando se envió un mensaje cada subproceso combinaría sus estadísticas en los totales globales. Pero sí, TLS no se usa con mucha frecuencia ni es necesario. Memoria compartida, por otro lado ... además de enviar datos de manera eficiente, también puede compartir los semáforos POSIX entre procesos colocándolos en la memoria compartida. Es bastante asombroso.
Ephemient

4

En mi trabajo reciente con LINUX, una cosa a tener en cuenta son las bibliotecas. Si está utilizando subprocesos, asegúrese de que las bibliotecas que pueda usar en todos los subprocesos sean seguras para subprocesos. Esto me quemó un par de veces. Notablemente libxml2 no es seguro para subprocesos fuera de la caja. Se puede compilar con subprocesos seguros, pero eso no es lo que obtienes con aptitude install.


3

Tendría que estar de acuerdo con lo que has estado escuchando. Cuando comparamos nuestro clúster ( xhply demás), siempre obtenemos un rendimiento significativamente mejor con los procesos a través de subprocesos.</anecdote>


3

La decisión entre hilo / proceso depende un poco de para qué la usará. Uno de los beneficios de un proceso es que tiene un PID y puede eliminarse sin terminar también con el padre.

Para un ejemplo del mundo real de un servidor web, apache 1.3 solía soportar solo múltiples procesos, pero en 2.0 agregaron una abstracción para que pueda alternar entre ambos. Los comentarios parecen estar de acuerdo en que los procesos son más robustos, pero los subprocesos pueden ofrecer un rendimiento un poco mejor (excepto en las ventanas donde el rendimiento de los procesos apesta y solo desea usar subprocesos).


2

Para la mayoría de los casos, preferiría procesos sobre hilos. los subprocesos pueden ser útiles cuando tiene una tarea relativamente más pequeña (sobrecarga del proceso >> tiempo empleado por cada unidad de tarea dividida) y existe la necesidad de compartir la memoria entre ellos. Piensa en una gran variedad. Además (fuera del tema), tenga en cuenta que si la utilización de su CPU es del 100 por ciento o cercana, no habrá ningún beneficio con el procesamiento o subprocesamiento múltiple. (de hecho empeorará)


¿Qué quieres decir con ningún beneficio? ¿Qué tal realizar cálculos pesados ​​en el hilo de la GUI? Moverlos a un subproceso paralelo será mucho mejor desde un punto de experiencia del usuario, sin importar cómo se cargue la CPU.
olegst

2

Subprocesos -> Los subprocesos comparten un espacio de memoria, es una abstracción de la CPU, es liviano. Procesos -> Los procesos tienen su propio espacio de memoria, es una abstracción de una computadora. Para paralelizar la tarea, necesita abstraer una CPU. Sin embargo, las ventajas de usar un proceso sobre un hilo son la seguridad, la estabilidad, mientras que un hilo usa menos memoria que el proceso y ofrece una menor latencia. Un ejemplo en términos de web sería Chrome y Firefox. En el caso de Chrome, cada pestaña es un proceso nuevo, por lo tanto, el uso de memoria de Chrome es mayor que el de Firefox, mientras que la seguridad y la estabilidad proporcionadas son mejores que las de Firefox. La seguridad aquí proporcionada por Chrome es mejor, ya que cada pestaña es un proceso nuevo. Una pestaña diferente no puede espiar en el espacio de memoria de un proceso dado.


2

Creo que todos han hecho un gran trabajo respondiendo a su pregunta. Solo estoy agregando más información sobre hilo versus proceso en Linux para aclarar y resumir algunas de las respuestas anteriores en el contexto del núcleo. Entonces, mi respuesta es con respecto al código específico del kernel en Linux. De acuerdo con la documentación del kernel de Linux, no existe una distinción clara entre el hilo y el proceso, excepto que el hilo usa un espacio de direcciones virtuales compartido a diferencia del proceso. También tenga en cuenta que el Kernel de Linux utiliza el término "tarea" para referirse al proceso y al hilo en general.

"No hay estructuras internas que implementen procesos o subprocesos, en su lugar hay una estructura task_struct que describe una unidad de programación abstracta llamada tarea"

Además, según Linus Torvalds, NO debe pensar en el proceso versus el hilo en absoluto y porque es demasiado limitante y la única diferencia es COE o Contexto de ejecución en términos de "separar el espacio de direcciones del padre" o el espacio de direcciones compartido. De hecho, usa un ejemplo de servidor web para hacer su punto aquí (que recomiendo leer).

Crédito completo a la documentación del kernel de Linux


-3

Si necesita compartir recursos, realmente debería usar hilos.

Considere también el hecho de que los cambios de contexto entre subprocesos son mucho menos costosos que los cambios de contexto entre procesos.

No veo ninguna razón para ir explícitamente con procesos separados a menos que tenga una buena razón para hacerlo (seguridad, pruebas de rendimiento comprobadas, etc.)


3
Tengo el representante para editar, pero no estoy del todo de acuerdo. El cambio de contexto entre procesos en Linux es casi tan barato como el cambio de contexto entre hilos.
Ephemient
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.