Quelle est la chose la plus proche de windows à fork ()?

je suppose que la question dit tout.

je veux fourcher sur les fenêtres. Ce qui est le plus semblable opération et comment l'utiliser.

108
demandé sur rlbond 2009-06-12 10:36:21

12 réponses

Cygwin a entièrement featured fork() sur Windows. Ainsi, si L'utilisation de Cygwin est acceptable pour vous, alors le problème est résolu dans le cas où la performance n'est pas un problème.

sinon vous pouvez regarder comment Cygwin implémente fork(). D'une architecture tout à fait ancienne de Cygwin doc :

5.6. Processus De Création L'appel de fourche à Cygwin est particulièrement intéressant. parce qu'il n'a pas carte bien en haut de L'API Win32. Cela le rend très difficile à mettre en œuvre correctement. Actuellement, la fourche Cygwin est mise en œuvre sans copie sur papier semblable à ce qui était présent au début saveurs de UNIX.

La première chose qui arrive quand une processus parent bifurque un processus enfant c'est que le parent initialise un espace dans la table de processus de Cygwin pour le enfant. Il crée alors une suspension processus enfant utilisant le Win32 CreateProcess call. Ensuite, le parent le processus appelle setjmp à sauver ses propres contexte et définit un pointeur vers cette une zone de mémoire partagée de Cygwin (partagée parmi toutes les tâches de Cygwin). Il remplit ensuite dans l'enfant .les données et les .sections bss en copiant à partir de son propre espace d'adressage dans la suspension adresse de l'enfant espace. Après l'adresse de l'enfant de l'espace est initialisé, l'enfant est exécutée alors que le parent attend sur un mutex. Enfant découvre qu'il a été Fourché et longjumps en utilisant le tampon de saut sauvegardé. Le l'enfant place alors le mutex le parent attend et bloque un autre mutex. C'est le signal pour le parent pour copier sa pile et tas dans l'enfant, après quoi il libère le mutex de l'enfant attendre et revenir de la fourche appeler. Enfin, l'enfant se réveille à partir de blocage sur le dernier mutex, recrée toute zone cartographiée par mémoire qui lui est transmise via la zone partagée, et retourne de fourche elle-même.

Alors que nous avons quelques idées sur la façon de accélérer la mise en œuvre de nos fourches en réduire le nombre de contextes bascule entre le parent et l'enfant processus, fourche sera presque certainement toujours être inefficace sous Win32. Heureusement, dans la plupart des circonstances de la frayer la famille d'appels fournies par Cygwin peut être substitué à un paire fourchette / exec avec seulement un peu effort. Ces appels carte proprement sur le dessus de L'API Win32. En conséquence, ils sont beaucoup plus efficaces. La modification de la programme pilote du compilateur à appeler frayer au lieu de fork était un trivial changement et compilation accrue la vitesse de vingt à trente pour cent dans nos tests.

cependant, spawn et exec présentent leur propre ensemble de difficultés. Parce qu'il n'y n'est pas une façon de faire une réelle exec sous Win32, Cygwin doit inventer sa propre Id De Processus (Pid). Par conséquent, lorsque un processus exécute plusieurs exec appels, il y aura plusieurs fenêtres Pid associé à un seul Cygwin PID. Dans certains cas, des bouts de chacun des ces processus Win32 peuvent perdurer, attendant leur Cygwin exec'd processus de sortie.

C'est beaucoup de travail, non? Et oui, c'est slooooow.

EDIT: le doc est périmé, s'il vous plaît voir cet excellent réponse pour une mise à jour

74
répondu Laurynas Biveinis 2017-05-23 12:34:37

Je ne connais certainement pas les détails car je ne l'ai jamais fait, mais L'API NT native a la capacité de bifurquer un processus (le sous - système POSIX sur Windows a besoin de cette capacité-Je ne suis pas sûr que le sous-système POSIX soit encore supporté).

une recherche pour ZwCreateProcess () devrait vous obtenir plus de détails - par exemple cette information de Maxim Shatskih :

le plus le paramètre important ici est SectionHandle. Si ce paramètre est NULL, le noyau va bifurquer le processus en cours. Sinon, cette le paramètre doit être une poignée de L'objet SEC_IMAGE section créé sur le fichier EXE avant d'appeler ZwCreateProcess ().

bien que notez que Corinna Vinschen indique que Cygwin trouvé en utilisant ZwCreateProcess () encore peu fiable :

écrit Iker Arizmendi:

> 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

ce document est plutôt vieux, d'une dizaine d'années. Alors que nous sommes toujours à l'aide de Win32 appelle à émuler fork, la méthode a changé sensiblement. Surtout, nous ne créons pas le processus enfant dans l'état suspendu de plus, à moins que des infrastructures de données spécifiques ne nécessitent une manipulation spéciale dans le parent avant d'avoir copié à l'enfant. Dans le courant 1.5.25 libérer le seul cas pour un enfant suspendu sont les douilles ouvertes dans le parent. La prochaine version 1.7.0 la libération ne sera pas suspendue du tout.

une des raisons de ne pas utiliser ZwCreateProcess était que jusqu'au 1.5.25 nous prenons toujours en charge les utilisateurs de Windows 9x. Cependant, deux les tentatives d'utiliser ZwCreateProcess sur des systèmes basés sur NT ont échoué pour un raison ou une autre.

ce serait vraiment bien si ce truc était meilleur ou pas du tout documentée, en particulier un couple d'infrastructures de données et comment processus à un sous-système. Tandis que fourchette n'est pas un Win32 concept, je n'ai pas voir que ce serait une mauvaise chose de rendre fourchette plus facile à mettre en œuvre.

54
répondu Michael Burr 2014-09-24 06:21:10

Eh bien, windows n'a pas vraiment quelque chose tout à fait comme elle. D'autant plus que la fourchette peut être utilisée pour créer conceptuellement un thread ou un processus en *nix.

donc, je dois dire:

CreateProcess() / CreateProcessEx()

et

CreateThread() (j'ai entendu dire que pour les applications C, _beginthreadex() is better).

35
répondu Evan Teran 2018-06-23 14:59:44

les gens ont essayé d'implémenter fork sur Windows. C'est la chose la plus proche que je puisse trouver:

tiré 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;
}
15
répondu Eric des Courtis 2013-02-01 16:02:22

le document suivant fournit quelques informations sur le transfert du code D'UNIX vers Win32.: https://msdn.microsoft.com/en-us/library/y23kc048.aspx

entre autres choses, il indique que le modèle de processus est très différent entre les deux systèmes et recommande la considération de CreateProcess et CreateThread où un comportement de type fork () est requis.

6
répondu Brandon E Taylor 2018-06-23 15:36:21

avant L'introduction de Microsoft leur nouvelle option" Linux subsystem for Windows", CreateProcess() était la chose la plus proche de Windows à fork() , mais Windows exige que vous spécifiez un exécutable pour exécuter dans ce processus.

la création du processus UNIX est très différente de celle de Windows. Son appel fork() reproduit essentiellement le processus actuel presque dans son ensemble, chacun dans son propre espace d'adresse, et continue à les exécuter séparément. Tandis que les processus eux-mêmes sont différents, ils exécutent toujours le même programme . voir ici pour un bon aperçu du modèle fork/exec .

revenant en arrière dans l'autre sens, l'équivalent des fenêtres CreateProcess() est la fork()/exec() paire de fonctions dans UNIX.

si vous portiez un logiciel à Windows et que vous ne vous souciez pas d'une couche de traduction, Cygwin a fourni la capacité que vous voulez mais c'était plutôt kludgey.

bien sûr, avec le nouveau sous-système Linux , la chose la plus proche de Windows à fork() est en fait fork() :-)

5
répondu paxdiablo 2018-04-05 03:30:20

fork () sémantique sont nécessaires lorsque l'enfant a besoin d'accéder à l'état de mémoire réelle du parent à partir de la fourche instantanée () est appelé. J'ai un logiciel qui s'appuie sur le mutex implicite de la copie de mémoire dès que la fourche instantanée() est appelée, ce qui rend les threads impossibles à utiliser. (Ceci est émulé sur les plateformes modernes * nix via copy-on-write / update-memory-table sémantique.)

le plus proche qui existe sur Windows comme un syscall est CreateProcess. Le le mieux que l'on puisse faire est que le parent gèle tous les autres fils pendant le temps qu'il copie la mémoire dans l'espace mémoire du nouveau processus, puis les dégèle. Ni la classe Cygwin frok [sic] ni le code Scilab qu'Eric des Courtis a posté ne fait le thread-freezing, que je peux voir.

de plus, vous ne devriez probablement pas utiliser les fonctions Zw* sauf si vous êtes en mode noyau, vous devriez probablement utiliser les fonctions Nt* à la place. Il y a une autre branche qui vérifie si vous êtes en mode noyau et, si ce n'est pas le cas, vous effectuez toutes les vérifications des limites et des paramètres que Nt* effectue toujours. Il est donc très légèrement moins efficace de les appeler du mode utilisateur.

3
répondu sjcaged 2014-02-06 19:59:16

"dès que vous voulez faire de l'accès aux fichiers ou printf puis io sont refusé"

  • Vous ne pouvez pas avoir votre gâteau et le manger aussi... dans msvcrt.dll, printf () est basé sur L'API de la Console, qui elle-même utilise lpc pour communiquer avec le sous-système de la console (csrss.EXE.) La connexion avec csrss est initiée au démarrage du processus, ce qui signifie que tout processus qui commence son exécution "au milieu" aura cette étape sautée. Sauf si vous avez accès à le code source du système d'exploitation, alors il est inutile d'essayer de se connecter à csrss manuellement. Vous devriez plutôt créer votre propre sous-système, et donc éviter les fonctions de console dans les applications qui utilisent fork().

  • une fois que vous avez mis en œuvre votre propre sous-système, n'oubliez pas de dupliquer tous les poignées du parent pour le processus enfant; -)

" en outre, vous ne devriez probablement pas utiliser les fonctions Zw* sauf si vous êtes en mode noyau, vous devriez probablement utiliser les fonctions Nt* à la place."

  • ceci est incorrect. Lorsqu'on y accède en mode utilisateur, il n'y a absolument aucune différence entre Zw*** Nt**; il s'agit simplement de deux différences (ntdll.dll) noms exportés qui se rapportent à la même adresse virtuelle (relative).

ZwGetContextThread (NtCurrentThread (), & context);

  • obtenir la contexte du thread courant (running) en appelant ZwGetContextThread est erroné, est susceptible de planter, et (en raison de l'appel système supplémentaire) n'est pas non plus le moyen le plus rapide d'accomplir la tâche.
3
répondu user3502619 2014-04-06 01:53:56

vos meilleures options sont CreateProcess () ou CreateThread () . Il y a plus d'informations sur le portage ici .

2
répondu John T 2009-06-12 06:40:43

il n'y a pas de moyen facile d'émuler fork() sur Windows.

je vous suggère d'utiliser des threads à la place.

2
répondu VVS 2009-06-12 06:44:53

le plus proche que tu dis... Laissez-moi réfléchir... Ce doit être fork (), je suppose :)

pour plus de détails, voir est-ce Qu'Interix implémente fork()?

2
répondu Piotr Dobrogost 2017-05-23 11:54:40

si vous ne vous souciez que de créer un sous-processus et de l'attendre, peut-être que L'API _spawn* est en cours.h sont suffisantes. Voici plus d'informations à ce sujet:

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

1
répondu solstice333 2017-09-15 09:24:30