pid_t - ¿Por qué este programa imprime "bifurcado" 4 veces?
vfork (6)
¿Por qué este programa imprime "bifurcado" 4 veces?
#include <stdio.h>
#include <unistd.h>
int main(void) {
fork() && (fork() || fork());
printf("forked!/n");
return 0;
}
Ejecutando
fork() && (fork() || fork())
, qué sucede
Cada
fork
proporciona 2 procesos con valores respectivamente pid (padre) y 0 (hijo)
Primer tenedor:
-
el valor de retorno primario es pid no nulo => ejecuta el
&& (fork() || fork())
-
el segundo valor primario de la bifurcación es pid no nulo deja de ejecutar el
||
parte => impresiónforked
-
valor secundario del segundo tenedor = 0 => ejecuta el
|| fork()
|| fork()
-
impresiones horquilla del tercer tenedor
forked
-
tercer tenedor infantil estampados
forked
-
impresiones horquilla del tercer tenedor
-
el segundo valor primario de la bifurcación es pid no nulo deja de ejecutar el
-
el valor de retorno secundario es 0, deja de ejecutar && part => impresiones
forked
Total: 4
forked
El primer
fork()
devuelve un valor distinto de cero en el proceso de llamada (llámelo p0) y 0 en el hijo (llámelo p1).
En p1 se toma el cortocircuito para
&&
y el proceso llama a
printf
y finaliza.
En p0 el proceso debe evaluar el resto de la expresión.
Luego llama a
fork()
nuevamente, creando así un nuevo proceso hijo (p2).
En p0
fork()
devuelve un valor distinto de cero, y el cortocircuito para
||
se toma, por lo que el proceso llama a
printf
y finaliza.
En p2,
fork()
devuelve 0, por lo que el resto de ||
debe evaluarse, que es la última
fork()
;
eso lleva a la creación de un hijo para p2 (llámelo p3).
P2 luego ejecuta
printf
y termina.
P3 luego ejecuta
printf
y termina.
Se ejecutan 4
printf
s.
El primero proviene de
main()
y los otros tres de cada
fork()
.
Observe que los tres
forks()
se ejecutarán.
Es posible que desee echar un vistazo a la
ref
:
VALOR DEVUELTO
Al completar con éxito, fork () devolverá 0 al proceso hijo y devolverá la ID del proceso hijo al proceso padre . Ambos procesos continuarán ejecutándose desde la función fork (). De lo contrario, -1 se devolverá al proceso padre, no se creará ningún proceso hijo y se establecerá errno para indicar el error.
Tenga en cuenta que la identificación del proceso no puede ser cero, como se indica here .
Entonces, ¿qué pasa realmente?
Tenemos:
fork() && (fork() || fork());
Por lo tanto, el primer
fork()
devolverá al padre su id. De proceso distinto de cero, mientras que devolverá 0 al proceso hijo.
Eso significa que la primera bifurcación de la expresión lógica se evaluará como verdadera en el proceso primario, mientras que en el proceso secundario se evaluará como falsa y, debido a la
evaluación de Cortocircuito
, no llamará a las dos
fork()
restantes
fork()
.
Entonces, ahora sabemos que obtendremos al menos dos impresiones (una de main y otra de la primera
fork()
).
Ahora, la segunda
fork()
en el proceso padre se ejecutará, lo hace y devuelve un valor distinto de cero al proceso padre y cero en el proceso hijo.
Así que ahora, el padre no continuará la ejecución hasta la última
fork()
(debido a cortocircuito), mientras que el proceso secundario ejecutará la última bifurcación, desde el primer operando de
||
es 0.
Eso significa que obtendremos dos impresiones más.
Como resultado, obtenemos cuatro impresiones en total.
Cortocircuito
Aquí, el cortocircuito básicamente significa que si el primer operando de && es cero, entonces los otros operandos no son evaluados. En la misma lógica, si un operando de a || es 1, entonces el resto de los operandos no necesitan evaluación. Esto sucede porque el resto de los operandos no pueden cambiar el resultado de la expresión lógica, por lo que no es necesario ejecutarlos, por lo que ahorramos tiempo.
Ver ejemplo a continuación.
Proceso
Recuerde que un proceso padre crea procesos descendientes que a su vez crean otros procesos, etc. Esto lleva a una jerarquía de procesos (o un árbol que se podría decir).
Teniendo esto en cuenta, vale la pena echar un vistazo a este problema similar , así como a this respuesta.
Imagen descriptiva
También hice esta figura que puede ayudar, supongo.
Supuse que la
fork()
pid
fork()
devuelta son 3, 4 y 5 por cada llamada.
Observe que algunos
fork()
s tienen una X roja sobre ellos, lo que significa que no se ejecutan debido a la evaluación de cortocircuito de la expresión lógica.
Los
fork()
s en la parte superior no se ejecutarán, ya que el primer operando del operador
&&
es 0, por lo tanto, la expresión completa dará como resultado 0, por lo que no hay esencia en la ejecución del resto de los operandos de
&&
.
La
fork()
en la parte inferior no se ejecutará, ya que es el segundo operando de un
||
, donde su primer operando es un número distinto de cero, por lo tanto, el resultado de la expresión ya se evalúa como verdadero, sin importar cuál sea el segundo operando.
Y en la siguiente imagen puedes ver la jerarquía de los procesos: basado en la figura anterior.
Ejemplo de cortocircuito
#include <stdio.h>
int main(void) {
if(printf("A printf() results in logic true/n"))
;//empty body
if(0 && printf("Short circuiting will not let me execute/n"))
;
else if(0 || printf("I have to be executed/n"))
;
else if(1 || printf("No need for me to get executed/n"))
;
else
printf("The answer wasn''t nonsense after all!/n");
return 0;
}
Salida:
A printf() results in logic true
I have to be executed
Este código:
fork();
fork() && fork() || fork();
fork();
obtiene 20 procesos por sí mismo y 20 veces Printf irá.
Y para
fork() && fork() || fork();
printf irá un total de 5 veces.
Me gustan todas las respuestas que ya se han enviado. Quizás si agrega algunas variables más a su declaración printf, le sería más fácil ver lo que está sucediendo.
#include<stdio.h>
#include<unistd.h>
int main(){
long child = fork() && (fork() || fork());
printf("forked! PID=%ld Child=%ld/n", getpid(), child);
return 0;
}
En mi máquina produjo esta salida:
forked! PID=3694 Child = 0
forked! PID=3696 Child = 0
forked! PID=3693 Child = 1
forked! PID=3695 Child = 1
Para todos los votantes negativos, esto se debe a una pregunta combinada pero diferente. Culpa a SO. Gracias.
Puede descomponer el problema en tres líneas, la primera y la última línea simplemente duplican el número de procesos.
fork() && fork() || fork();
Los operadores están en corto circuito, así que esto es lo que obtienes:
fork()
/ /
0/ />0
|| fork() && fork()
// / /
/ / 0/ />0
* * || fork() *
/ /
* *
Esto es en total 4 * 5 = 20 procesos cada impresión de una línea.
Nota: Si por alguna razón fork () falla (por ejemplo, tiene algún límite en el número de procesos), devuelve -1 y luego puede obtener resultados diferentes.