¿Qué es lo más parecido que tiene Windows para tenedor ()?

Supongo que la pregunta lo dice todo.

Quiero bifurcar en las ventanas. ¿Cuál es la operación más similar y cómo la uso?

Cygwin ha presentado fork () completamente en Windows. Por lo tanto, si utiliza Cygwin es aceptable para usted, entonces el problema se resuelve en caso de que el rendimiento no sea un problema.

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

5.6. Creación de procesos La llamada a fork en Cygwin es particularmente interesante porque no se correlaciona bien con la API de Win32. Esto hace que sea muy difícil de implementar correctamente. Actualmente, la horquilla Cygwin es una implementación que no es de copia por escritura, similar a lo que estaba presente en los primeros sabores de UNIX.

Lo primero que ocurre cuando un proceso principal bifurca un proceso secundario es que el padre inicializa un espacio en la tabla de proceso de Cygwin para el niño. Luego crea un proceso hijo suspendido utilizando la llamada Win32 CreateProcess. 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). A continuación, rellena las secciones .data y .bss del niño copiando desde su propio espacio de direcciones en el espacio de direcciones del niño suspendido. Después de que se inicializa el espacio de direcciones del niño, el niño se ejecuta mientras el padre espera en un mutex. El niño descubre que ha sido ahorquillado y saltos largos utilizando el buffer de salto guardado. El niño entonces establece el mutex que el padre está esperando y bloquea otro mutex. Esta es la señal para que el padre copie su stack y stack en el niño, luego de lo cual libera el mutex que el niño está esperando y regresa de la llamada de la tenedor. Finalmente, el niño se despierta del locking en el último mutex, recrea las áreas mapeadas en memoria que se le pasan a través del área compartida y regresa de la propia horquilla.

Si bien tenemos algunas ideas sobre cómo acelerar nuestra implementación fork mediante la reducción del número de cambios de contexto entre el proceso principal y el secundario, es casi seguro que fork siempre sea ineficiente en Win32. Afortunadamente, en la mayoría de los casos, la familia de llamadas de spawn proporcionada por Cygwin se puede sustituir por un par de fork / exec con solo un pequeño esfuerzo. Estas llamadas se asignan perfectamente a la API de Win32. Como resultado, son mucho más eficientes. Cambiar el progtwig del controlador del comstackdor para invocar spawn en lugar de fork fue un cambio trivial y aumentó las velocidades de comstackción en un veinte o treinta por ciento en nuestras pruebas.

Sin embargo, spawn y el ejecutivo presentan su propio conjunto de dificultades. Debido a que no hay forma de hacer un ejecutivo real bajo Win32, Cygwin tiene que inventar sus propios ID de proceso (PID). Como resultado, cuando un proceso realiza varias llamadas ejecutivas, habrá múltiples PID de Windows asociados con un solo PID de Cygwin. En algunos casos, los resguardos de cada uno de estos procesos de Win32 pueden persistir, a la espera de que finalice su proceso ejecutado de Cygwin.

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

EDITAR: el documento está desactualizado, vea esta excelente respuesta para una actualización

Ciertamente no conozco los detalles porque nunca lo hice, pero la NT API nativa tiene la capacidad de bifurcar un proceso (el subsistema POSIX en Windows necesita esta capacidad; no estoy seguro si el subsistema POSIX incluso se admite más).

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

El parámetro más importante aquí es SectionHandle. Si este parámetro es NULL, el núcleo 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 ().

Sin embargo, tenga en cuenta que Corinna Vinschen indica que Cygwin encontró que usar 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 viejo, 10 años más o menos. Mientras todavía utilizamos las llamadas de Win32 para emular fork, el método ha cambiado notablemente. Especialmente, ya no creamos el proceso secundario en el estado suspendido, a menos que las estructuras de datos específicas necesiten un manejo especial en el padre antes de que se copien al niño. 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 estamos soportando usuarios de Windows 9x. Sin embargo, dos bashs de usar ZwCreateProcess en sistemas basados ​​en NT fallaron por una razón u otra.

Sería realmente bueno si esto estuviese mejor o en absoluto documentado, 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 creo que sea malo hacer que sea más fácil implementarlo.

Bueno, Windows realmente no tiene nada como eso. Especialmente dado que 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).

La gente ha intentado implementar fork en Windows. Esto es lo más cercano que puedo encontrar:

Tomado de: http://doxygen.scilab.org/5.3/d0/d8f/forkWindows_8c_source.html#l00216

 static BOOL haveLoadedFunctionsForFork(void); int fork(void) { HANDLE hProcess = 0, hThread = 0; OBJECT_ATTRIBUTES oa = { sizeof(oa) }; MEMORY_BASIC_INFORMATION mbi; CLIENT_ID cid; USER_STACK stack; PNT_TIB tib; THREAD_BASIC_INFORMATION tbi; CONTEXT context = { CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT }; if (setjmp(jenv) != 0) return 0; /* return as a child */ /* check whether the entry points are initilized and get them if necessary */ if (!ZwCreateProcess && !haveLoadedFunctionsForFork()) return -1; /* create forked process */ ZwCreateProcess(&hProcess, PROCESS_ALL_ACCESS, &oa, NtCurrentProcess(), TRUE, 0, 0, 0); /* set the Eip for the child process to our child function */ ZwGetContextThread(NtCurrentThread(), &context); /* In x64 the Eip and Esp are not present, their x64 counterparts are Rip and Rsp respectively. */ #if _WIN64 context.Rip = (ULONG)child_entry; #else context.Eip = (ULONG)child_entry; #endif #if _WIN64 ZwQueryVirtualMemory(NtCurrentProcess(), (PVOID)context.Rsp, MemoryBasicInformation, &mbi, sizeof mbi, 0); #else ZwQueryVirtualMemory(NtCurrentProcess(), (PVOID)context.Esp, MemoryBasicInformation, &mbi, sizeof mbi, 0); #endif stack.FixedStackBase = 0; stack.FixedStackLimit = 0; stack.ExpandableStackBase = (PCHAR)mbi.BaseAddress + mbi.RegionSize; stack.ExpandableStackLimit = mbi.BaseAddress; stack.ExpandableStackBottom = mbi.AllocationBase; /* create thread using the modified context and stack */ ZwCreateThread(&hThread, THREAD_ALL_ACCESS, &oa, hProcess, &cid, &context, &stack, TRUE); /* copy exception table */ ZwQueryInformationThread(NtCurrentThread(), ThreadBasicInformation, &tbi, sizeof tbi, 0); tib = (PNT_TIB)tbi.TebBaseAddress; ZwQueryInformationThread(hThread, ThreadBasicInformation, &tbi, sizeof tbi, 0); ZwWriteVirtualMemory(hProcess, tbi.TebBaseAddress, &tib->ExceptionList, sizeof tib->ExceptionList, 0); /* start (resume really) the child */ ZwResumeThread(hThread, 0); /* clean up */ ZwClose(hThread); ZwClose(hProcess); /* exit with child's pid */ return (int)cid.UniqueProcess; } static BOOL haveLoadedFunctionsForFork(void) { HANDLE ntdll = GetModuleHandle("ntdll"); if (ntdll == NULL) return FALSE; if (ZwCreateProcess && ZwQuerySystemInformation && ZwQueryVirtualMemory && ZwCreateThread && ZwGetContextThread && ZwResumeThread && ZwQueryInformationThread && ZwWriteVirtualMemory && ZwClose) { return TRUE; } ZwCreateProcess = (ZwCreateProcess_t) GetProcAddress(ntdll, "ZwCreateProcess"); ZwQuerySystemInformation = (ZwQuerySystemInformation_t) GetProcAddress(ntdll, "ZwQuerySystemInformation"); ZwQueryVirtualMemory = (ZwQueryVirtualMemory_t) GetProcAddress(ntdll, "ZwQueryVirtualMemory"); ZwCreateThread = (ZwCreateThread_t) GetProcAddress(ntdll, "ZwCreateThread"); ZwGetContextThread = (ZwGetContextThread_t) GetProcAddress(ntdll, "ZwGetContextThread"); ZwResumeThread = (ZwResumeThread_t) GetProcAddress(ntdll, "ZwResumeThread"); ZwQueryInformationThread = (ZwQueryInformationThread_t) GetProcAddress(ntdll, "ZwQueryInformationThread"); ZwWriteVirtualMemory = (ZwWriteVirtualMemory_t) GetProcAddress(ntdll, "ZwWriteVirtualMemory"); ZwClose = (ZwClose_t) GetProcAddress(ntdll, "ZwClose"); if (ZwCreateProcess && ZwQuerySystemInformation && ZwQueryVirtualMemory && ZwCreateThread && ZwGetContextThread && ZwResumeThread && ZwQueryInformationThread && ZwWriteVirtualMemory && ZwClose) { return TRUE; } else { ZwCreateProcess = NULL; ZwQuerySystemInformation = NULL; ZwQueryVirtualMemory = NULL; ZwCreateThread = NULL; ZwGetContextThread = NULL; ZwResumeThread = NULL; ZwQueryInformationThread = NULL; ZwWriteVirtualMemory = NULL; ZwClose = NULL; } return FALSE; } 

El siguiente documento proporciona información sobre el código de portación de UNIX a Win32: https://msdn.microsoft.com/en-us/library/y23kc048.aspx

Entre otras cosas, indica que el modelo de proceso es bastante diferente entre los dos sistemas y recomienda la consideración de CreateProcess y CreateThread donde se requiere el comportamiento fork ().

Antes de que Microsoft presentara su nueva opción de “subsistema Linux para Windows”, CreateProcess() era lo más parecido que Windows tenía que fork() , pero Windows requiere que especifiques un ejecutable para ejecutar en ese proceso.

La creación del proceso de UNIX es bastante diferente de Windows. Su llamada fork() básicamente duplica el proceso actual casi en total, cada uno en su propio espacio de direcciones, y continúa ejecutándolos por separado. Si bien los procesos en sí son diferentes, todavía ejecutan el mismo progtwig. Consulte aquí para obtener una buena visión general del modelo fork/exec .

Volviendo al otro lado, el equivalente de Windows CreateProcess() es el par de funciones fork()/exec() en UNIX.

Si estaba transfiriendo el software a Windows y no le importa una capa de traducción, Cygwin le proporcionó la capacidad que desea, pero fue un poco tonto.

Por supuesto, con el nuevo subsistema Linux , lo más parecido que Windows tiene para fork() es en realidad fork() 🙂

“tan pronto como desee acceder al archivo o imprimir, entonces io son rechazados”

  • No puede tener su pastel y comérselo también … en msvcrt.dll, printf () se basa en la API de la consola, que en sí misma usa lpc para comunicarse con el subsistema de la consola (csrss.exe). La conexión con csrss se inicia al inicio del proceso, lo que significa que cualquier proceso que comience su ejecución “en el medio” tendrá ese paso omitido. A menos que tenga acceso al código fuente del sistema operativo, no tiene sentido tratar de conectarse a csrss manualmente. En su lugar, debe crear su propio subsistema y, en consecuencia, evitar las funciones de la consola en las aplicaciones que usan fork ().

  • una vez que haya implementado su propio subsistema, no olvide duplicar también todos los identificadores de los padres para el proceso secundario 😉

“Además, probablemente no deberías usar las funciones Zw * a menos que estés en modo kernel, probablemente deberías usar las funciones Nt *”.

  • Esto es incorrecto. Cuando se accede en modo de usuario, no hay absolutamente ninguna diferencia entre Zw *** Nt ***; estos son simplemente dos nombres exportados diferentes (ntdll.dll) que hacen referencia a la misma dirección virtual (relativa).

ZwGetContextThread (NtCurrentThread (), & context);

  • obtener el contexto del hilo actual (en ejecución) llamando a ZwGetContextThread es incorrecto, es probable que se cuelgue, y (debido a la llamada al sistema adicional) tampoco es la forma más rápida de llevar a cabo la tarea.

Sus mejores opciones son CreateProcess () o CreateThread () . Hay más información sobre portar aquí .

No hay una manera fácil de emular fork () en Windows.

Le sugiero que use hilos en su lugar.

Lo más cerca que dices … Déjame pensar … Esto debe ser fork (), supongo 🙂

Para más detalles, consulte ¿Interix implementa fork ()?

La semántica de fork () es necesaria cuando el niño necesita acceder al estado real de la memoria del elemento primario a partir del instante en que se llama fork (). Tengo un progtwig que se basa en el mutex implícito de la copia de memoria a partir del instante en que se llama fork (), lo que hace que los hilos sean imposibles de usar. (Esto se emula en las plataformas modern * nix a través de la semántica copy-on-write / update-memory-table).

Lo más cercano que existe en Windows como un syscall es CreateProcess. Lo mejor que se puede hacer es que el padre congele todos los otros hilos durante el tiempo que está copiando la memoria en el espacio de memoria del nuevo proceso, y luego los descongela. Ni la clase de Cygwin frok [sic] ni el código de Scilab que publicó Eric des Courtis se congela, lo que puedo ver.

Además, probablemente no deberías usar las funciones Zw * a menos que estés en modo kernel, probablemente deberías usar las funciones Nt *. Hay una twig adicional que verifica si está en modo kernel y, si no, realiza todos los controles de límites y verificación de parámetros que Nt * siempre hace. Por lo tanto, es un poco menos eficiente llamarlos desde el modo de usuario.

Si solo le interesa crear un subproceso y esperarlo, quizás las API _spawn * en proceso.h sean suficientes. Aquí hay más información sobre eso:

https://docs.microsoft.com/en-us/cpp/c-runtime-library/process-and-environment-control https://en.wikipedia.org/wiki/Process.h