process exec clone fork

process - La diferencia entre fork(), vfork(), exec() y clone()



(5)

Estaba buscando la diferencia entre estos cuatro en Google y esperaba que hubiera una gran cantidad de información sobre esto, pero realmente no había ninguna comparación sólida entre las cuatro llamadas.

Comencé a tratar de compilar una especie de vistazo básico a simple vista de las diferencias entre estas llamadas al sistema y esto es lo que obtuve. ¿Toda esta información es correcta / me falta algo importante?

Fork : la llamada tenedor básicamente hace un duplicado del proceso actual, idéntico en casi todos los sentidos (no todo se copia, por ejemplo, los límites de recursos en algunas implementaciones, pero la idea es crear una copia lo más cercana posible).

El nuevo proceso (hijo) obtiene un ID de proceso (PID) diferente y tiene el PID del proceso anterior (principal) como su PID principal (PPID). Debido a que los dos procesos ahora están ejecutando exactamente el mismo código, pueden decir cuál es cuál por el código de retorno del tenedor: el niño obtiene 0, el padre obtiene el PID del niño. Esto es todo, por supuesto, suponiendo que funciona la llamada de la horquilla; si no, no se crea un niño y el padre recibe un código de error.

Vfork : la diferencia básica entre vfork y fork es que cuando se crea un nuevo proceso con vfork (), el proceso principal se suspende temporalmente, y el proceso hijo puede tomar prestado el espacio de direcciones del padre. Este extraño estado de cosas continúa hasta que el proceso secundario sale o se llama a execve (), en cuyo punto continúa el proceso principal.

Esto significa que el proceso hijo de vfork () debe tener cuidado para evitar variables que se modifiquen inesperadamente en el proceso principal. En particular, el proceso hijo no debe regresar de la función que contiene la llamada vfork (), y no debe llamar a exit () (si necesita salir, debe usar _exit (); en realidad, esto también es cierto para el niño de una horquilla normal ()).

Exec : la llamada del ejecutivo es una manera de básicamente reemplazar todo el proceso actual con un nuevo programa. Carga el programa en el espacio de proceso actual y lo ejecuta desde el punto de entrada. exec () reemplaza el proceso actual con el ejecutable apuntado por la función. El control nunca regresa al programa original a menos que haya un error exec ().

Clone : Clonar, como tenedor, crea un nuevo proceso. A diferencia de fork, estas llamadas permiten que el proceso secundario comparta partes de su contexto de ejecución con el proceso de llamada, como el espacio de memoria, la tabla de descriptores de archivos y la tabla de controladores de señal.

Cuando el proceso hijo se crea con clon, ejecuta la aplicación de función fn (arg). (Esto difiere del tenedor, donde la ejecución continúa en el niño desde el punto de la llamada original). El argumento fn es un puntero a una función que el proceso hijo llama al principio de su ejecución. El argumento arg pasa a la función fn.

Cuando la aplicación de función fn (arg) vuelve, el proceso hijo finaliza. El entero devuelto por fn es el código de salida para el proceso secundario. El proceso hijo también puede terminar explícitamente llamando a exit (2) o después de recibir una señal fatal.

Información obtenida de la forma:

¡Gracias por tomarse el tiempo de leer esto! :)


  1. fork() : crea un nuevo proceso secundario, que es una copia completa del proceso principal. Los procesos secundarios y secundarios utilizan diferentes espacios de direcciones virtuales, que inicialmente están ocupados por las mismas páginas de memoria. Luego, cuando se ejecutan ambos procesos, los espacios de direcciones virtuales comienzan a diferir cada vez más, porque el sistema operativo realiza una copia diferida de las páginas de memoria que se están escribiendo con cualquiera de estos dos procesos y asigna una copia independiente de las páginas modificadas de memoria para cada proceso. Esta técnica se llama Copy-On-Write (COW).
  2. vfork() : crea un nuevo proceso hijo, que es una copia "rápida" del proceso principal. A diferencia de la llamada de sistema fork() , los procesos secundarios y secundarios comparten el mismo espacio de direcciones virtuales. ¡NOTA! Utilizando el mismo espacio de direcciones virtuales, tanto el padre como el hijo utilizan la misma pila, el puntero de pila y el puntero de instrucción, como en el caso del clásico fork() . Para evitar interferencias no deseadas entre padres e hijos, que utilizan la misma pila, la ejecución del proceso principal se congela hasta que el niño exec() (cree un nuevo espacio de direcciones virtuales y una transición a una pila diferente) o _exit() (finalización de la ejecución del proceso). vfork() es la optimización de fork() para el modelo "fork-and-exec". Puede realizarse 4-5 veces más rápido que la fork() , porque a diferencia de la fork() (incluso con COW guardado en la mente), la implementación de la llamada al sistema vfork() no incluye la creación de un nuevo espacio de direcciones (el asignación y configuración de nuevos directorios de página).
  3. clone() - crea un nuevo proceso hijo. Varios parámetros de esta llamada al sistema especifican qué partes del proceso padre deben copiarse en el proceso hijo y qué partes se compartirán entre ellos. Como resultado, esta llamada al sistema se puede usar para crear todo tipo de entidades de ejecución, comenzando desde hilos y terminando por procesos completamente independientes. De hecho, la llamada al sistema clone() es la base que se utiliza para la implementación de pthread_create() y toda la familia de las llamadas al sistema fork() .
  4. exec() - restablece toda la memoria del proceso, carga y analiza el binario ejecutable especificado, configura una nueva pila y pasa el control al punto de entrada del archivo ejecutable cargado. Esta llamada al sistema nunca devuelve el control a la persona que llama y sirve para cargar un nuevo programa al proceso ya existente. Esta llamada al sistema con la llamada al sistema fork() forma un modelo clásico de gestión de procesos UNIX llamado "fork-and-exec".

El tenedor (), vfork () y el clon () llaman al do_fork () para hacer el trabajo real, pero con diferentes parámetros.

asmlinkage int sys_fork(struct pt_regs regs) { return do_fork(SIGCHLD, regs.esp, &regs, 0); } asmlinkage int sys_clone(struct pt_regs regs) { unsigned long clone_flags; unsigned long newsp; clone_flags = regs.ebx; newsp = regs.ecx; if (!newsp) newsp = regs.esp; return do_fork(clone_flags, newsp, &regs, 0); } asmlinkage int sys_vfork(struct pt_regs regs) { return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0); } #define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */ #define CLONE_VM 0x00000100 /* set if VM shared between processes */ SIGCHLD means the child should send this signal to its father when exit.

Para fork, el hijo y el padre tienen la tabla de páginas VM independiente, pero desde la eficiencia, fork no copiará realmente ninguna página, solo configurará todas las páginas que se pueden escribir para que sean solo de lectura para el proceso hijo. Entonces, cuando el proceso hijo desea escribir algo en esa página, se produce una excepción de página y kernel asignará una nueva página clonada desde la página anterior con permiso de escritura. Eso se llama "copiar al escribir".

Para vfork, la memoria virtual es exactamente hija y padre; solo por eso, padre e hijo no pueden estar despiertos al mismo tiempo, ya que se influenciarán mutuamente. Entonces el padre duerme al final de "do_fork ()" y se despierta cuando el niño llama a exit () o execve () ya que entonces será el propietario de la nueva tabla de páginas. Aquí está el código (en do_fork ()) que el padre duerme.

if ((clone_flags & CLONE_VFORK) && (retval > 0)) down(&sem); return retval;

Aquí está el código (en mm_release () llamado por exit () y execve ()) que despierta al padre.

up(tsk->p_opptr->vfork_sem);

Para sys_clone (), es más flexible ya que puede ingresar cualquier clone_flags en él. Entonces pthread_create () llama a esta llamada al sistema con muchos clone_flags:

int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM);

Resumen: fork (), vfork () y clone () crearán procesos secundarios con diferente montaje de recursos compartidos con el proceso padre. También podemos decir que vfork () y clone () pueden crear subprocesos (en realidad son procesos ya que tienen task_struct independiente) ya que comparten la tabla de página VM con el proceso padre.


en fork (), el proceso primario o secundario se ejecutará en función de la selección de la CPU. Pero en vfork (), seguramente el hijo se ejecutará primero. después de que el niño termine, el padre se ejecutará.


  • execve() reemplaza la imagen ejecutable actual con otra cargada desde un archivo ejecutable.
  • fork() crea un proceso hijo.
  • vfork() es una versión optimizada histórica de fork() , destinada a ser utilizada cuando se llama a execve() directamente después de fork() . Resultó funcionar bien en sistemas que no son MMU (donde fork() no puede funcionar de una manera eficiente) y cuando fork() ing procesa con una gran huella de memoria para ejecutar algún pequeño programa (piense en Runtime.exec() de Java Runtime.exec() ). POSIX ha estandarizado posix_spawn() para reemplazar estos dos últimos usos más modernos de vfork() .
  • posix_spawn() hace el equivalente de un fork()/execve() , y también permite algunos malabares fd en el medio. Se supone que reemplaza fork()/execve() , principalmente para plataformas que no son MMU.
  • pthread_create() crea un nuevo hilo.
  • clone() es una llamada específica de Linux, que se puede usar para implementar cualquier cosa desde fork() a pthread_create() . Da mucho control. Inspirado en rfork() .
  • rfork() es una llamada específica de Plan-9. Se supone que es una llamada genérica, que permite varios grados de intercambio, entre procesos completos e hilos.

  • vfork() es una optimización obsoleta. Antes de una buena administración de memoria, fork() hizo una copia completa de la memoria de los padres, por lo que era bastante costosa. dado que en muchos casos un fork() fue seguido por exec() , que descarta el mapa de memoria actual y crea uno nuevo, fue un gasto innecesario. Hoy en día, fork() no copia la memoria; simplemente se establece como "copiar al escribir", por lo que fork() + exec() es tan eficiente como vfork() + exec() .

  • clone() es el syscall utilizado por fork() . con algunos parámetros, crea un nuevo proceso, con otros, crea un hilo. la diferencia entre ellos es qué estructuras de datos (espacio de memoria, estado del procesador, pila, PID, archivos abiertos, etc.) se comparten o no.