syscall - ¿Cuál es el propósito de fork()?
wait c (14)
El multiprocesamiento es fundamental para la informática. Por ejemplo, su IE o Firefox pueden crear un proceso para descargar un archivo mientras navega por Internet. O bien, mientras está imprimiendo un documento en un procesador de textos, aún puede mirar diferentes páginas y aún editarlas con él.
En muchos programas y páginas man de Linux, he visto código usando fork()
. ¿Por qué necesitamos usar fork()
y cuál es su propósito?
Fork () se utiliza para crear nuevos procesos como todo el mundo ha escrito.
Aquí está mi código que crea procesos en forma de árbol binario ... Solicitará escanear el número de niveles hasta los cuales desea crear procesos en árbol binario
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
int t1,t2,p,i,n,ab;
p=getpid();
printf("enter the number of levels/n");fflush(stdout);
scanf("%d",&n);
printf("root %d/n",p);fflush(stdout);
for(i=1;i<n;i++)
{
t1=fork();
if(t1!=0)
t2=fork();
if(t1!=0 && t2!=0)
break;
printf("child pid %d parent pid %d/n",getpid(),getppid());fflush(stdout);
}
waitpid(t1,&ab,0);
waitpid(t2,&ab,0);
return 0;
}
SALIDA
enter the number of levels
3
root 20665
child pid 20670 parent pid 20665
child pid 20669 parent pid 20665
child pid 20672 parent pid 20670
child pid 20671 parent pid 20670
child pid 20674 parent pid 20669
child pid 20673 parent pid 20669
Fork crea nuevos procesos. Sin fork, tendrías un sistema Unix que solo podría ejecutar init.
La función fork () se usa para crear un nuevo proceso duplicando el proceso existente desde el que se llama. El proceso existente desde el que se llama a esta función se convierte en el proceso principal y el proceso recién creado se convierte en el proceso secundario. Como ya se dijo, el hijo es una copia duplicada del padre, pero hay algunas excepciones.
El niño tiene un PID único como cualquier otro proceso que se ejecuta en el sistema operativo.
El niño tiene una ID de proceso principal que es igual que el PID de la
proceso que lo creó.La utilización de recursos y los contadores de tiempo de CPU se restablecen a cero en el proceso hijo.
El conjunto de señales pendientes en el niño está vacío.
El niño no hereda ningún temporizador de su padre
Ejemplo:
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
int var_glb; /* A global variable*/
int main(void)
{
pid_t childPID;
int var_lcl = 0;
childPID = fork();
if(childPID >= 0) // fork was successful
{
if(childPID == 0) // child process
{
var_lcl++;
var_glb++;
printf("/n Child Process :: var_lcl = [%d], var_glb[%d]/n", var_lcl, var_glb);
}
else //Parent process
{
var_lcl = 10;
var_glb = 20;
printf("/n Parent process :: var_lcl = [%d], var_glb[%d]/n", var_lcl, var_glb);
}
}
else // fork failed
{
printf("/n Fork failed, quitting!!!!!!/n");
return 1;
}
return 0;
}
Ahora, cuando se compila y ejecuta el código anterior:
$ ./fork
Parent process :: var_lcl = [10], var_glb[20]
Child Process :: var_lcl = [1], var_glb[1]
Primero, uno necesita comprender qué es la llamada al sistema fork (). Dejame explicar
La llamada al sistema fork () crea el duplicado exacto del proceso principal. Realiza el duplicado de la pila primaria, el montón, los datos inicializados, los datos no inicializados y comparte el código en modo de solo lectura con el proceso principal.
La llamada al sistema Fork copia la memoria en la base de copiar y escribir, significa que el niño hace en la página de la memoria virtual cuando es necesario copiar.
Ahora propósito de la bifurcación ():
- Fork () se puede utilizar en el lugar donde hay división de trabajo, como un servidor tiene que manejar múltiples clientes, por lo que el padre tiene que aceptar la conexión de manera regular, por lo que el servidor hace una bifurcación para que cada cliente realice lectura-escritura.
Probablemente no necesite utilizar el tenedor en la programación diaria si está escribiendo aplicaciones.
Incluso si desea que su programa inicie otro programa para realizar alguna tarea, existen otras interfaces más simples que usan fork detrás de escena, como "sistema" en C y perl.
Por ejemplo, si desea que su aplicación inicie otro programa como bc para hacer algunos cálculos por usted, puede usar ''system'' para ejecutarlo. El sistema hace un ''fork'' para crear un nuevo proceso, luego un ''exec'' para convertir ese proceso en bc. Una vez que bc completa, el sistema devuelve el control a su programa.
También puede ejecutar otros programas de forma asíncrona, pero no recuerdo cómo.
Si está escribiendo servidores, shells, virus o sistemas operativos, es más probable que quiera usar fork.
System call fork () se utiliza para crear procesos. No requiere argumentos y devuelve un ID de proceso. El propósito de fork () es crear un nuevo proceso, que se convierte en el proceso secundario de la persona que llama. Después de que se crea un nuevo proceso hijo, ambos procesos ejecutarán la siguiente instrucción siguiendo la llamada al sistema fork (). Por lo tanto, tenemos que distinguir el padre del niño. Esto se puede hacer probando el valor devuelto de fork ():
Si fork () devuelve un valor negativo, la creación de un proceso secundario no tuvo éxito. fork () devuelve un cero al proceso hijo recién creado. fork () devuelve un valor positivo, el ID de proceso del proceso hijo, al padre. El ID del proceso devuelto es del tipo pid_t definido en sys / types.h. Normalmente, el ID del proceso es un número entero. Además, un proceso puede usar la función getpid () para recuperar el ID de proceso asignado a este proceso. Por lo tanto, después de la llamada del sistema a fork (), una prueba simple puede indicar qué proceso es el niño. Tenga en cuenta que Unix hará una copia exacta del espacio de direcciones de los padres y se lo dará al niño. Por lo tanto, los procesos padre e hijo tienen espacios de direcciones separados.
Permítanos entenderlo con un ejemplo para aclarar los puntos anteriores. Este ejemplo no distingue los procesos padre e hijo.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#define MAX_COUNT 200
#define BUF_SIZE 100
void main(void)
{
pid_t pid;
int i;
char buf[BUF_SIZE];
fork();
pid = getpid();
for (i = 1; i <= MAX_COUNT; i++) {
sprintf(buf, "This line is from pid %d, value = %d/n", pid, i);
write(1, buf, strlen(buf));
}
}
Supongamos que el programa anterior se ejecuta hasta el punto de la llamada a fork ().
Si la llamada a fork () se ejecuta con éxito, Unix hará dos copias idénticas de espacios de direcciones, una para el padre y la otra para el niño. Ambos procesos comenzarán su ejecución en la siguiente instrucción después de la llamada fork (). En este caso, ambos procesos comenzarán su ejecución en la asignación
pid = .....;
Ambos procesos comienzan su ejecución justo después de que el sistema llame a fork (). Como ambos procesos tienen espacios de direcciones idénticos pero separados, las variables inicializadas antes de la llamada fork () tienen los mismos valores en ambos espacios de direcciones. Como cada proceso tiene su propio espacio de direcciones, cualquier modificación será independiente de las demás. En otras palabras, si el padre cambia el valor de su variable, la modificación solo afectará a la variable en el espacio de direcciones del proceso principal. Otros espacios de direcciones creados por fork () llamadas no se verán afectados aunque tengan nombres de variable idénticos.
¿Cuál es el motivo de usar write en lugar de printf? Se debe a que printf () está "almacenado", lo que significa que printf () agrupará la salida de un proceso. Al almacenar en búfer la salida para el proceso principal, el niño también puede usar printf para imprimir cierta información, que también se almacenará en búfer. Como resultado, dado que el resultado no se enviará a la pantalla de inmediato, es posible que no obtenga el orden correcto del resultado esperado. Peor aún, la salida de los dos procesos puede mezclarse de maneras extrañas. Para superar este problema, puede considerar utilizar la escritura "sin búfer".
Si ejecuta este programa, puede ver lo siguiente en la pantalla:
................
This line is from pid 3456, value 13
This line is from pid 3456, value 14
................
This line is from pid 3456, value 20
This line is from pid 4617, value 100
This line is from pid 4617, value 101
................
This line is from pid 3456, value 21
This line is from pid 3456, value 22
................
El proceso ID 3456 puede ser el asignado al padre o al hijo. Debido al hecho de que estos procesos se ejecutan al mismo tiempo, sus líneas de salida se entremezclan de una manera bastante impredecible. Además, el orden de estas líneas está determinado por el programador de la CPU. Por lo tanto, si ejecuta este programa nuevamente, puede obtener un resultado totalmente diferente.
Uso de llamada del sistema Fork () para crear un proceso hijo. Es un duplicado exacto del proceso principal. Fork copia la sección de apilamiento, la sección de apilamiento, la sección de datos, la variable de entorno, los argumentos de línea de comando desde el elemento primario.
fork () creará un nuevo proceso hijo idéntico al padre. De modo que todo lo que ejecute en el código después de eso será ejecutado por ambos procesos, muy útil si tiene, por ejemplo, un servidor y desea manejar múltiples solicitudes.
fork () es cómo Unix crea nuevos procesos. En el punto que llamó fork (), su proceso se clona, y dos procesos diferentes continúan la ejecución desde allí. Uno de ellos, el niño, tendrá fork () return 0. El otro, el padre, tendrá fork () devolverá el PID (ID de proceso) del padre.
Por ejemplo, si escribe lo siguiente en un intérprete de comandos, el programa shell llamará a fork (), y luego ejecutará el comando que pasó (telnetd, en este caso) en el elemento secundario, mientras que el padre mostrará el mensaje de nuevo también. como un mensaje que indica el PID del proceso de fondo.
$ telnetd &
En cuanto a la razón por la que creas procesos nuevos, así es como tu sistema operativo puede hacer muchas cosas al mismo tiempo. Es por eso que puede ejecutar un programa y, mientras se está ejecutando, cambiar a otra ventana y hacer otra cosa.
fork () se usa básicamente para crear un proceso hijo para el proceso en el que está llamando a esta función. Siempre que llame a un tenedor (), devuelve un cero para la identificación del niño.
pid=fork()
if pid==0
//this is the child process
else if pid!=0
//this is the parent process
con esto puede proporcionar diferentes acciones para el padre y el hijo y hacer uso de la función de subprocesamiento múltiple.
fork () se usa para crear un proceso hijo. Cuando se llama a una función fork (), se generará un nuevo proceso y la llamada a la función fork () devolverá un valor diferente para el hijo y el padre.
Si el valor devuelto es 0, sabe que es el proceso hijo y si el valor devuelto es un número (que es el id. De proceso secundario), sabe que es el padre. (y si es un número negativo, el tenedor falló y no se creó ningún proceso secundario)
fork()
es cómo crea nuevos procesos en Unix. Cuando llamas a fork
, estás creando una copia de tu propio proceso que tiene su propio espacio de direcciones . Esto permite que varias tareas se ejecuten independientemente una de la otra como si cada una de ellas tuviera la memoria completa de la máquina.
Aquí hay algunos ejemplos de usos de fork
:
- Tu shell usa un
fork
para ejecutar los programas que invocas desde la línea de comando. - Los servidores web como apache usan
fork
para crear múltiples procesos de servidor, cada uno de los cuales maneja las solicitudes en su propio espacio de direcciones. Si uno muere o pierde memoria, otros no se verán afectados, por lo que funciona como un mecanismo para la tolerancia a fallas. - Google Chrome usa un
fork
para manejar cada página dentro de un proceso separado. Esto evitará que el código del lado del cliente en una página reduzca todo su navegador. -
fork
se usa para generar procesos en algunos programas paralelos (como los escritos usando MPI ). Tenga en cuenta que esto es diferente al uso de threads , que no tienen su propio espacio de direcciones y existen dentro de un proceso. - Los lenguajes de scripting usan
fork
indirectamente para iniciar procesos secundarios. Por ejemplo, cada vez que utiliza un comando comosubprocess.Popen
en Python,fork
un proceso secundario y lee su salida. Esto permite que los programas trabajen juntos.
El uso típico de la fork
en un caparazón podría ser similar a esto:
int child_process_id = fork();
if (child_process_id) {
// Fork returns a valid pid in the parent process. Parent executes this.
// wait for the child process to complete
waitpid(child_process_id, ...); // omitted extra args for brevity
// child process finished!
} else {
// Fork returns 0 in the child process. Child executes this.
// new argv array for the child process
const char *argv[] = {"arg1", "arg2", "arg3", NULL};
// now start executing some other program
exec("/path/to/a/program", argv);
}
El intérprete de comandos genera un proceso secundario utilizando el exec
y espera a que se complete, luego continúa con su propia ejecución. Tenga en cuenta que no tiene que usar el tenedor de esta manera. Siempre puede generar muchos procesos secundarios, como lo haría un programa paralelo, y cada uno podría ejecutar un programa al mismo tiempo. Básicamente, cada vez que está creando nuevos procesos en un sistema Unix, está utilizando fork()
. Para el equivalente de Windows, eche un vistazo a CreateProcess
.
Si quieres más ejemplos y una explicación más larga, Wikipedia tiene un resumen decente. Y aquí hay algunas diapositivas sobre cómo funcionan los procesos, los hilos y la concurrencia en los sistemas operativos modernos.
fork()
se usa para generar un proceso secundario. Por lo general, se usa en situaciones similares como el enhebrado, pero existen diferencias. A diferencia de los hilos, fork()
crea procesos separados, lo que significa que el hijo y el padre son copias directas el uno del otro en el punto en que se llama fork()
, están completamente separados, ninguno puede acceder al espacio de memoria del otro ( sin ir a los problemas normales, vaya a acceder a la memoria de otro programa).
fork()
todavía es utilizado por algunas aplicaciones de servidor, la mayoría de las cuales se ejecutan como root en una máquina * NIX que descarta permisos antes de procesar las solicitudes de los usuarios. Todavía hay algunos otros casos de uso, pero la mayoría de la gente ahora se ha cambiado a multihilo.