Qual è la cosa più vicina che windows ha da fork ()?

Immagino che la domanda dica tutto.

Voglio biforcarsi su Windows. Qual è l’operazione più simile e come la utilizzo.

Cygwin è dotato di fork () su Windows. Pertanto se l’utilizzo di Cygwin è accettabile per te, il problema viene risolto nel caso in cui la prestazione non sia un problema.

Altrimenti puoi dare un’occhiata a come Cygwin implementa fork (). Da un vecchio documento di architettura Cygwin:

5.6. Creazione del processo La chiamata al fork in Cygwin è particolarmente interessante perché non si adatta bene all’API Win32. Ciò rende molto difficile l’implementazione corretta. Attualmente, la fork Cygwin è un’implementazione non copy-on-write simile a quella presente nei primi sapori di UNIX.

La prima cosa che accade quando un processo genitore forca un processo figlio è che il genitore inizializza uno spazio nella tabella del processo Cygwin per il figlio. Quindi crea un processo figlio sospeso utilizzando la chiamata CreateProcess Win32. Successivamente, il processo padre chiama setjmp per salvare il proprio contesto e imposta un puntatore a questo in un’area di memoria condivisa Cygwin (condivisa tra tutte le attività Cygwin). Riempie quindi le sezioni .data e .bss del bambino copiando dal proprio spazio indirizzo nello spazio degli indirizzi del figlio sospeso. Dopo l’inizializzazione dello spazio indirizzo del figlio, il figlio viene eseguito mentre il genitore attende un mutex. Il bambino scopre che è stato biforcato e longjumps utilizzando il buffer di salto salvato. Il bambino imposta quindi il mutex su cui il genitore è in attesa e blocca su un altro mutex. Questo è il segnale per il genitore di copiare il suo stack e heap nel figlio, dopo di che rilascia il mutex su cui il bambino è in attesa e ritorna dalla chiamata a fork. Infine, il bambino si risveglia dal blocco dell’ultimo mutex, ricrea qualsiasi area mappata alla memoria passata attraverso l’area condivisa e ritorna dalla stessa fork.

Mentre abbiamo alcune idee su come accelerare la nostra implementazione di fork riducendo il numero di switch di contesto tra il genitore e il processo figlio, la fork sarà quasi sicuramente sempre inefficiente con Win32. Fortunatamente, nella maggior parte dei casi, la famiglia di chiamate di spawn fornita da Cygwin può essere sostituita con una coppia fork / exec con un piccolo sforzo. Queste chiamate vengono mappate in modo pulito in cima all’API Win32. Di conseguenza, sono molto più efficienti. Cambiare il programma del driver del compilatore per chiamare spawn al posto di fork è stato un cambiamento insignificante e una maggiore velocità di compilazione dal venti al trenta percento nei nostri test.

Tuttavia, spawn ed exec presentano le proprie difficoltà. Poiché non esiste alcun modo per eseguire un exec in Win32, Cygwin deve inventare i propri Process ID (PID). Di conseguenza, quando un processo esegue più chiamate exec, saranno presenti più PID Windows associati a un singolo PID Cygwin. In alcuni casi, gli stub di ciascuno di questi processi Win32 potrebbero persistere, in attesa dell’uscita del processo Cygwin di exec’d.

Sembra molto lavoro, vero? E sì, è slooooow.

EDIT: il documento è obsoleto, si prega di vedere questa risposta eccellente per un aggiornamento

Certamente non conosco i dettagli su questo perché non l’ho mai fatto, ma l’API NT nativa ha la capacità di eseguire il fork di un processo (il sottosistema POSIX su Windows ha bisogno di questa funzionalità – non sono sicuro se il sottosistema POSIX è persino supportato più).

Una ricerca per ZwCreateProcess () dovrebbe fornirti ulteriori dettagli, ad esempio questo bit di informazioni di Maxim Shatskih :

Il parametro più importante qui è SectionHandle. Se questo parametro è NULL, il kernel esegue il fork del processo corrente. In caso contrario, questo parametro deve essere un handle dell’object della sezione SEC_IMAGE creato nel file EXE prima di chiamare ZwCreateProcess ().

Sebbene si noti che Corinna Vinschen indica che Cygwin ha trovato utilizzando ZwCreateProcess () ancora inaffidabile :

Iker Arizmendi ha scritto:

 > 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 

Questo documento è piuttosto vecchio, circa 10 anni. Mentre stiamo ancora utilizzando le chiamate Win32 per emulare fork, il metodo è cambiato in modo notevole. Soprattutto, non creiamo più il processo figlio nello stato sospeso, a meno che determinati dati non necessitino di una gestione speciale nel genitore prima che vengano copiati nel figlio. Nella versione 1.5.25 attuale l’unico caso per un bambino sospeso sono i socket aperti nel genitore. La prossima release 1.7.0 non sospenderà affatto.

Una ragione per non utilizzare ZwCreateProcess era che fino alla versione 1.5.25 stiamo ancora supportando gli utenti di Windows 9x. Tuttavia, due tentativi di utilizzare ZwCreateProcess su sistemi basati su NT hanno avuto esito negativo per un motivo o per l’altro.

Sarebbe davvero bello se questa roba fosse migliore o del tutto documentata, specialmente un paio di strutture dati e come colbind un processo a un sottosistema. Mentre fork non è un concetto Win32, non vedo che sarebbe una brutta cosa rendere la fork più facile da implementare.

Bene, Windows non ha davvero niente del genere. Tanto più che la fork può essere usata per concettualmente creare un thread o un processo in * nix.

Quindi, dovrei dire:

CreateProcess() / CreateProcessEx()

e

CreateThread() (Ho sentito che per le applicazioni C, _beginthreadex() è migliore).

Le persone hanno provato a implementare fork su Windows. Questa è la cosa più vicina ad esso che posso trovare:

Tratto da: 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; } 

Il seguente documento fornisce alcune informazioni sul porting di codice da UNIX a Win32: https://msdn.microsoft.com/en-us/library/y23kc048.aspx

Tra le altre cose, indica che il modello di processo è abbastanza diverso tra i due sistemi e raccomanda di prendere in considerazione CreateProcess e CreateThread dove è richiesto il comportamento simile a fork ().

Prima che Microsoft introducesse la sua nuova opzione “Sottosistema Linux per Windows”, CreateProcess() era la cosa più vicina a Windows per fork() , ma Windows richiede di specificare un eseguibile da eseguire in quel processo.

La creazione del processo UNIX è abbastanza diversa da Windows. La sua chiamata a fork() praticamente duplica il processo corrente quasi in totale, ciascuno nel proprio spazio indirizzo, e continua a eseguirli separatamente. Mentre i processi stessi sono diversi, stanno ancora eseguendo lo stesso programma. Vedi qui per una buona panoramica del modello fork/exec .

Tornando indietro, l’equivalente di Windows CreateProcess() è la coppia di funzioni fork()/exec() in UNIX.

Se esegui il porting del software su Windows e non ti dispiace un livello di traduzione, Cygwin ha fornito le funzionalità che desideri, ma era piuttosto sciatto.

Ovviamente, con il nuovo sottosistema Linux , la cosa più vicina che Windows ha da fork() è in realtà fork() 🙂

“non appena si vuole fare l’accesso ai file o printf allora io sono rifiutati”

  • Non puoi avere la tua torta e mangiarla anche tu … in msvcrt.dll, printf () si basa sulla console API, che di per sé usa lpc per comunicare con il sottosistema della console (csrss.exe). La connessione con csrss viene avviata all’avvio del processo, il che significa che qualsiasi processo che inizia la sua esecuzione “nel mezzo” avrà saltato quel passo. A meno che tu non abbia accesso al codice sorgente del sistema operativo, allora non ha senso tentare di connettersi a csrss manualmente. Invece, dovresti creare il tuo sottosistema e quindi evitare le funzioni della console nelle applicazioni che usano fork ().

  • una volta implementato il proprio sottosistema, non dimenticare di duplicare anche tutti gli handle del genitore per il processo figlio 😉

“Inoltre, probabilmente non dovresti usare le funzioni Zw * a meno che tu non sia in modalità kernel, dovresti probabilmente usare le funzioni Nt *.”

  • Questo non è corretto Quando si accede in modalità utente, non c’è assolutamente alcuna differenza tra Zw *** Nt ***; questi sono solo due diversi nomi esportati (ntdll.dll) che si riferiscono allo stesso indirizzo (relativo) virtuale.

ZwGetContextThread (NtCurrentThread (), & context);

  • ottenere il contesto del thread corrente (in esecuzione) chiamando ZwGetContextThread è sbagliato, è probabile che si verifichi un arresto anomalo e (a causa della chiamata di sistema aggiuntiva) non è anche il modo più veloce per eseguire l’attività.

Le tue migliori opzioni sono CreateProcess () o CreateThread () . Ci sono ulteriori informazioni sul porting qui .

Non c’è un modo semplice per emulare fork () su Windows.

Ti suggerisco di usare invece i thread.

Il più vicino che dici … Fammi pensare … Questo deve essere forchetta () Immagino 🙂

Per i dettagli, vedere Interix implementa fork ()?

la semantica forchetta () è necessaria laddove il bambino ha bisogno di accedere allo stato di memoria effettivo del genitore a partire dall’istante in cui viene chiamato il fork (). Ho un pezzo di software che si basa sul mutex implicito della copia della memoria a partire dall’istante in cui viene chiamato il fork (), che rende i thread impossibili da usare. (Questo è emulato su piattaforms moderne * nix tramite semantica di copy-on-write / update-memory-table.)

Il più vicino esistente su Windows come syscall è CreateProcess. Il meglio che si può fare è che il genitore congeli tutti gli altri thread durante il tempo in cui copia la memoria nello spazio di memoria del nuovo processo, quindi li scongeli. Né la class Cygwin frok [sic] né il codice Scilab che Eric des Courtis ha pubblicato fanno il congelamento dei thread, che posso vedere.

Inoltre, probabilmente non dovresti usare le funzioni Zw * a meno che tu non sia in modalità kernel, dovresti probabilmente usare le funzioni Nt *. C’è un ramo aggiuntivo che controlla se sei in modalità kernel e, in caso contrario, esegue tutti i controlli sui limiti e la verifica dei parametri che Nt * fa sempre. Pertanto, è leggermente meno efficiente chiamarli dalla modalità utente.

Se ti interessa solo creare un sottoprocesso e aspettarlo, forse API _spawn * in process.h sono sufficienti. Ecco ulteriori informazioni a riguardo:

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