
En Unix, siempre que queremos crear un nuevo proceso, bifurcamos el proceso actual, creando un nuevo proceso hijo que es exactamente igual al proceso padre; luego hacemos una llamada ejecutiva al sistema para reemplazar todos los datos del proceso principal con los del nuevo proceso.
¿Por qué creamos una copia del proceso principal en primer lugar y no creamos un nuevo proceso directamente?
Respuesta1
La respuesta corta es: fork
está en Unix porque era fácil de adaptar al sistema existente en ese momento y porquesistema predecesor en BerkeleyHabía utilizado el concepto de tenedores.
DeLa evolución del sistema de tiempo compartido Unix(el texto relevante ha sidoresaltado):
El control de procesos en su forma moderna se diseñó e implementó en un par de días. Es sorprendente la facilidad con la que se incorporó al sistema existente; al mismo tiempo es fácil ver cómoAlgunas de las características ligeramente inusuales del diseño están presentes precisamente porque representaban cambios pequeños y fáciles de codificar en lo que existía.. Un buen ejemplo es la separación de las funciones fork y exec. El modelo más común para la creación de nuevos procesos implica especificar un programa para que el proceso se ejecute; En Unix, un proceso bifurcado continúa ejecutando el mismo programa que su padre hasta que realiza un proceso ejecutivo explícito. La separación de funciones ciertamente no es exclusiva de Unix, yde hecho, estaba presente en el sistema de tiempo compartido de Berkeley, que Thompson conocía bien.. Aun así, parece razonable suponer queexiste en Unix principalmente debido a la facilidad con la que se podría implementar la bifurcación sin cambiar mucho más. El sistema ya manejaba múltiples (es decir, dos) procesos; había una tabla de procesos y los procesos se intercambiaban entre la memoria principal y el disco. La implementación inicial de fork solo requirió
1) Ampliación de la mesa de proceso.
2) Adición de una llamada de bifurcación que copió el proceso actual en el área de intercambio de disco, utilizando las primitivas de E/S de intercambio ya existentes, e hizo algunos ajustes en la tabla de procesos.
De hecho, la llamada de bifurcación del PDP-7 requirió precisamente 27 líneas de código ensamblador. Por supuesto, fueron necesarios otros cambios en el sistema operativo y en los programas de usuario, y algunos de ellos fueron bastante interesantes e inesperados. Peroun fork-exec combinado habría sido considerablemente más complicado, aunque sólo fuera porque exec como tal no existía; su función ya fue realizada, utilizando IO explícita, por el shell.
Desde ese artículo, Unix ha evolucionado. fork
seguido de exec
ya no es la única forma de ejecutar un programa.
horquillafue creado para ser una bifurcación más eficiente para el caso en el que el nuevo proceso pretende realizar un ejecutivo justo después de la bifurcación. Después de realizar un vfork, los procesos padre e hijo comparten el mismo espacio de datos y el proceso padre se suspende hasta que el proceso hijo ejecuta un programa o sale.
posix_spawncrea un nuevo proceso y ejecuta un archivo en una sola llamada al sistema. Se necesitan una serie de parámetros que le permiten compartir selectivamente los archivos abiertos de la persona que llama y copiar la disposición de su señal y otros atributos al nuevo proceso.
Respuesta2
[Repetiré parte de mi respuesta deaquí.]
¿Por qué no tener un comando que cree un nuevo proceso desde cero? ¿No es absurdo e ineficaz copiar uno que sólo va a ser reemplazado inmediatamente?
De hecho, eso probablemente no sería tan eficiente por varias razones:
La "copia" producida por
fork()
es un poco abstracta, ya que el núcleo utiliza unCopiar en escritosistema; lo único que realmente hay que crear es un mapa de memoria virtual. Si la copia llama inmediatamente aexec()
, la mayoría de los datos que se habrían copiado si hubieran sido modificados por la actividad del proceso nunca tienen que copiarse/crearse porque el proceso no hace nada que requiera su uso.Varios aspectos importantes del proceso hijo (por ejemplo, su entorno) no tienen que duplicarse individualmente ni establecerse en función de un análisis complejo del contexto, etc. Simplemente se supone que son los mismos que los del proceso de llamada, y este es el sistema bastante intuitivo con el que estamos familiarizados.
Para explicar el punto 1 un poco más, la memoria que se "copia" pero a la que nunca se accede posteriormente nunca se copia realmente, al menos en la mayoría de los casos. Una excepción en este contextopodríaser si bifurcó un proceso y luego el proceso principal salió antes de que el hijo se reemplazara con exec()
. yo digopodríaporque gran parte del padre podría almacenarse en caché si hay suficiente memoria libre, y no estoy seguro de hasta qué punto esto se explotaría (lo que dependería de la implementación del sistema operativo).
Por supuesto, eso en la superficie no significa que usar una copiamáseficiente que usar una pizarra en blanco, excepto que "la pizarra en blanco" no es literalmente nada y debe implicar una asignación. El sistema podría tener una plantilla de proceso nueva/en blanco genérica que copia de la misma manera, 1 pero eso en realidad no guardaría nada en comparación con la bifurcación de copia en escritura. Entonces, el punto 1 simplemente demuestra que usar un "nuevo" proceso vacío no sería más eficiente.
El punto 2 explica por qué usar el tenedor probablemente sea más eficiente. El entorno de un niño se hereda de su padre, incluso si es un ejecutable completamente diferente. Por ejemplo, si el proceso padre es un shell y el hijo un navegador web, $HOME
sigue siendo el mismo para ambos, pero dado que cualquiera de ellos podría cambiarlo posteriormente, deben ser dos copias separadas. El del niño es producido por el original fork()
.
1. Una estrategia que puede no tener mucho sentido literal, pero lo que quiero decir es que crear un proceso implica más que copiar su imagen en la memoria desde el disco.
Respuesta3
Creo que la razón por la que Unix sólo tenía la fork
función de crear nuevos procesos es el resultado de laFilosofía Unix
Construyen una función que hace bien una cosa. Crea un proceso hijo.
Lo que uno haga con el nuevo proceso depende entonces del programador. Puede usar una de las exec*
funciones e iniciar un programa diferente, o no podría usar exec y usar las dos instancias del mismo programa, lo cual puede resultar útil.
Entonces obtienes un mayor grado de libertad ya que puedes usar
- bifurcación sin ejecutivo*
- bifurcación con exec* o
- solo ejecutivo* sin tenedor
y además sólo tienes que memorizar las llamadas fork
a exec*
funciones, algo que en los años 1970 había que hacer.
Respuesta4
La función fork() no es solo para copiar el proceso padre, sino que devuelve un valor que refiere que el proceso es el proceso padre o hijo, la imagen a continuación explica cómo se puede usar fork() como padre y como hijo:
como se muestra cuando el proceso es el padre fork() devuelve el ID del proceso hijo; PID
de lo contrario, devuelve0
por ejemplo, puede utilizarlo si tiene un proceso (servidor web) que recibe las solicitudes y en cada solicitud crea un proceso son process
para procesar esta solicitud, aquí el padre y sus hijos tienen trabajos diferentes.
Entonces, no ejecutar una copia de un proceso no es exactamente lo mismo que fork().