Я думаю, вопрос говорит сам за себя.
Я хочу раскошелиться на Windows. Какая операция наиболее похожа и как ее использовать.
Я думаю, вопрос говорит сам за себя.
Я хочу раскошелиться на Windows. Какая операция наиболее похожа и как ее использовать.
Cygwin имеет полнофункциональный fork() в Windows. Таким образом, если использование Cygwin приемлемо для вас, то проблема решена в случае, если производительность не является проблемой.
В противном случае вы можете посмотреть, как Cygwin реализует fork(). Из довольно старой архитектуры Cygwin doc:
5,6. Создание процесса Особенно интересен вызов вилки в Cygwin потому что он плохо отображается на вершине API Win32. Это делает его очень трудно реализовать правильно. В настоящее время вилка Cygwin представляет собой реализация без копирования на запись аналогично тому, что было в начале ароматов UNIX.
Первое, что происходит, когда родительский процесс вызывает дочерний процесс что родитель инициализирует пространство в таблице процессов Cygwin для ребенок. Затем он создает приостановленный дочерний процесс с использованием Win32 CreateProcess. Затем родительский обрабатывает вызовы setjmp, чтобы сохранить свои собственные контекст и устанавливает указатель на это в область общей памяти Cygwin (общая среди всех задач Cygwin). Затем он заполняет в дочерних разделах .data и .bss путем копирования из собственного адресного пространства на приостановленный адрес ребенка пространство. После адресного пространства для детей инициализируется, ребенок запускается во время родитель ждет мьютекс. Ребенок обнаруживает, что он был раздвоен и longjumps с использованием сохраненного буфера переходов. Затем ребенок устанавливает мьютекс родитель ждет и блокирует другой мьютекс. Это сигнал для родитель, чтобы скопировать его стек и кучу в ребенка, после чего он релиз мьютекса, который ребенок ожидание и возврат из вилки вызов. Наконец, ребенок просыпается от блокировка последнего мьютекса, воссоздает любые переданные на него области памяти через общую область и возвращает самой вилкой.
Хотя у нас есть некоторые идеи относительно того, как ускорить реализацию нашей вилки сокращение количества контекстов переключатели между родительским и дочерним процесс, вилка почти наверняка всегда всегда неэффективны в Win32. К счастью, в большинстве случаев вызвать множество вызовов, предоставляемых Cygwin можно заменить на fork/exec с небольшим количеством усилия. Эти призывы отображают чисто сверху API Win32. В результате они намного эффективнее. Изменение программа-драйвер компилятора для вызова Икру вместо вилки был тривиальным изменение и увеличение компиляции скорости на двадцать-тридцать процентов в наши тесты.
Однако, spawn и exec представляют свои собственный набор трудностей. Потому что там не может делать фактического Win32, Cygwin должен изобретать собственные Идентификаторы процессов (PID). В результате, когда процесс выполняет несколько exec вызовов, будет несколько окон PID, связанные с одним Cygwin PID. В некоторых случаях, заглушки каждого из эти процессы Win32 могут задерживаться, ожидая их exec'd Cygwin процесс для выхода.
Звучит много работы, не так ли? И да, это slooooow.
EDIT: документ устарел, посмотрите этот отличный ответ для обновления
Я, конечно, не знаю подробностей об этом, потому что я никогда не делал этого, но собственный API-интерфейс NT имеет возможность разветвлять процесс (подсистема POSIX на Windows нуждается в этой возможности - я не уверен, что подсистема POSIX поддерживается даже больше).
Поиск ZwCreateProcess() должен предоставить вам более подробную информацию - например этот бит информации от Максима Шацких:
Самый важный параметр здесь - SectionHandle. Если этот параметр является NULL, ядро откажет текущий процесс. В противном случае это Параметр должен быть дескриптором объекта секции SEC_IMAGE, созданного на EXE файл перед вызовом ZwCreateProcess().
Обратите внимание, что Коринна Винсшен указывает, что Cygwin обнаружил, что ZwCreateProcess() все еще ненадежен:
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 also rather complex. See here (section > 5.6) for details: > > http://www.redhat.com/support/wpapers/cygnus/cygnus_cygwin/architecture.html
Этот документ довольно старый, 10 лет или около того. Пока мы все еще используем Вызов Win32 для эмуляции fork, метод изменился заметно. В частности, мы не создаем дочерний процесс в приостановленном состоянии больше, если конкретные данные не требуют специальной обработки в родителя, прежде чем они будут скопированы для ребенка. В текущем 1.5.25 освободить единственный случай для приостановленного ребенка - это открытые сокеты в Родитель. Предстоящая версия 1.7.0 не будет приостанавливаться вообще.
Одной из причин не использовать ZwCreateProcess было то, что до 1.5.25 релиз мы по-прежнему поддерживаем пользователей Windows 9x. Однако два попытки использования ZwCreateProcess на NT-системах не удались для одного по тем или иным причинам.
Было бы очень приятно, если бы этот материал был бы лучше или вообще задокументированы, особенно пара данных и как подключить процесс в подсистему. Хотя fork не является концепцией Win32, я не см., что было бы неплохо сделать вилку проще.
Ну, у окон действительно нет ничего подобного. Тем более, что fork можно использовать для концептуального создания потока или процесса в * nix.
Итак, я должен сказать:
CreateProcess()
/CreateProcessEx()
а также
CreateThread()
(я слышал, что для приложений C лучше использовать _beginthreadex()
).
Люди пытались реализовать fork в Windows. Это самая близкая вещь, которую я могу найти:
Взято из: 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 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;
}
В следующем документе представлена некоторая информация о переносе кода из UNIX в Win32: https://msdn.microsoft.com/en-us/library/y23kc048.aspx
Среди прочего, это указывает на то, что модель процесса отличается от двух систем и рекомендует рассматривать CreateProcess и CreateThread, где требуется поведение fork() -like.
До того, как Microsoft представила свою новую "подсистему Linux для Windows", CreateProcess()
был самым близким к тому, что Windows имеет fork()
, но Windows требует указать исполняемый файл для запуска в этом процессе.
Процесс создания UNIX сильно отличается от Windows. Его вызов fork()
основном дублирует текущий процесс почти в целом, каждый в своем собственном адресном пространстве, и продолжает запускать их отдельно. Хотя сами процессы разные, они все еще работают с одной и той же программой. См. Здесь хороший обзор модели fork/exec
.
Возвращаясь в другую сторону, эквивалент Windows CreateProcess()
- это пара функций fork()/exec()
в UNIX.
Если вы портировали программное обеспечение на Windows и не против слоя перевода, Cygwin предоставил возможность, которую вы хотите, но это было довольно kludgey.
Конечно, с новой подсистемой Linux, самая близкая вещь для Windows к fork()
- это фактически fork()
:-)
fork() необходимы семантика, когда ребенку нужен доступ к фактическому состоянию памяти родителя при вызове моментального fork(). У меня есть часть программного обеспечения, которое опирается на неявный мьютекс копирования памяти с момента вызова fork(), что делает потоки невозможными для использования. (Это эмулируется на современных платформах nix посредством семантики copy-on-write/update-memory-table.)
Ближе всего, что существует в Windows в качестве syscall, является CreateProcess. Лучшее, что можно сделать, - это заставить родителя замораживать все остальные потоки в то время, когда он копирует память в новое пространство памяти процесса, а затем оттаивает их. Ни класс Cygwin frok [sic], ни код Scilab, который опубликовал Eric des Courtis, не затухают в потоке, что я вижу.
Кроме того, вы, вероятно, не должны использовать функции Zw *, если вы не находитесь в режиме ядра, вы должны, вероятно, использовать функции Nt *. Там есть дополнительная ветка, которая проверяет, находится ли вы в режиме ядра, а если нет, выполняет всю проверку границ и проверку параметров, которые всегда выполняет Nt *. Таким образом, это очень немного менее эффективно для вызова их из пользовательского режима.
", как только вы захотите сделать доступ к файлам или printf, тогда io отказано"
Вы не можете получить свой торт и съесть его тоже... в msvcrt.dll, printf() основан на API-интерфейсе Console, который сам по себе использует lpc для связи с консольной подсистемой (csrss.exe). Соединение с csrss запускается при запуске процесса, а это означает, что любой процесс, который начинает свое выполнение "посередине", пропустит этот шаг. Если у вас нет доступа к исходному коду операционной системы, тогда нет смысла пытаться подключиться к csrss вручную. Вместо этого вы должны создать свою собственную подсистему и, соответственно, избегать функций консоли в приложениях, использующих fork().
после того, как вы внедрили свою собственную подсистему, не забудьте также дублировать все родительские дескрипторы для дочернего процесса; -)
"Кроме того, вы, вероятно, не должны использовать функции Zw *, если вы не находитесь в режиме ядра, вы, вероятно, должны использовать функции Nt *.
ZwGetContextThread (NtCurrentThread(), & контекст);
Ваши лучшие варианты CreateProcess() или CreateThread(). Более подробная информация о переносе здесь.
Нет простого способа эмулировать fork() в Windows.
Я предлагаю вам вместо этого использовать потоки.
Ближайший вы говорите... Позвольте мне подумать... Это должно быть fork() Я думаю:)
Подробнее см. Использует ли Interix fork()?
Если вы только заботитесь о создании подпроцесса и ожидаете его, возможно, _spawn * API в process.h достаточно. Здесь больше информации об этом:
https://docs.microsoft.com/en-us/cpp/c-runtime-library/process-and-environment-control https://en.wikipedia.org/wiki/Process.h