programing

Windows가 fork()에 가장 가까운 것은 무엇입니까?

firstcheck 2022. 7. 28. 22:19
반응형

Windows가 fork()에 가장 가까운 것은 무엇입니까?

질문이 모든 걸 말해주는 것 같아.

Windows를 포크로 하고 싶다.가장 유사한 조작은 무엇이며 어떻게 사용하는가?

자세한 것은 아직 모릅니다만, 네이티브 NT API에는 프로세스를 분기하는 기능이 있습니다(Windows의 POSIX 서브시스템은 이 기능을 필요로 합니다.POSIX 서브시스템이 지원되고 있는지조차 잘 모르겠습니다).

ZwCreateProcess()를 검색하면 상세 정보를 얻을 수 있습니다.를 들어 Maxim Shatskih에서 다음 정보를 얻을 수 있습니다.

여기서 가장 중요한 파라미터는 SectionHandle입니다.이 파라미터가 NULL일 경우 커널은 현재 프로세스를 포크합니다.그렇지 않으면 이 파라미터는 SEC_의 핸들이어야 합니다.ZwCreateProcess()를 호출하기 전에 EXE 파일에 작성된 IMAGE 섹션오브젝트

Corinna Vinschen은 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's also rather complex. See here (section
> 5.6) for details:
>  
> http://www.redhat.com/support/wpapers/cygnus/cygnus_cygwin/architecture.html

이 문서는 10년 정도 된 오래된 문서입니다.포크를 에뮬레이트하기 위해 Win32 콜을 사용하고 있는 동안 메서드는 눈에 띄게 변경되었습니다.특히 특정 데이터 구조가 자녀에게 복사되기 전에 부모에서 특별한 처리를 필요로 하지 않는 한 더 이상 자녀 프로세스를 일시 중단된 상태로 만들지 않습니다.현재의 1.5.25 릴리스에서는 부모 소켓에서 자녀에 대한 유일한 경우는 열린 소켓입니다.다음 1.7.0 릴리즈에서는 전혀 중단되지 않습니다.

ZwCreateProcess를 사용하지 않는 이유 중 하나는 1.5.25 릴리스까지 Windows 9x 사용자를 계속 지원한다는 것입니다.그러나 NT 기반 시스템에서 ZwCreateProcess를 사용하려는 두 가지 시도가 여러 가지 이유로 실패했습니다.

특히 몇 가지 데이터 구조 및 프로세스를 서브시스템에 연결하는 방법 등 이러한 내용이 더 낫거나 문서화되어 있다면 정말 좋을 것입니다.포크는 Win32 컨셉은 아니지만, 포크를 구현하기 쉽게 하는 것이 나쁘다고는 생각하지 않습니다.

Cygwin은 Windows에서 fork() 기능을 모두 갖추고 있습니다.따라서 Cygwin을 사용해도 문제가 없는 경우 문제가 해결됩니다.

그렇지 않으면 Cygwin이 fork()를 구현하는 방법을 볼 수 있습니다.꽤 오래된 Cygwin의 아키텍처 문서:

5.6. 프로세스 생성 Cygwin의 포크 콜은 특히 Win32 API 상에서 잘 매핑되지 않기 때문에 매우 흥미롭습니다.따라서 올바르게 구현하기가 매우 어렵습니다.현재 Cygwin 포크는 초기 버전의 UNIX와 유사한 비복사-on-write 구현입니다.

부모 프로세스가 자녀 프로세스를 포크할 때 가장 먼저 발생하는 것은 부모가 자녀에 대한 Cygwin 프로세스 테이블의 공간을 초기화하는 것입니다.그런 다음 Win32 CreateProcess 호출을 사용하여 일시 중단된 하위 프로세스를 만듭니다.다음으로 부모 프로세스는 setjmp를 호출하여 자체 컨텍스트를 저장하고 Cygwin 공유 메모리 영역(모든 Cygwin 태스크 간에 공유됨)에 포인터를 설정합니다.그런 다음 자신의 주소 공간에서 일시 중단된 자녀의 주소 공간으로 복사하여 자녀의 .data 및 .bss 섹션을 채웁니다.자녀의 주소 공간이 초기화되면 부모가 뮤텍스에서 대기하는 동안 자녀가 실행됩니다.아이는 저장된 점프 버퍼를 사용하여 포크 및 롱점프가 발생했음을 알게 된다.그런 다음 자녀가 부모가 대기 중인 뮤텍스를 설정하고 다른 뮤텍스를 차단합니다.이것은 부모에게 스택과 힙을 자녀에게 복사하도록 하는 신호이며, 그 후 자녀가 대기하고 있는 뮤텍스를 해제하고 포크콜에서 돌아갑니다마지막으로 아이는 마지막 뮤텍스 블로킹에서 깨어나 공유 영역을 통해 전달된 메모리 매핑 영역을 재생성하고 포크에서 돌아갑니다.

부모 프로세스와 자녀 프로세스 간의 컨텍스트 전환 수를 줄임으로써 포크 구현을 가속화하는 방법에 대한 아이디어는 있지만, Win32에서는 포크는 거의 항상 비효율적입니다.다행히 대부분의 경우 Cygwin에 의해 제공되는 콜의 생성 패밀리는 약간의 노력만으로 포크/exec 쌍을 대체할 수 있습니다.이러한 콜은 Win32 API 상부에 깔끔하게 매핑됩니다.그 결과, 그것들은 훨씬 더 효율적입니다.컴파일러의 드라이버 프로그램을 포크 대신 호출 생성으로 변경한 것은 사소한 변경이었고 테스트에서 컴파일 속도를 20~30% 향상시켰습니다.

단, 생성과 실행은 각자의 어려움을 제시합니다.Win32에서는 실제 exec을 실행할 방법이 없기 때문에 Cygwin은 자체 프로세스 ID(PID)를 개발해야 합니다.그 결과, 프로세스가 복수의 exec 콜을 실행하는 경우, 1개의 Cygwin PID 에 복수의 Windows PID 가 관련지어집니다.경우에 따라서는 이러한 Win32 프로세스의 stub가 execute'd Cygwin 프로세스가 종료되기를 기다리며 남아 있을 수 있습니다.

힘든 일인 것 같지 않아?그리고 네, 지저분합니다.

편집: 문서가 오래되었습니다.업데이트에 대해서는, 이 훌륭한 답변을 참조해 주세요.

창문에 이런 게 없어요특히 *nix에서는 개념적으로 스레드 또는 프로세스를 작성할 때 포크를 사용할 수 있습니다.

그래서 이렇게 말해야겠네요.

CreateProcess()/CreateProcessEx()

그리고.

CreateThread() (C 어플리케이션은 좋다고 들었습니다).

다른 답변에서 언급했듯이 NT(현대판 Windows의 기반이 되는 커널)는 Unix fork()와 동등합니다.그게 문제가 아니야.

문제는 프로세스의 전체 상태를 복제하는 것은 일반적으로 정상적인 작업이 아니라는 것입니다.이것은, Windows 와 같이 Unix 세계에서도 마찬가지입니다만, Unix 세계에서는 항상 fork() 가 사용되고 있어 라이브러리는 이것에 대응하도록 설계되어 있습니다.Windows 라이브러리는 그렇지 않습니다.

예를 들어 시스템 DLL kernel32.dll 및 user32.dll은 Win32 서버 프로세스 csrss.exe에 대한 개인 연결을 유지합니다.포크 후에 그 접속의 클라이언트 측에는 2개의 프로세스가 있어 문제가 발생합니다.자프로세스가 csrss에 통지해야 합니다.이 라이브러리는 fork()를 염두에 두고 설계되지 않았기 때문에 새로운 접속을 확립할 인터페이스가 없습니다.

두 가지 선택이 있습니다.하나는 kernel32와 user32 및 포크로 설계되지 않은 다른 라이브러리(kernel32 또는 user32에 직간접적으로 링크하는 라이브러리 포함)의 사용을 금지하는 것입니다.즉, Windows 데스크탑과 전혀 상호 작용할 수 없고, 독자적인 Unixy 세계에 갇히게 됩니다.이는 NT용 다양한 Unix 서브시스템에서 채택된 접근법입니다.

또 다른 옵션은 알 수 없는 라이브러리를 fork()로 동작시키기 위해 일종의 끔찍한 해킹에 의지하는 것입니다.그게 사이그윈이 하는 일이야새로운 프로세스를 생성하여 초기화하고(csrss.exe에 등록하는 것을 포함), 오래된 프로세스에서 대부분의 다이내믹스테이트를 복사하여 최선의 처리를 희망합니다.이게 효과가 있다니 놀랍다.확실히 신뢰성 있게 동작하지 않습니다.주소 공간의 경합에 의해 랜덤으로 장해가 발생하지 않는 경우라도, 사용하고 있는 라이브러리는, 모두 고장 상태로 방치되어 있는 경우가 있습니다.Cygwin이 "full-featured fork()"를 가지고 있다는 현재 인정된 답변의 주장은 다음과 같습니다.미심쩍은

요약:.Interix와 같은 환경에서는 fork()를 호출하여 포크를 설정할 수 있습니다.그렇지 않으면 하고 싶은 욕망을 버리세요.Cygwin을 타겟으로 하는 경우에도 반드시 필요한 경우가 아니면 fork()를 사용하지 마십시오.

Windows에, Microsoft는 「Linux」를 「Linux」 for Windows」로 설정했습니다.CreateProcess() 있는 가장 이었습니다.fork() 그 할 실행 가 있습니다.

UNIX 프 unix windows windows 、 Windows 릅릅릅릅릅릅 。fork()call은 기본적으로 각각 독자적인 주소 공간에 있는 현재의 프로세스를 거의 전체적으로 복제하여 개별적으로 계속 실행합니다.프로세스 자체는 다르지만 동일한 프로그램을 실행하고 있습니다.자세한 내용은 여기를 참조하십시오.fork/execmodel.model.model.models

반대로 말하면, Windows와 동등합니다.CreateProcess()는 는 입니다.fork()/exec() UNIX의 함수 쌍.

소프트웨어를 Windows로 이식하고 번역 레이어를 사용해도 상관없는 경우 Cygwin은 원하는 기능을 제공했지만 상당히 엉성했습니다.

물론 새로운 Linux 서브시스템에서는 Windows가 가장 필요로 하는 것은fork()사실 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;
}

최적의 옵션은 Create Process() 또는 Create Thread()입니다.포팅에 대한 자세한 내용은 여기를 참조하십시오.

"파일 액세스 또는 인쇄를 원하는 즉시 I/O가 거부됩니다."

  • 케익도 먹을 수 없고...msvcrt.dll에서는 printf()는 콘솔 API를 기반으로 합니다.이 API 자체는 콘솔서브시스템(csrss.exe)과의 통신에 lpc를 사용합니다.csrss와의 연결은 프로세스 시작 시 시작됩니다.즉, "중간"에서 실행을 시작하는 모든 프로세스는 해당 단계를 건너뜁니다.오퍼레이팅시스템의 소스 코드에 액세스 할 수 없는 한 수동으로 csrss에 접속하려고 해도 의미가 없습니다.대신 자체 서브시스템을 생성하여 fork()를 사용하는 응용 프로그램에서 콘솔 기능을 사용하지 않도록 해야 합니다.

  • 독자적인 서브시스템을 실장하면, 자프로세스를 위해서, 부모 핸들도 모두 복제하는 것을 잊지 말아 주세요.-)

"또한 커널 모드가 아니면 Zw* 함수를 사용하지 않는 것이 좋습니다.대신 Nt* 함수를 사용하는 것이 좋습니다."

  • 틀렸습니다.사용자 모드로 액세스 할 경우 Zw**** Nt*** 와는 전혀 차이가 없습니다.이것들은, 같은(상대적인) 가상 주소를 참조하는, 2개의 다른(ntdll.dll) export명에 지나지 않습니다.

ZwGetContextThread(NtCurrent)스레드(), &context(콘텍스트);

  • 는 ZwGetContextThread를 호출하여 현재 (실행 중인) 스레드의 컨텍스트를 취득하는 것이 잘못되어 크래시가 발생할 가능성이 높으며 (추가 시스템콜로 인해) 작업을 수행하는 가장 빠른 방법은 아닙니다.

Windows 에서는, fork() 를 에뮬레이트 하는 간단한 방법은 없습니다.

대신 실을 사용하는 것이 좋습니다.

fork() 시멘틱스는 인스턴트 fork()가 호출되었을 때 자녀가 부모의 실제 메모리 상태에 액세스할 필요가 있는 경우에 필요합니다.즉석 포크()가 호출되었을 때 메모리 복사의 암묵적인 뮤텍스에 의존하는 소프트웨어를 가지고 있기 때문에 스레드를 사용할 수 없습니다.(이는 copy-on-write/update-memory-table 시멘틱스를 통해 최신 *nix 플랫폼에서 에뮬레이트됩니다).

Windows 에 시스템으로서 존재하는 가장 가까운 것은 Create Process 입니다.가장 좋은 방법은 부모가 새 프로세스의 메모리 공간에 메모리를 복사하는 동안 다른 모든 스레드를 동결했다가 해동하는 것입니다.Cygwin frok [ sic ]클래스도, 에릭 데 쿠르티스가 올린 Scilab 코드도, 제가 볼 수 있는 것처럼, 스레드 프리즈는 하지 않습니다.

또한 커널 모드가 아니면 Zw* 함수를 사용하지 않고 Nt* 함수를 사용해야 합니다.커널 모드인지 아닌지를 체크하고 그렇지 않은 경우 Nt*가 항상 하는 모든 경계 체크와 파라미터 검증을 실행하는 추가 브런치가 있습니다.따라서 사용자 모드에서 호출하는 것이 매우 효율적이지 않습니다.

UNIX 에서 Win32 로의 코드 이식 방법에 대해서는, https://msdn.microsoft.com/en-us/library/y23kc048.aspx 를 참조해 주세요.

특히 프로세스 모델이 두 시스템 간에 상당히 다르다는 것을 나타내며 fork()와 같은 동작이 필요한 CreateProcess와 CreateThread를 고려할 것을 권장합니다.

서브프로세스를 생성하여 기다리는 것만 신경 쓴다면 _spawn* API in process.h로 충분합니다.자세한 내용은 다음과 같습니다.

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

당신이 말한 것 중 가장 가까운 것은...생각해볼게...이것은 fork()이어야 합니다.

상세한 것에 대하여는, 「Interix 에서는 fork() 를 실장하고 있는가」를 참조해 주세요.

대부분의 진부한 해결책들은 구식이다.Winnie the fuzzer는 현재 버전의 Windows 10에서 작동하는 포크 버전을 가지고 있습니다(따라서 시스템 고유의 오프셋이 필요하며 쉽게 고장날 수도 있습니다).

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

언급URL : https://stackoverflow.com/questions/985281/what-is-the-closest-thing-windows-has-to-fork

반응형