¿Qué es lo más parecido que tiene Windows a bifurcarse()?

Resuelto rlbond asked hace 15 años • 15 respuestas

Supongo que la pregunta lo dice todo.

Quiero bifurcarme en Windows. Cuál es la operación más similar y cómo la uso.

rlbond avatar Jun 12 '09 13:06 rlbond
Aceptado

Cygwin tiene fork() con todas las funciones en Windows. Por lo tanto, si usar Cygwin es aceptable para usted, entonces el problema se resuelve en el caso de que el rendimiento no sea un problema.

De lo contrario, puedes ver cómo Cygwin implementa fork(). De un documento de arquitectura bastante antiguo de Cygwin :

5.6. Creación de procesos La llamada fork en Cygwin es particularmente interesante porque no se asigna bien sobre la API de Win32. Esto hace que sea muy difícil implementarlo correctamente. Actualmente, la bifurcación Cygwin es una implementación sin copia en escritura similar a la que estaba presente en las primeras versiones de UNIX.

Lo primero que sucede cuando un proceso padre bifurca un proceso hijo es que el padre inicializa un espacio en la tabla de procesos Cygwin para el hijo. Luego crea un proceso secundario suspendido mediante la llamada CreateProcess de Win32. A continuación, el proceso principal llama a setjmp para guardar su propio contexto y establece un puntero a este en un área de memoria compartida de Cygwin (compartida entre todas las tareas de Cygwin). Luego completa las secciones .data y .bss del niño copiándolas desde su propio espacio de direcciones al espacio de direcciones del niño suspendido. Después de inicializar el espacio de direcciones del niño, el niño se ejecuta mientras el padre espera en un mutex. El niño descubre que se ha bifurcado y realiza saltos largos utilizando el buffer de salto guardado. Luego, el niño establece el mutex que el padre está esperando y bloquea otro mutex. Esta es la señal para que el padre copie su pila y su montón en el hijo, después de lo cual libera el mutex que el hijo está esperando y regresa de la llamada de bifurcación. Finalmente, el niño se despierta del bloqueo en el último mutex, recrea cualquier área asignada en memoria que se le haya pasado a través del área compartida y regresa de la bifurcación.

Si bien tenemos algunas ideas sobre cómo acelerar la implementación de nuestra bifurcación reduciendo la cantidad de cambios de contexto entre el proceso principal y el secundario, es casi seguro que la bifurcación siempre será ineficiente en Win32. Afortunadamente, en la mayoría de las circunstancias, la familia de llamadas generadas proporcionada por Cygwin se puede sustituir por un par fork/exec con sólo un poco de esfuerzo. Estas llamadas se asignan claramente sobre la API de Win32. Como resultado, son mucho más eficientes. Cambiar el programa controlador del compilador para llamar a spawn en lugar de fork fue un cambio trivial y aumentó la velocidad de compilación entre un veinte y un treinta por ciento en nuestras pruebas.

Sin embargo, spawn y exec presentan sus propias dificultades. Debido a que no hay forma de realizar un ejecutivo real en Win32, Cygwin tiene que inventar sus propios ID de proceso (PID). Como resultado, cuando un proceso realiza múltiples llamadas ejecutivas, habrá múltiples PID de Windows asociados con un único PID de Cygwin. En algunos casos, los fragmentos de cada uno de estos procesos Win32 pueden permanecer, esperando a que salga el proceso Cygwin ejecutado.

Parece mucho trabajo, ¿no? Y sí, es lento.

EDITAR: el documento está desactualizado; consulte esta excelente respuesta para obtener una actualización

Laurynas Biveinis avatar Jun 12 '2009 07:06 Laurynas Biveinis

Ciertamente no conozco los detalles sobre esto porque nunca lo he hecho, pero la API NT nativa tiene la capacidad de bifurcar un proceso (el subsistema POSIX en Windows necesita esta capacidad; no estoy seguro de si el subsistema POSIX ya ni siquiera es compatible).

Una búsqueda de ZwCreateProcess() debería brindarle más detalles, por ejemplo, esta información de Maxim Shatskih :

El parámetro más importante aquí es SecciónHandle. Si este parámetro es NULL, el kernel bifurcará el proceso actual. De lo contrario, este parámetro debe ser un identificador del objeto de sección SEC_IMAGE creado en el archivo EXE antes de llamar a ZwCreateProcess().

Aunque tenga en cuenta que Corinna Vinschen indica que Cygwin encontró que el uso de ZwCreateProcess() aún no es confiable :

Iker Arizmendi escribió:

> Because the Cygwin project relied solely on Win32 APIs its fork
> implementation is non-COW and inefficient in those cases where a fork
> is not followed by exec.  It's also rather complex. See here (section
> 5.6) for details:
>  
> http://www.redhat.com/support/wpapers/cygnus/cygnus_cygwin/architecture.html

Este documento es bastante antiguo, unos 10 años. Si bien todavía usamos llamadas Win32 para emular la bifurcación, el método ha cambiado notablemente. Especialmente, ya no creamos el proceso hijo en estado suspendido, a menos que estructuras de datos específicas necesiten un manejo especial en el padre antes de ser copiadas al hijo. En la versión actual 1.5.25, el único caso para un hijo suspendido son los sockets abiertos en el padre. La próxima versión 1.7.0 no se suspenderá en absoluto.

Una razón para no usar ZwCreateProcess fue que hasta la versión 1.5.25 todavía admitimos usuarios de Windows 9x. Sin embargo, dos intentos de utilizar ZwCreateProcess en sistemas basados ​​en NT fallaron por una razón u otra.

Sería realmente bueno si estas cosas estuvieran mejor o estuvieran documentadas, especialmente un par de estructuras de datos y cómo conectar un proceso a un subsistema. Si bien fork no es un concepto de Win32, no veo que sea malo hacer que fork sea más fácil de implementar.

Michael Burr avatar Jun 12 '2009 07:06 Michael Burr

Bueno, Windows realmente no tiene nada parecido. Especialmente porque fork se puede usar para crear conceptualmente un hilo o un proceso en *nix.

Entonces, tendría que decir:

CreateProcess()/CreateProcessEx()

y

CreateThread()(He oído que para aplicaciones C, _beginthreadex()es mejor).

Evan Teran avatar Jun 12 '2009 06:06 Evan Teran