Windows 与 fork() 最接近的东西是什么?

发布于 2024-07-24 08:31:30 字数 44 浏览 8 评论 0原文

我想在 Windows 上 fork。 最相似的操作是什么?如何使用它?

I want to fork on Windows. What is the most similar operation, and how do I use it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

回忆躺在深渊里 2024-07-31 08:31:31

如果您只关心创建子进程并等待它,也许 process.h 中的 _spawn* API 就足够了。 以下是有关此内容的更多信息:

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

If you only care about creating a subprocess and waiting for it, perhaps _spawn* API's in process.h are sufficient. Here's more information about that:

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

牵你的手,一向走下去 2024-07-31 08:31:31

与“Eric de Courtis”相关的最新链接
回答了此处

由于他的回答是最得体的明确答案,我觉得这个“转发”很方便。

forkWindows.h

#ifndef __FORK_WINDOWS_H__
#define __FORK_WINDOWS_H__
 
#include "BOOL.h"
 
/**
* simulate fork on Windows
*/
int fork(void);
 
/**
* check if symbols to simulate fork are present
* and load these symbols
*/
BOOL haveLoadedFunctionsForFork(void);
 
#endif /* __FORK_WINDOWS_H__ */

>forkWindows.c(已删除)

#include <windows.h>
#include <WinNT.h>
#include <setjmp.h>
#include "forkWindows.h"

typedef LONG NTSTATUS;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG ProcessId;
    UCHAR ObjectTypeNumber;
    UCHAR Flags;
    USHORT Handle;
    PVOID Object;
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

typedef struct _OBJECT_ATTRIBUTES
{
    ULONG Length;
    HANDLE RootDirectory;
    PVOID /* really PUNICODE_STRING */  ObjectName;
    ULONG Attributes;
    PVOID SecurityDescriptor;       /* type SECURITY_DESCRIPTOR */
    PVOID SecurityQualityOfService; /* type SECURITY_QUALITY_OF_SERVICE */
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;

typedef enum _MEMORY_INFORMATION_
{
    MemoryBasicInformation,
    MemoryWorkingSetList,
    MemorySectionName,
    MemoryBasicVlmInformation
} MEMORY_INFORMATION_CLASS;

typedef struct _CLIENT_ID
{
    HANDLE UniqueProcess;
    HANDLE UniqueThread;
} CLIENT_ID, *PCLIENT_ID;

typedef struct _USER_STACK
{
    PVOID FixedStackBase;
    PVOID FixedStackLimit;
    PVOID ExpandableStackBase;
    PVOID ExpandableStackLimit;
    PVOID ExpandableStackBottom;
} USER_STACK, *PUSER_STACK;

typedef LONG KPRIORITY;
typedef ULONG_PTR KAFFINITY;
typedef KAFFINITY *PKAFFINITY;

typedef struct _THREAD_BASIC_INFORMATION
{
    NTSTATUS                ExitStatus;
    PVOID                   TebBaseAddress;
    CLIENT_ID               ClientId;
    KAFFINITY               AffinityMask;
    KPRIORITY               Priority;
    KPRIORITY               BasePriority;
} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;

typedef enum _SYSTEM_INFORMATION_CLASS { SystemHandleInformation = 0x10 } SYSTEM_INFORMATION_CLASS;

typedef NTSTATUS (NTAPI *ZwWriteVirtualMemory_t)(IN HANDLE               ProcessHandle,
        IN PVOID                BaseAddress,
        IN PVOID                Buffer,
        IN ULONG                NumberOfBytesToWrite,
        OUT PULONG              NumberOfBytesWritten OPTIONAL);

typedef NTSTATUS (NTAPI *ZwCreateProcess_t)(OUT PHANDLE            ProcessHandle,
        IN  ACCESS_MASK        DesiredAccess,
        IN  POBJECT_ATTRIBUTES ObjectAttributes,
        IN  HANDLE             InheriteFromProcessHandle,
        IN  BOOLEAN            InheritHandles,
        IN  HANDLE             SectionHandle    OPTIONAL,
        IN  HANDLE             DebugPort        OPTIONAL,
        IN  HANDLE             ExceptionPort    OPTIONAL);

typedef NTSTATUS (WINAPI *ZwQuerySystemInformation_t)(SYSTEM_INFORMATION_CLASS SystemInformationClass,
        PVOID SystemInformation,
        ULONG SystemInformationLength,
        PULONG ReturnLength);
typedef NTSTATUS (NTAPI *ZwQueryVirtualMemory_t)(IN  HANDLE ProcessHandle,
        IN  PVOID BaseAddress,
        IN  MEMORY_INFORMATION_CLASS MemoryInformationClass,
        OUT PVOID MemoryInformation,
        IN  ULONG MemoryInformationLength,
        OUT PULONG ReturnLength OPTIONAL);

typedef NTSTATUS (NTAPI *ZwGetContextThread_t)(IN HANDLE ThreadHandle, OUT PCONTEXT Context);
typedef NTSTATUS (NTAPI *ZwCreateThread_t)(OUT PHANDLE ThreadHandle,
        IN  ACCESS_MASK DesiredAccess,
        IN  POBJECT_ATTRIBUTES ObjectAttributes,
        IN  HANDLE ProcessHandle,
        OUT PCLIENT_ID ClientId,
        IN  PCONTEXT ThreadContext,
        IN  PUSER_STACK UserStack,
        IN  BOOLEAN CreateSuspended);

typedef NTSTATUS (NTAPI *ZwResumeThread_t)(IN HANDLE ThreadHandle, OUT PULONG SuspendCount OPTIONAL);
typedef NTSTATUS (NTAPI *ZwClose_t)(IN HANDLE ObjectHandle);
typedef NTSTATUS (NTAPI *ZwQueryInformationThread_t)(IN HANDLE               ThreadHandle,
        IN THREAD_INFORMATION_CLASS ThreadInformationClass,
        OUT PVOID               ThreadInformation,
        IN ULONG                ThreadInformationLength,
        OUT PULONG              ReturnLength OPTIONAL );

static ZwCreateProcess_t ZwCreateProcess = NULL;
static ZwQuerySystemInformation_t ZwQuerySystemInformation = NULL;
static ZwQueryVirtualMemory_t ZwQueryVirtualMemory = NULL;
static ZwCreateThread_t ZwCreateThread = NULL;
static ZwGetContextThread_t ZwGetContextThread = NULL;
static ZwResumeThread_t ZwResumeThread = NULL;
static ZwClose_t ZwClose = NULL;
static ZwQueryInformationThread_t ZwQueryInformationThread = NULL;
static ZwWriteVirtualMemory_t ZwWriteVirtualMemory = NULL;

#define NtCurrentProcess() ((HANDLE)-1)
#define NtCurrentThread() ((HANDLE) -2)
/*! we use really the Nt versions - so the following is just for completeness */
#define ZwCurrentProcess() NtCurrentProcess()
#define ZwCurrentThread() NtCurrentThread()
#define STATUS_INFO_LENGTH_MISMATCH      ((NTSTATUS)0xC0000004L)
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)

/* setjmp env for the jump back into the fork() function */
static jmp_buf jenv;

/* entry point for our child thread process - just longjmp into fork */
static int child_entry(void)
{
    longjmp(jenv, 1);
    return 0;
}

static BOOL haveLoadedFunctionsForFork(void)
{
    HMODULE 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;
}

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(), ThreadMemoryPriority, &tbi, sizeof tbi, 0);
    tib = (PNT_TIB)tbi.TebBaseAddress;
    ZwQueryInformationThread(hThread, ThreadMemoryPriority, &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;
}

。 .. 问候 !

Up-To-Date link(s), relating to what "Eric de Courtis"
answered HERE:

Since his answer is the most decent explicit answer, I felt this "re-post" is convinient.

forkWindows.h

#ifndef __FORK_WINDOWS_H__
#define __FORK_WINDOWS_H__
 
#include "BOOL.h"
 
/**
* simulate fork on Windows
*/
int fork(void);
 
/**
* check if symbols to simulate fork are present
* and load these symbols
*/
BOOL haveLoadedFunctionsForFork(void);
 
#endif /* __FORK_WINDOWS_H__ */

forkWindows.c (stripped)

#include <windows.h>
#include <WinNT.h>
#include <setjmp.h>
#include "forkWindows.h"

typedef LONG NTSTATUS;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG ProcessId;
    UCHAR ObjectTypeNumber;
    UCHAR Flags;
    USHORT Handle;
    PVOID Object;
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

typedef struct _OBJECT_ATTRIBUTES
{
    ULONG Length;
    HANDLE RootDirectory;
    PVOID /* really PUNICODE_STRING */  ObjectName;
    ULONG Attributes;
    PVOID SecurityDescriptor;       /* type SECURITY_DESCRIPTOR */
    PVOID SecurityQualityOfService; /* type SECURITY_QUALITY_OF_SERVICE */
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;

typedef enum _MEMORY_INFORMATION_
{
    MemoryBasicInformation,
    MemoryWorkingSetList,
    MemorySectionName,
    MemoryBasicVlmInformation
} MEMORY_INFORMATION_CLASS;

typedef struct _CLIENT_ID
{
    HANDLE UniqueProcess;
    HANDLE UniqueThread;
} CLIENT_ID, *PCLIENT_ID;

typedef struct _USER_STACK
{
    PVOID FixedStackBase;
    PVOID FixedStackLimit;
    PVOID ExpandableStackBase;
    PVOID ExpandableStackLimit;
    PVOID ExpandableStackBottom;
} USER_STACK, *PUSER_STACK;

typedef LONG KPRIORITY;
typedef ULONG_PTR KAFFINITY;
typedef KAFFINITY *PKAFFINITY;

typedef struct _THREAD_BASIC_INFORMATION
{
    NTSTATUS                ExitStatus;
    PVOID                   TebBaseAddress;
    CLIENT_ID               ClientId;
    KAFFINITY               AffinityMask;
    KPRIORITY               Priority;
    KPRIORITY               BasePriority;
} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;

typedef enum _SYSTEM_INFORMATION_CLASS { SystemHandleInformation = 0x10 } SYSTEM_INFORMATION_CLASS;

typedef NTSTATUS (NTAPI *ZwWriteVirtualMemory_t)(IN HANDLE               ProcessHandle,
        IN PVOID                BaseAddress,
        IN PVOID                Buffer,
        IN ULONG                NumberOfBytesToWrite,
        OUT PULONG              NumberOfBytesWritten OPTIONAL);

typedef NTSTATUS (NTAPI *ZwCreateProcess_t)(OUT PHANDLE            ProcessHandle,
        IN  ACCESS_MASK        DesiredAccess,
        IN  POBJECT_ATTRIBUTES ObjectAttributes,
        IN  HANDLE             InheriteFromProcessHandle,
        IN  BOOLEAN            InheritHandles,
        IN  HANDLE             SectionHandle    OPTIONAL,
        IN  HANDLE             DebugPort        OPTIONAL,
        IN  HANDLE             ExceptionPort    OPTIONAL);

typedef NTSTATUS (WINAPI *ZwQuerySystemInformation_t)(SYSTEM_INFORMATION_CLASS SystemInformationClass,
        PVOID SystemInformation,
        ULONG SystemInformationLength,
        PULONG ReturnLength);
typedef NTSTATUS (NTAPI *ZwQueryVirtualMemory_t)(IN  HANDLE ProcessHandle,
        IN  PVOID BaseAddress,
        IN  MEMORY_INFORMATION_CLASS MemoryInformationClass,
        OUT PVOID MemoryInformation,
        IN  ULONG MemoryInformationLength,
        OUT PULONG ReturnLength OPTIONAL);

typedef NTSTATUS (NTAPI *ZwGetContextThread_t)(IN HANDLE ThreadHandle, OUT PCONTEXT Context);
typedef NTSTATUS (NTAPI *ZwCreateThread_t)(OUT PHANDLE ThreadHandle,
        IN  ACCESS_MASK DesiredAccess,
        IN  POBJECT_ATTRIBUTES ObjectAttributes,
        IN  HANDLE ProcessHandle,
        OUT PCLIENT_ID ClientId,
        IN  PCONTEXT ThreadContext,
        IN  PUSER_STACK UserStack,
        IN  BOOLEAN CreateSuspended);

typedef NTSTATUS (NTAPI *ZwResumeThread_t)(IN HANDLE ThreadHandle, OUT PULONG SuspendCount OPTIONAL);
typedef NTSTATUS (NTAPI *ZwClose_t)(IN HANDLE ObjectHandle);
typedef NTSTATUS (NTAPI *ZwQueryInformationThread_t)(IN HANDLE               ThreadHandle,
        IN THREAD_INFORMATION_CLASS ThreadInformationClass,
        OUT PVOID               ThreadInformation,
        IN ULONG                ThreadInformationLength,
        OUT PULONG              ReturnLength OPTIONAL );

static ZwCreateProcess_t ZwCreateProcess = NULL;
static ZwQuerySystemInformation_t ZwQuerySystemInformation = NULL;
static ZwQueryVirtualMemory_t ZwQueryVirtualMemory = NULL;
static ZwCreateThread_t ZwCreateThread = NULL;
static ZwGetContextThread_t ZwGetContextThread = NULL;
static ZwResumeThread_t ZwResumeThread = NULL;
static ZwClose_t ZwClose = NULL;
static ZwQueryInformationThread_t ZwQueryInformationThread = NULL;
static ZwWriteVirtualMemory_t ZwWriteVirtualMemory = NULL;

#define NtCurrentProcess() ((HANDLE)-1)
#define NtCurrentThread() ((HANDLE) -2)
/*! we use really the Nt versions - so the following is just for completeness */
#define ZwCurrentProcess() NtCurrentProcess()
#define ZwCurrentThread() NtCurrentThread()
#define STATUS_INFO_LENGTH_MISMATCH      ((NTSTATUS)0xC0000004L)
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)

/* setjmp env for the jump back into the fork() function */
static jmp_buf jenv;

/* entry point for our child thread process - just longjmp into fork */
static int child_entry(void)
{
    longjmp(jenv, 1);
    return 0;
}

static BOOL haveLoadedFunctionsForFork(void)
{
    HMODULE 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;
}

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(), ThreadMemoryPriority, &tbi, sizeof tbi, 0);
    tib = (PNT_TIB)tbi.TebBaseAddress;
    ZwQueryInformationThread(hThread, ThreadMemoryPriority, &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;
}

... regards !

脸赞 2024-07-31 08:31:30

Cygwin 在 Windows 上具有功能齐全的 fork()。 因此,如果您可以接受使用 Cygwin,那么在性能不是问题的情况下问题就解决了。

否则你可以看看Cygwin是如何实现fork()的。 来自相当古老的 Cygwin 架构 doc

5.6。 流程创建
Cygwin 中的 fork 调用特别有趣
因为它不能很好地映射到
Win32 API。 这使得它非常
很难正确实施。
目前,Cygwin 叉子是
非写时复制实现
类似于早期出现的情况
UNIX 风格。

当发生的第一件事
父进程fork出一个子进程
是父进程初始化一个空间
在 Cygwin 进程表中
孩子。 然后它创建一个暂停的
使用 Win32 的子进程
创建进程调用。 接下来,家长
进程调用setjmp来保存自己的
context 并设置一个指向 this 的指针
Cygwin 共享内存区域(共享
在所有 Cygwin 任务中)。 然后它填充
在孩子的 .data 和 .bss 部分
通过从自己的地址空间复制
进入被暂停孩子的地址
空间。 孩子的地址空间之后
初始化后,子进程运行
父进程等待互斥体。 孩子
发现它已经被分叉并且
使用保存的跳转缓冲区进行长跳转。
然后孩子将互斥体设置为
父母正在等待并阻止
另一个互斥体。 这是信号
父级复制其堆栈和堆
进入孩子体内,之后
释放子进程的互斥体
等待并从分叉处返回
称呼。 终于,孩子从梦中醒来
阻塞在最后一个互斥体上,重新创建
传递给它的任何内存映射区域
通过共享区域,并从
分叉本身。

虽然我们对如何做有一些想法
加快我们的分叉实施速度
减少上下文的数量
在父子之间切换
进程,fork 几乎肯定会
在Win32下总是效率低下。
幸运的是,在大多数情况下
提供的调用的生成家族
Cygwin 可以替代
fork/exec 对只有一点点
努力。 这些调用清晰地映射在顶部
Win32 API 的。 结果,他们
效率更高。 改变
编译器调用的驱动程序
使用spawn代替fork是一件微不足道的事情
改变并增加编译
速度提高百分之二十到百分之三十
我们的测试。

然而,spawn 和 exec 展示了他们的
自己的一系列困难。 因为那里
没有办法在下面进行实际的执行
Win32、Cygwin 必须发明自己的
进程 ID (PID)。 结果,当
一个进程执行多个exec
调用时,会有多个Windows
与单个 Cygwin 关联的 PID
PID。 在某些情况下,每个的存根
这些 Win32 进程可能会持续存在,
等待他们的 exec'd Cygwin
进程退出。

听起来工作量很大,不是吗? 是的,它很慢。

编辑:该文档已过时,请参阅这个优秀的 回答更新

Cygwin has fully featured fork() on Windows. Thus if using Cygwin is acceptable for you, then the problem is solved in the case performance is not an issue.

Otherwise you can take a look at how Cygwin implements fork(). From a quite old Cygwin's architecture doc:

5.6. Process Creation
The fork call in Cygwin is particularly interesting
because it does not map well on top of
the Win32 API. This makes it very
difficult to implement correctly.
Currently, the Cygwin fork is a
non-copy-on-write implementation
similar to what was present in early
flavors of UNIX.

The first thing that happens when a
parent process forks a child process
is that the parent initializes a space
in the Cygwin process table for the
child. It then creates a suspended
child process using the Win32
CreateProcess call. Next, the parent
process calls setjmp to save its own
context and sets a pointer to this in
a Cygwin shared memory area (shared
among all Cygwin tasks). It then fills
in the child's .data and .bss sections
by copying from its own address space
into the suspended child's address
space. After the child's address space
is initialized, the child is run while
the parent waits on a mutex. The child
discovers it has been forked and
longjumps using the saved jump buffer.
The child then sets the mutex the
parent is waiting on and blocks on
another mutex. This is the signal for
the parent to copy its stack and heap
into the child, after which it
releases the mutex the child is
waiting on and returns from the fork
call. Finally, the child wakes from
blocking on the last mutex, recreates
any memory-mapped areas passed to it
via the shared area, and returns from
fork itself.

While we have some ideas as to how to
speed up our fork implementation by
reducing the number of context
switches between the parent and child
process, fork will almost certainly
always be inefficient under Win32.
Fortunately, in most circumstances the
spawn family of calls provided by
Cygwin can be substituted for a
fork/exec pair with only a little
effort. These calls map cleanly on top
of the Win32 API. As a result, they
are much more efficient. Changing the
compiler's driver program to call
spawn instead of fork was a trivial
change and increased compilation
speeds by twenty to thirty percent in
our tests.

However, spawn and exec present their
own set of difficulties. Because there
is no way to do an actual exec under
Win32, Cygwin has to invent its own
Process IDs (PIDs). As a result, when
a process performs multiple exec
calls, there will be multiple Windows
PIDs associated with a single Cygwin
PID. In some cases, stubs of each of
these Win32 processes may linger,
waiting for their exec'd Cygwin
process to exit.

Sounds like a lot of work, doesn't it? And yes, it is slooooow.

EDIT: the doc is outdated, please see this excellent answer for an update

↙温凉少女 2024-07-31 08:31:30

我当然不知道这方面的细节,因为我从来没有这样做过,但是本机 NT API 具有分叉进程的功能(Windows 上的 POSIX 子系统需要此功能 - 我不确定 POSIX 子系统是否甚至不再支持)。

搜索 ZwCreateProcess() 应该会为您提供更多详细信息 - 例如 来自 Maxim Shatskih 的信息

这里最重要的参数是SectionHandle。 如果这个参数
为NULL时,内核将fork当前进程。 否则,这
参数必须是创建于 SEC_IMAGE 节对象的句柄
调用 ZwCreateProcess() 之前的 EXE 文件。

但请注意 Corinna Vinschen 表示 Cygwin 使用 ZwCreateProcess 发现() 仍然不可靠

伊克尔·阿里兹门迪写道:

<代码>>   因为 Cygwin 项目完全依赖 Win32 API,所以它的分支 
  >   在分叉的情况下,实现是非 COW 且效率低下的 
  >   后面没有 exec。   它也相当复杂。   请参阅此处(部分 
  >   5.6)详情: 
  >   
  >   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 用户。 然而,两个
尝试在基于 NT 的系统上使用 ZwCreateProcess 失败了
原因或其他。

如果这个东西能更好或者有的话那就太好了
记录下来,特别是一些数据结构以及如何连接
处理子系统。 虽然 fork 不是 Win32 概念,但我不这么认为
看到让 fork 更容易实现是一件坏事。

I certainly don't know the details on this because I've never done it it, but the native NT API has a capability to fork a process (the POSIX subsystem on Windows needs this capability - I'm not sure if the POSIX subsystem is even supported anymore).

A search for ZwCreateProcess() should get you some more details - for example this bit of information from Maxim Shatskih:

The most important parameter here is SectionHandle. If this parameter
is NULL, the kernel will fork the current process. Otherwise, this
parameter must be a handle of the SEC_IMAGE section object created on
the EXE file before calling ZwCreateProcess().

Though note that Corinna Vinschen indicates that Cygwin found using ZwCreateProcess() still unreliable:

Iker Arizmendi wrote:

> 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

This document is rather old, 10 years or so. While we're still using
Win32 calls to emulate fork, the method has changed noticably.
Especially, we don't create the child process in the suspended state
anymore, unless specific datastructes need a special handling in the
parent before they get copied to the child. In the current 1.5.25
release the only case for a suspended child are open sockets in the
parent. The upcoming 1.7.0 release will not suspend at all.

One reason not to use ZwCreateProcess was that up to the 1.5.25
release we're still supporting Windows 9x users. However, two
attempts to use ZwCreateProcess on NT-based systems failed for one
reason or another.

It would be really nice if this stuff would be better or at all
documented, especially a couple of datastructures and how to connect a
process to a subsystem. While fork is not a Win32 concept, I don't
see that it would be a bad thing to make fork easier to implement.

又怨 2024-07-31 08:31:30

嗯,Windows 确实没有类似的东西。 特别是因为 fork 可以在概念上用于在 *nix 中创建线程或进程。

所以,我不得不说:

CreateProcess() /CreateProcessEx()

CreateThread()(我听说对于 C 应用程序,_beginthreadex() 更好)。

Well, windows doesn't really have anything quite like it. Especially since fork can be used to conceptually create a thread or a process in *nix.

So, I'd have to say:

CreateProcess()/CreateProcessEx()

and

CreateThread() (I've heard that for C applications, _beginthreadex() is better).

是你 2024-07-31 08:31:30

人们尝试在 Windows 上实现 fork。 这是我能找到的最接近的东西:

取自: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;
}

People have tried to implement fork on Windows. This is the closest thing to it I can find:

Taken from: 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;
}
傲世九天 2024-07-31 08:31:30

正如其他答案所提到的,NT(现代 Windows 版本的底层内核)具有相当于 Unix fork() 的功能。 那不是问题。

问题在于,克隆进程的整个状态通常不是一件明智的事情。 这在 Unix 世界和 Windows 中都是如此,但在 Unix 世界中,一直使用 fork(),并且库被设计来处理它。 Windows 库则不然。

例如,系统 DLL kernel32.dll 和 user32.dll 维护与 Win32 服务器进程 csrss.exe 的专用连接。 分叉后,该连接的客户端有两个进程,这会导致问题。 子进程应该通知 csrss.exe 它的存在并建立一个新的连接 - 但没有接口可以做到这一点,因为这些库在设计时没有考虑到 fork()。

所以你有两个选择。 一是禁止使用 kernel32 和 user32 以及其他并非旨在分叉的库 - 包括任何直接或间接链接到 kernel32 或 user32 的库,这实际上是所有库。 这意味着您根本无法与 Windows 桌面交互,并且被困在您自己独立的 Unixy 世界中。 这是 NT 的各种 Unix 子系统所采用的方法。

另一种选择是采用某种可怕的黑客手段来尝试让不知情的库使用 fork()。 这就是 Cygwin 所做的。 它创建一个新进程,让它初始化(包括向 csrss.exe 注册自身),然后从旧进程复制大部分动态状态,并希望得到最好的结果。 令我惊讶的是,这曾经有效。 它当然不能可靠地工作——即使它不会由于地址空间冲突而随机失败,您正在使用的任何库都可能会默默地处于损坏状态。 当前接受的答案声称 Cygwin 具有“功能齐全的 fork()”,这是......可疑的。

简介: 在类似Interix 的环境中,您可以通过调用fork() 来进行分叉。 否则,请尝试戒掉这样做的欲望。 即使您的目标是 Cygwin,也不要使用 fork(),除非绝对必要。

As other answers have mentioned, NT (the kernel underlying modern versions of Windows) has an equivalent of Unix fork(). That's not the problem.

The problem is that cloning a process's entire state is not generally a sane thing to do. This is as true in the Unix world as it is in Windows, but in the Unix world, fork() is used all the time, and libraries are designed to deal with it. Windows libraries aren't.

For example, the system DLLs kernel32.dll and user32.dll maintain a private connection to the Win32 server process csrss.exe. After a fork, there are two processes on the client end of that connection, which is going to cause problems. The child process should inform csrss.exe of its existence and make a new connection – but there's no interface to do that, because these libraries weren't designed with fork() in mind.

So you have two choices. One is to forbid the use of kernel32 and user32 and other libraries that aren't designed to be forked – including any libraries that link directly or indirectly to kernel32 or user32, which is virtually all of them. This means that you can't interact with the Windows desktop at all, and are stuck in your own separate Unixy world. This is the approach taken by the various Unix subsystems for NT.

The other option is to resort to some sort of horrible hack to try to get unaware libraries to work with fork(). That's what Cygwin does. It creates a new process, lets it initialize (including registering itself with csrss.exe), then copies most of the dynamic state over from the old process and hopes for the best. It amazes me that this ever works. It certainly doesn't work reliably – even if it doesn't randomly fail due to an address space conflict, any library you're using may be silently left in a broken state. The claim of the current accepted answer that Cygwin has a "fully-featured fork()" is... dubious.

Summary: In an Interix-like environment, you can fork by calling fork(). Otherwise, please try to wean yourself from the desire to do it. Even if you're targeting Cygwin, don't use fork() unless you absolutely have to.

给妤﹃绝世温柔 2024-07-31 08:31:30

在 Microsoft 推出新的“Windows Linux 子系统”选项之前,CreateProcess() 是 Windows 与 fork() 最接近的东西,但 Windows 要求您指定一个可执行文件在该过程中运行。

UNIX 进程的创建与Windows 有很大不同。 它的 fork() 调用基本上几乎完全复制了当前进程,每个进程都在自己的地址空间中,并继续单独运行它们。 虽然进程本身不同,但它们仍然运行相同的程序。请参阅这里可以很好地概述fork/exec模型。

反过来说,Windows CreateProcess() 的等价物是 UNIX 中的fork()/exec()函数。

如果您将软件移植到 Windows 并且不介意翻译层,Cygwin 提供了您想要的功能,但它相当混乱。

当然,新的 Linux 子系统 ,Windows 与 fork() 最接近的东西是实际上 fork() :-)

Prior to Microsoft introducing their new "Linux subsystem for Windows" option, CreateProcess() was the closest thing Windows has to fork(), but Windows requires you to specify an executable to run in that process.

The UNIX process creation is quite different to Windows. Its fork() call basically duplicates the current process almost in total, each in their own address space, and continues running them separately. While the processes themselves are different, they are still running the same program. See here for a good overview of the fork/exec model.

Going back the other way, the equivalent of the Windows CreateProcess() is the fork()/exec() pair of functions in UNIX.

If you were porting software to Windows and you don't mind a translation layer, Cygwin provided the capability that you want but it was rather kludgey.

Of course, with the new Linux subsystem, the closest thing Windows has to fork() is actually fork() :-)

平生欢 2024-07-31 08:31:30

以下文档提供了有关将代码从 UNIX 移植到 Win32 的一些信息:
https://msdn.microsoft.com/en-us/library/y23kc048。除此之外

,它表明两个系统之间的进程模型有很大不同,并且建议在需要类似 fork() 的行为时考虑 CreateProcess 和 CreateThread。

The following document provides some information on porting code from UNIX to Win32:
https://msdn.microsoft.com/en-us/library/y23kc048.aspx

Among other things, it indicates that the process model is quite different between the two systems and recommends consideration of CreateProcess and CreateThread where fork()-like behavior is required.

2024-07-31 08:31:30

“一旦你想要进行文件访问或 printf,那么 io 就会被拒绝”

  • 鱼和熊掌不可兼得……在 msvcrt.dll 中,printf() 基于 Console API,它本身使用 lpc 与控制台子系统 (csrss.exe) 进行通信。 与 csrss 的连接是在进程启动时启动的,这意味着任何“在中间”开始执行的进程都将跳过该步骤。 除非您有权访问操作系统的源代码,否则尝试手动连接 csrss 是没有意义的。 相反,您应该创建自己的子系统,并相应地避免使用 fork() 的应用程序中的控制台函数。

  • 一旦您实现了自己的子系统,请不要忘记为子进程复制所有父进程的句柄;-)

“此外,除非处于内核模式,否则您可能不应该使用 Zw* 函数,您可能应该使用 Nt* 函数。”

  • 这是不正确的。 在用户态访问时,Zw*** Nt*** 绝对没有区别; 这些只是引用相同(相对)虚拟地址的两个不同(ntdll.dll)导出名称。

ZwGetContextThread(NtCurrentThread(), &context);

  • 通过调用 ZwGetContextThread 获取当前(正在运行)线程的上下文是错误的,可能会崩溃,并且(由于额外的系统调用)也不是完成任务的最快方法。

"as soon as you want to do file access or printf then io are refused"

  • You cannot have your cake and eat it too... in msvcrt.dll, printf() is based on the Console API, which in itself uses lpc to communicate with the console subsystem (csrss.exe). Connection with csrss is initiated at process start-up, which means that any process that begins its execution "in the middle" will have that step skipped. Unless you have access to the source code of the operating system, then there is no point in trying to connect to csrss manually. Instead, you should create your own subsystem, and accordingly avoid the console functions in applications that use fork().

  • once you have implemented your own subsystem, don't forget to also duplicate all of the parent's handles for the child process;-)

"Also, you probably shouldn't use the Zw* functions unless you're in kernel mode, you should probably use the Nt* functions instead."

  • This is incorrect. When accessed in user mode, there is absolutely no difference between Zw*** Nt***; these are merely two different (ntdll.dll) exported names that refer to the same (relative) virtual address.

ZwGetContextThread(NtCurrentThread(), &context);

  • obtaining the context of the current (running) thread by calling ZwGetContextThread is wrong, is likely to crash, and (due to the extra system call) is also not the fastest way to accomplishing the task.
陌路黄昏 2024-07-31 08:31:30

您最好的选择是 CreateProcess()CreateThread()此处提供了有关移植的更多信息。

Your best options are CreateProcess() or CreateThread(). There is more information on porting here.

灼痛 2024-07-31 08:31:30

没有简单的方法可以在 Windows 上模拟 fork()。

我建议您改用线程。

There is no easy way to emulate fork() on Windows.

I suggest you to use threads instead.

夏尔 2024-07-31 08:31:30

你说的最接近...让我想想...我想这一定是 fork() :)

有关详细信息,请参阅 Interix 是否实现 fork( )?

The closest you say... Let me think... This must be fork() I guess :)

For details see Does Interix implement fork()?

一影成城 2024-07-31 08:31:30

当子进程需要访问父进程在调用 fork() 时的实际内存状态时,fork() 语义是必要的。 我有一个软件,它依赖于调用即时 fork() 时内存复制的隐式互斥体,这使得线程无法使用。 (这是通过写时复制/更新内存表语义在现代 *nix 平台上模拟的。)

Windows 上最接近的系统调用是 CreateProcess。 最好的办法是父进程在将内存复制到新进程的内存空间期间冻结所有其他线程,然后解冻它们。 我可以看到,Cygwin frok [原文如此] 类和 Eric des Courtis 发布的 Scilab 代码都没有执行线程冻结。

另外,除非处于内核模式,否则您可能不应该使用 Zw* 函数,而应该使用 Nt* 函数。 有一个额外的分支检查您是否处于内核模式,如果不是,则执行 Nt* 始终执行的所有边界检查和参数验证。 因此,从用户模式调用它们的效率稍低。

fork() semantics are necessary where the child needs access to the actual memory state of the parent as of the instant fork() is called. I have a piece of software which relies on the implicit mutex of memory copying as of the instant fork() is called, which makes threads impossible to use. (This is emulated on modern *nix platforms via copy-on-write/update-memory-table semantics.)

The closest that exists on Windows as a syscall is CreateProcess. The best that can be done is for the parent to freeze all other threads during the time that it is copying memory over to the new process's memory space, then thaw them. Neither the Cygwin frok [sic] class nor the Scilab code that Eric des Courtis posted does the thread-freezing, that I can see.

Also, you probably shouldn't use the Zw* functions unless you're in kernel mode, you should probably use the Nt* functions instead. There's an extra branch that checks whether you're in kernel mode and, if not, performs all of the bounds checking and parameter verification that Nt* always do. Thus, it's very slightly less efficient to call them from user mode.

宁愿没拥抱 2024-07-31 08:31:30

大多数黑客解决方案都已经过时了。 Winnie the fuzzer 有一个适用于当前版本的 Windows 10 的 fork 版本(尽管这需要系统特定的偏移量,并且也很容易崩溃)。

https://github.com/sslab-gatech/winnie/tree/master/叉库

Most of the hacky solutions are outdated. Winnie the fuzzer has a version of fork that works on current versions of Windows 10 (tho this requires system specific offsets and can break easily too).

https://github.com/sslab-gatech/winnie/tree/master/forklib

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文