Je suppose que la question dit tout.
Je veux bifurquer sur Windows. Quelle est l'opération la plus similaire et comment l'utiliser ?
Je suppose que la question dit tout.
Je veux bifurquer sur Windows. Quelle est l'opération la plus similaire et comment l'utiliser ?
Cygwin a un fork() complet sous Windows. Ainsi, si l'utilisation de Cygwin est acceptable pour vous, le problème est résolu dans le cas où les performances ne sont pas un problème.
Sinon, vous pouvez regarder comment Cygwin implémente fork(). A partir d'une assez vieille architecture de Cygwin doc :
5.6. Création de processus L'appel fork dans Cygwin est particulièrement intéressant parce qu'il ne correspond pas bien à l'API l'API Win32. Cela le rend très difficile à mettre en œuvre correctement. Actuellement, le fork de Cygwin est une fonction implémentation sans copie sur écriture similaire à ce qui était présent dans les premières dans les premières versions d'UNIX.
La première chose qui se produit lorsqu'un processus parent bifurque vers un processus enfant est que le parent initialise un espace dans la table des processus Cygwin pour le processus enfant. Il crée ensuite un processus enfant suspendu processus enfant suspendu en utilisant l'appel Win32 CreateProcess. Ensuite, le processus parent parent appelle setjmp pour sauvegarder son propre contexte et place un pointeur sur celui-ci dans dans une zone de mémoire partagée Cygwin (partagée partagée entre toutes les tâches Cygwin). Il remplit ensuite les sections .data et .bss de l'enfant. en copiant depuis son propre espace d'adressage dans l'espace d'adressage de l'enfant suspendu. de l'enfant suspendu. Après que l'espace d'adressage du fils est initialisé, l'enfant est exécuté pendant que le parent attend sur un mutex. L'enfant découvre qu'il a été bifurqué et saute longuement en utilisant le tampon de saut sauvegardé. L'enfant active ensuite le mutex sur lequel le parent est en attente et bloque sur un autre mutex. C'est le signal pour parent de copier sa pile et son tas dans l'enfant, après quoi il libère le mutex sur lequel l'enfant enfant et retourne de l'appel à la fourche et retourne de l'appel fork. Enfin, l'enfant se réveille de le blocage sur le dernier mutex, recrée toutes les zones de mémoire qui lui ont été transmises via la zone partagée, et revient de fork lui-même.
Bien que nous ayons quelques idées sur la façon de accélérer l'implémentation de notre fourchette en réduisant le nombre de changements de contexte entre le processus parent et le processus enfant. processus parent et enfant, fork sera presque certainement toujours inefficace sous Win32. Heureusement, dans la plupart des circonstances, la méthode famille d'appels spawn fournie par Cygwin peut être substituée à une paire fork/exec avec peu d'efforts. effort. Ces appels se superposent proprement à de l'API Win32. Par conséquent, ils sont beaucoup plus efficaces. En changeant le du compilateur pour appeler spawn au lieu de fork était un changement trivial trivial et a permis d'augmenter la vitesse vitesse de compilation de vingt à trente pour cent nos tests.
Cependant, le spawn et l'exec présentent leurs propres difficultés. Parce qu'il n'y a il n'y a aucun moyen de faire un vrai exec sous Win32, Cygwin doit inventer ses propres IDs de processus (PIDs). En conséquence, quand un processus exécute plusieurs appels multiples, il y aura plusieurs PID Windows Windows associés à un seul PID Cygwin. CYGWIN. Dans certains cas, des stubs de chacun de ces processus Win32 peuvent traîner, attendant que leur processus Cygwin exécuté exécuté se termine.
Ça semble être beaucoup de travail, n'est-ce pas ? Et oui, c'est lent.
EDIT : la doc est périmée, veuillez voir cet excellent document. réponse pour une mise à jour
C'est une bonne réponse si vous voulez écrire une application Cygwin sur Windows. Mais en général, ce n'est pas la meilleure chose à faire. Fondamentalement, les modèles de processus et de threads *nix et Windows sont assez différents. CreateProcess() et CreateThread() sont des API généralement équivalentes.
Les développeurs doivent garder à l'esprit qu'il s'agit d'un mécanisme non pris en charge, et qu'il a tendance à se briser dès qu'un autre processus du système utilise l'injection de code.
Je ne connais certainement pas les détails de cette opération car je ne l'ai jamais faite, mais l'API NT native a la capacité de forker un processus (le sous-système POSIX de Windows a besoin de cette capacité - je ne suis pas sûr que le sous-système POSIX soit encore supporté).
Une recherche sur ZwCreateProcess() devrait vous permettre d'obtenir plus de détails, par exemple ce petit bout d'information de Maxim Shatskih :
Le paramètre le plus important ici est SectionHandle. Si ce paramètre est NULL, le noyau fera bifurquer le processus actuel. Sinon, ce paramètre paramètre doit être un handle de l'objet de section SEC_IMAGE créé sur le fichier le fichier EXE avant d'appeler ZwCreateProcess().
Cependant, notez que Corinna Vinschen indique que Cygwin a trouvé que l'utilisation de ZwCreateProcess() n'est toujours pas fiable :
Iker Arizmendi a écrit :
> 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 assez ancien, 10 ans environ. Bien que nous utilisions toujours des appels Win32 pour émuler le fork, la méthode a sensiblement changé. En particulier, nous ne créons plus le processus enfant à l'état suspendu plus, sauf si des structures de données spécifiques ont besoin d'une manipulation spéciale dans le parent avant d'être copiées dans le processus enfant. Dans la version actuelle 1.5.25 actuelle, le seul cas d'enfant suspendu est celui des sockets ouverts dans le parent. parent. La prochaine version 1.7.0 ne suspendra plus du tout.
Une des raisons de ne pas utiliser ZwCreateProcess était que jusqu'à la version 1.5.25 nous prenons toujours en charge les utilisateurs de Windows 9x. Cependant, deux tentatives d'utiliser ZwCreateProcess sur des systèmes basés sur NT ont échoué pour une raison ou une autre.
Ce serait vraiment bien si ce genre de choses était mieux ou tout simplement documenté, en particulier quelques structures de données et la façon de connecter un processus à un sous-système. Bien que fork ne soit pas un concept Win32, je ne vois pas Je ne vois pas en quoi ce serait une mauvaise chose de rendre le fork plus facile à mettre en œuvre.
Interix est disponible dans Windows Vista Enterprise/Ultimate en tant que "Subsystem for UNIX Applications" : fr.wikipedia.org/wiki/Interix
@Foredecker - C'est peut-être une mauvaise réponse, mais CreateProcess()/CreateThread() pourrait bien être erroné aussi. Tout dépend si l'on recherche "la façon Win32 de faire les choses" ou "une sémantique aussi proche que possible de celle de fork()". CreateProcess() se comporte de manière très différente de fork(), ce qui est la raison pour laquelle Cygwin a dû faire beaucoup de travail pour le supporter.
Eh bien, Windows n'a pas vraiment quelque chose de semblable. D'autant plus que fork peut être utilisé pour créer conceptuellement un thread ou un processus dans *nix.
Donc, je dois dire :
CreateProcess()
/ CreateProcessEx()
et
CreateThread()
(J'ai entendu cela pour les applications C.) _beginthreadex()
est préférable).
Des gens ont essayé d'implémenter la fourche sur Windows. C'est la chose la plus proche que j'ai pu 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;
}
Notez que la plupart des contrôles d'erreurs sont manquants - par exemple, ZwCreateThread renvoie une valeur NTSTATUS qui peut être vérifiée à l'aide des macros SUCCEEDED et FAILED.
Que se passe-t-il si le fork
se bloque, est-ce qu'il bloque le programme, ou est-ce que le fil de discussion se bloque simplement ? Si cela fait planter le programme, alors ce n'est pas vraiment une bifurcation. Je suis juste curieux, parce que je cherche une vraie solution, et j'espère que cela pourrait être une alternative décente.
Je voudrais noter qu'il y a un bug dans le code fourni. haveLoadedFunctionsForFork est une fonction globale dans l'en-tête, mais une fonction statique dans le fichier c. Les deux devraient être globales. Et actuellement fork se plante, en ajoutant la vérification d'erreur maintenant.
Le document suivant fournit des informations sur le portage de code d'UNIX à Win32 : https://msdn.microsoft.com/en-us/library/y23kc048.aspx
Il indique, entre autres, que le modèle de processus est très différent entre les deux systèmes et recommande de prendre en compte CreateProcess et CreateThread lorsqu'un comportement de type fork() est requis.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.