¿Qué recursos se comparten entre hilos?


264

Recientemente, me han hecho una pregunta en una entrevista cuál es la diferencia entre un proceso y un hilo. Realmente, no sabía la respuesta. Pensé por un minuto y di una respuesta muy extraña.

Los hilos comparten la misma memoria, los procesos no. Después de responder esto, el entrevistador me dio una sonrisa malvada y me lanzó las siguientes preguntas:

P. ¿Conoces los segmentos en los que se divide un programa?

Mi respuesta: sí (pensé que era fácil) Pila, datos, código, montón

P. Entonces, dime: ¿qué segmentos comparten los hilos?

No pude responder a esto y terminé diciendo todas.

Por favor, ¿alguien puede presentar las respuestas correctas e impresionantes para la diferencia entre un proceso y un hilo?


9
Los hilos comparten el mismo espacio de direcciones virtual , el proceso no.
Benoit

Respuestas:


177

Estás bastante correcto, pero los hilos comparten todos los segmentos excepto la pila. Los subprocesos tienen pilas de llamadas independientes, sin embargo, la memoria en otras pilas de subprocesos todavía es accesible y, en teoría, podría mantener un puntero a la memoria en el marco de pila local de otro subproceso (¡aunque probablemente debería encontrar un lugar mejor para colocar esa memoria!).


27
La parte interesante es que, aunque los subprocesos tienen pilas de llamadas independientes, la memoria en otras pilas aún es accesible.
Karthik Balaguru

1
sí, me pregunto si es aceptable acceder a la memoria en otras pilas entre hilos. Mientras esté seguro de que no está tratando de hacer referencia a una pila que se ha desasignado, ¿no estoy seguro de ver un problema con ella?
bph

2
@bph: es posible acceder a la memoria de la pila de otro hilo, pero en interés de una buena práctica de ingeniería de software, no diría que es aceptable hacerlo.
Greg Hewgill

1
Acceder, especialmente escribir en, las pilas de otros hilos se mete con varias implementaciones de recolector de basura. Sin embargo, esto podría justificarse como un fallo de la implementación del GC.
yyny

56

De Wikipedia (creo que sería una buena respuesta para el entrevistador: P)

Los subprocesos difieren de los procesos tradicionales del sistema operativo multitarea en que:

  • los procesos suelen ser independientes, mientras que los subprocesos existen como subconjuntos de un proceso
  • los procesos llevan una considerable información de estado, mientras que múltiples subprocesos dentro de un proceso comparten estado, así como memoria y otros recursos
  • los procesos tienen espacios de direcciones separados, mientras que los hilos comparten su espacio de direcciones
  • los procesos interactúan solo a través de mecanismos de comunicación entre procesos proporcionados por el sistema.
  • El cambio de contexto entre hilos en el mismo proceso suele ser más rápido que el cambio de contexto entre procesos.

2
sobre el punto no 2 anterior: para los hilos también la CPU mantiene un contexto.
Jack

49

Algo que realmente debe señalarse es que realmente hay dos aspectos en esta pregunta: el aspecto teórico y el aspecto de las implementaciones.

Primero, veamos el aspecto teórico. Debe comprender qué es un proceso conceptualmente para comprender la diferencia entre un proceso y un hilo y lo que se comparte entre ellos.

Tenemos lo siguiente de la sección 2.2.2 El modelo de hilo clásico en los sistemas operativos modernos 3e de Tanenbaum:

El modelo de proceso se basa en dos conceptos independientes: agrupación y ejecución de recursos. A veces es útil separarlos; aquí es donde entran los hilos ...

Él continúa:

Una forma de ver un proceso es que es una forma de agrupar recursos relacionados. Un proceso tiene un espacio de direcciones que contiene texto y datos del programa, así como otros recursos. Estos recursos pueden incluir archivos abiertos, procesos secundarios, alarmas pendientes, manejadores de señales, información contable y más. Al unirlos en forma de proceso, se pueden gestionar más fácilmente. El otro concepto que tiene un proceso es un hilo de ejecución, generalmente acortado a solo un hilo. El hilo tiene un contador de programa que realiza un seguimiento de qué instrucción ejecutar a continuación. Tiene registros, que contienen sus variables de trabajo actuales. Tiene una pila, que contiene el historial de ejecución, con un marco para cada procedimiento llamado pero aún no devuelto. Aunque un hilo debe ejecutarse en algún proceso, El hilo y su proceso son conceptos diferentes y pueden tratarse por separado. Los procesos se utilizan para agrupar recursos juntos; los subprocesos son las entidades programadas para su ejecución en la CPU.

Más abajo proporciona la siguiente tabla:

Per process items             | Per thread items
------------------------------|-----------------
Address space                 | Program counter
Global variables              | Registers
Open files                    | Stack
Child processes               | State
Pending alarms                |
Signals and signal handlers   |
Accounting information        |

Lo anterior es lo que necesita para que los hilos funcionen. Como otros han señalado, cosas como los segmentos son detalles de implementación dependientes del sistema operativo.


2
Esta es una gran explicación. Pero probablemente debería estar vinculado a la pregunta de alguna manera para ser considerado una "Respuesta"
catalizador294

En cuanto a la tabla, ¿el programa no es un contador? y el "estado" de un hilo, capturado en el valor de los registros? También me falta el puntero al código que ejecutan (puntero al texto del proceso)
onlycparra

29

Dígale al entrevistador que depende completamente de la implementación del sistema operativo.

Tome Windows x86 por ejemplo. Solo hay 2 segmentos [1], Código y Datos. Y ambos están asignados a todo el espacio de direcciones de 2GB (lineal, usuario). Base = 0, Límite = 2GB. Habrían hecho uno, pero x86 no permite que un segmento sea tanto de lectura / escritura como de ejecución. Entonces hicieron dos, y configuraron CS para que apunte al descriptor de código, y el resto (DS, ES, SS, etc.) para apuntar al otro [2]. ¡Pero ambos apuntan a lo mismo!

La persona que lo entrevistó hizo una suposición oculta de que él / ella no declaró, y ese es un truco estúpido para hacer.

Entonces con respecto

P. Entonces, ¿qué segmento de hilo comparte?

Los segmentos son irrelevantes para la pregunta, al menos en Windows. Los hilos comparten todo el espacio de direcciones. Solo hay 1 segmento de pila, SS, y apunta exactamente a las mismas cosas que DS, ES y CS hacen [2]. Es decir, todo el sangriento espacio de usuario . 0-2GB. Por supuesto, eso no significa que los hilos solo tengan 1 pila. Naturalmente, cada uno tiene su propia pila, pero los segmentos x86 no se utilizan para este propósito.

Quizás * nix hace algo diferente. Quién sabe. La premisa en la que se basaba la pregunta estaba rota.


  1. Al menos para el espacio del usuario.
  2. De ntsd notepad:cs=001b ss=0023 ds=0023 es=0023

1
Sí ... los segmentos dependen del sistema operativo y del compilador / enlazador. A veces hay un segmento BSS separado del segmento DATA. A veces hay RODATA (datos como cadenas constantes que pueden estar en páginas marcadas como de solo lectura). Algunos sistemas incluso dividen los DATOS en DATOS PEQUEÑOS (accesibles desde una base + desplazamiento de 16 bits) y DATOS (LEJOS) (se requiere un desplazamiento de 32 bits para acceder). También es posible que haya un segmento adicional de TLS DATA (Thread Local Store) que se genera por subproceso
Adisak el

55
Ah no! ¡Estás confundiendo segmentos con secciones! Las secciones son cómo el enlazador divide el módulo en partes (datos, datos, texto, bss, etc.) como usted describió. Pero estoy hablando de segmentos, como se especifica en el hardware Intel / AMD x86. No está relacionado en absoluto con los compiladores / enlazadores. Espero que tenga sentido.
Alex Budovski

Sin embargo, Adisak tiene razón sobre la tienda Thread Local. Es privado para el hilo y no se comparte. Conozco el sistema operativo Windows y no estoy seguro de otro sistema operativo.
Jack

20

En general, los hilos se denominan proceso de peso ligero. Si dividimos la memoria en tres secciones, será: Código, datos y Pila. Cada proceso tiene su propio código, datos y secciones de pila y, debido a este cambio de contexto, el tiempo es un poco alto. Para reducir el tiempo de cambio de contexto, las personas han ideado el concepto de subproceso, que comparte segmento de datos y código con otro subproceso / proceso y tiene su propio segmento STACK.


Olvidaste el montón. El montón, si no me equivoco, debería compartirse entre los subprocesos
Phate

20

Un proceso tiene segmentos de código, datos, montón y pila. Ahora, el puntero de instrucción (IP) de un hilo O hilos apunta al segmento de código del proceso. Los segmentos de datos y montón son compartidos por todos los hilos. ¿Qué pasa con el área de la pila? ¿Cuál es realmente el área de la pila? Es un área creada por el proceso solo para que lo use su hilo ... porque las pilas se pueden usar de una manera mucho más rápida que los montones, etc. El área de pila del proceso se divide entre hilos, es decir, si hay 3 hilos, entonces el El área de apilamiento del proceso se divide en 3 partes y cada una se da a los 3 hilos. En otras palabras, cuando decimos que cada hilo tiene su propia pila, esa pila es en realidad una parte del área de pila de proceso asignada a cada hilo. Cuando un subproceso finaliza su ejecución, el proceso recupera la pila del subproceso. De hecho, no solo la pila de un proceso está dividida entre hilos, sino que todo el conjunto de registros que usa un hilo como SP, PC y registros de estado son los registros del proceso. Entonces, cuando se trata de compartir, el código, los datos y las áreas de almacenamiento dinámico se comparten, mientras que el área de la pila se divide entre hilos.


13

Los subprocesos comparten el código y los segmentos de datos y el montón, pero no comparten la pila.


11
Hay una diferencia entre "poder acceder a los datos en la pila" y compartir la pila. Esos hilos tienen sus propias pilas que son empujadas y reventadas cuando llaman a los métodos.
Kevin Peterson

2
Ambos son puntos de vista igualmente válidos. Sí, cada subproceso tiene su propia pila en el sentido de que hay una correspondencia uno a uno entre subprocesos y pilas y cada subproceso tiene un espacio que utiliza para su propio uso normal de pila. Pero también son recursos de proceso totalmente compartidos y, si lo desea, cualquier subproceso puede acceder a la pila de cualquier otro subproceso tan fácilmente como el suyo.
David Schwartz

@DavidSchwartz, ¿puedo resumir su punto de la siguiente manera: cada subproceso tiene su propia pila, y la pila consta de 2 partes: la primera parte que se comparte entre los subprocesos antes de que el proceso sea multiproceso, y la segunda parte que se completa cuando el subproceso propietario se está ejecutando ... ¿De acuerdo?
FaceBro

2
@nextTide No hay dos partes. Las pilas son compartidas, punto. Cada hilo tiene su propia pila, pero también se comparten. Quizás una buena analogía es si usted y su esposa tienen un auto, pero pueden usar el auto del otro en cualquier momento que lo deseen.
David Schwartz el

5

Los hilos comparten datos y código mientras que los procesos no. La pila no se comparte para ambos.

Los procesos también pueden compartir memoria, más precisamente código, por ejemplo después de un Fork(), pero esto es un detalle de implementación y optimización (del sistema operativo). El código compartido por múltiples procesos se duplicará (con suerte) en la primera escritura en el código, esto se conoce como copia en escritura . No estoy seguro de la semántica exacta para el código de subprocesos, pero supongo que el código compartido.

           Subproceso de proceso

   Pila privada privada
   Datos privados compartidos
   Código privado 1   compartido 2

1 El código es lógicamente privado, pero podría compartirse por razones de rendimiento. 2 No estoy 100% seguro.


Yo diría que el segmento de código (segmento de texto), a diferencia de los datos, casi siempre es de solo lectura en la mayoría de las arquitecturas.
Jorge Córdoba el

4

Los hilos comparten todo [1]. Hay un espacio de direcciones para todo el proceso.

Cada hilo tiene su propia pila y registros, pero todas las pilas de hilos son visibles en el espacio de direcciones compartido.

Si un subproceso asigna algún objeto en su pila y envía la dirección a otro subproceso, ambos tendrán igual acceso a ese objeto.


En realidad, acabo de notar un problema más amplio: creo que estás confundiendo dos usos del segmento de palabras .

El formato de archivo para un ejecutable (p. Ej., ELF) tiene distintas secciones, que pueden denominarse segmentos, que contienen código compilado (texto), datos inicializados, símbolos de enlace, información de depuración, etc. No hay segmentos de montón o pila. aquí, ya que esas son construcciones de tiempo de ejecución solamente.

Estos segmentos de archivo binario se pueden asignar al espacio de direcciones del proceso por separado, con diferentes permisos (por ejemplo, ejecutable de solo lectura para código / texto, y no ejecutable de copia en escritura para datos inicializados).

Las áreas de este espacio de direcciones se usan para diferentes propósitos, como la asignación del montón y las pilas de subprocesos, por convención (aplicada por las bibliotecas de tiempo de ejecución de idioma). Sin embargo, todo es solo memoria, y probablemente no esté segmentado a menos que esté ejecutando en modo virtual 8086. La pila de cada subproceso es una porción de memoria asignada en el momento de la creación del subproceso, con la dirección superior de la pila actual almacenada en un registro de puntero de la pila, y cada subproceso mantiene su propio puntero de la pila junto con sus otros registros.


[1] OK, lo sé: máscaras de señal, TSS / TSD, etc. Sin embargo, el espacio de direcciones, incluidos todos sus segmentos de programa asignados, aún se comparten.


3

En un marco x86, uno puede dividir tantos segmentos (hasta 2 ^ 16-1). Las directivas ASM SEGMENT / ENDS permiten esto, y los operadores SEG y OFFSET permiten la inicialización de registros de segmento. CS: IP suele ser inicializado por el cargador, pero para DS, ES, SS, la aplicación es responsable de la inicialización. Muchos entornos permiten las llamadas "definiciones de segmento simplificadas" como .code, .data, .bss, .stack, etc. y, dependiendo también del "modelo de memoria" (pequeño, grande, compacto, etc.) el cargador inicializa registros de segmento en consecuencia. Por lo general, .data, .bss, .stack y otros segmentos habituales (no he hecho esto desde hace 20 años, así que no recuerdo todos) se agrupan en un solo grupo, es por eso que generalmente DS, ES y SS apuntan a misma área, pero esto es solo para simplificar las cosas.

En general, todos los registros de segmento pueden tener valores diferentes en tiempo de ejecución. Entonces, la pregunta de la entrevista fue correcta: cuál de los CÓDIGOS, DATOS y PILAS se comparten entre los hilos. La gestión del montón es otra cosa: es simplemente una secuencia de llamadas al sistema operativo. Pero, ¿qué pasa si no tiene un sistema operativo en absoluto, como en un sistema integrado? ¿Puede tener nuevo / eliminar en su código?

Mi consejo para los jóvenes: lea un buen libro de programación de ensamblajes. Parece que los planes de estudio universitarios son bastante pobres a este respecto.


2

Además de la memoria global, los subprocesos también comparten una serie de otros atributos (es decir, estos atributos son globales para un proceso, en lugar de específicos para un subproceso). Estos atributos incluyen lo siguiente:

  • ID de proceso e ID de proceso principal;
  • ID de grupo de proceso e ID de sesión;
  • terminal de control;
  • credenciales de proceso (ID de usuario y grupo);
  • descriptores de archivos abiertos;
  • grabar cerraduras creadas usando fcntl();
  • disposiciones de señal;
  • información relacionada con el sistema de archivos: umask, directorio de trabajo actual y directorio raíz;
  • temporizadores de intervalo ( setitimer()) y temporizadores POSIX ( timer_create());
  • Sistema V semáforo semadjvalores de deshacer ( ) (Sección 47.8);
  • límites de recursos;
  • Tiempo de CPU consumido (según lo devuelto por times());
  • recursos consumidos (según lo devuelto por getrusage()); y
  • Buen valor (establecido por setpriority()y nice()).

Entre los atributos que son distintos para cada hilo están los siguientes:

  • ID de hilo (Sección 29.5);
  • máscara de señal;
  • datos específicos del hilo (Sección 31.3);
  • pila de señal alternativa ( sigaltstack());
  • la variable errno;
  • entorno de punto flotante (ver fenv(3));
  • política y prioridad de programación en tiempo real (Secciones 35.2 y 35.3);
  • Afinidad de CPU (específica de Linux, descrita en la Sección 35.4);
  • capacidades (específicas de Linux, descritas en el Capítulo 39); y
  • stack (variables locales e información de enlace de llamada de función).

Extracto de: La interfaz de programación de Linux: Manual de programación del sistema Linux y UNIX, Michael Kerrisk , página 619


0

El subproceso comparte el montón (hay una investigación sobre el montón específico del subproceso) pero la implementación actual comparte el montón. (y por supuesto el código)


0

En proceso, todos los subprocesos comparten recursos del sistema, como memoria de almacenamiento dinámico, etc., mientras que Thread tiene su propia pila

Por lo tanto, su ans debe ser memoria de montón que todos los hilos comparten para un proceso.

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.