อะไรคือสิ่งที่ใกล้เคียงที่สุดที่ Windows ต้องแยก ()?


124

ฉันเดาว่าคำถามพูดได้ทั้งหมด

ฉันต้องการแยกบน Windows การทำงานที่คล้ายกันมากที่สุดคืออะไรและฉันจะใช้มันอย่างไร

คำตอบ:


86

Cygwinมี fork () ที่โดดเด่นบน Windows ดังนั้นหากคุณยอมรับการใช้ Cygwin ปัญหาจะได้รับการแก้ไขในประสิทธิภาพของเคสไม่ใช่ปัญหา

มิฉะนั้นคุณสามารถดูวิธีที่ Cygwin ใช้ fork () จากเอกสารสถาปัตยกรรมของ Cygwin ที่ค่อนข้างเก่า:

5.6 การสร้างกระบวนการการเรียก Fork ใน Cygwin นั้นน่าสนใจเป็นอย่างยิ่งเพราะมันไม่ได้แมปที่ด้านบนของ Win32 API ทำให้ยากมากที่จะนำไปใช้อย่างถูกต้อง ปัจจุบันส้อม Cygwin เป็นการนำไปใช้งานแบบไม่คัดลอกเมื่อเขียนคล้ายกับสิ่งที่มีอยู่ใน UNIX ในช่วงแรก ๆ

สิ่งแรกที่เกิดขึ้นเมื่อกระบวนการหลักแยกกระบวนการลูกคือพาเรนต์เริ่มต้นช่องว่างในตารางกระบวนการ Cygwin สำหรับเด็ก จากนั้นจะสร้างกระบวนการลูกที่ถูกระงับโดยใช้การเรียก Win32 CreateProcess จากนั้นกระบวนการหลักจะเรียกใช้ setjmp เพื่อบันทึกบริบทของตนเองและตั้งค่าตัวชี้ไปที่สิ่งนี้ในพื้นที่หน่วยความจำแบบแบ่งใช้ของ Cygwin (แบ่งใช้ระหว่างงาน Cygwin ทั้งหมด) จากนั้นกรอกข้อมูลในส่วน. data และ. bss ของเด็กโดยคัดลอกจากช่องที่อยู่ของตัวเองลงในช่องที่อยู่ของเด็กที่ถูกระงับ หลังจากเริ่มต้นพื้นที่แอดเดรสของเด็กแล้วเด็กจะถูกเรียกใช้ในขณะที่ผู้ปกครองรอใน mutex เด็กพบว่ามันถูกส้อมและกระโดดไกลโดยใช้บัฟเฟอร์กระโดดที่บันทึกไว้ จากนั้นเด็กจะตั้งค่า mutex ที่พาเรนต์รออยู่และบล็อกบน mutex อื่น นี่คือสัญญาณสำหรับผู้ปกครองในการคัดลอกสแต็กและฮีพลงในลูกหลังจากนั้นจะปล่อย mutex ที่เด็กรออยู่และกลับมาจากการเรียกส้อม ในที่สุดเด็กก็ตื่นจากการบล็อกบน mutex สุดท้ายสร้างพื้นที่ที่แมปหน่วยความจำส่งผ่านไปยังพื้นที่ที่ใช้ร่วมกันอีกครั้งและกลับมาจากทางแยก

ในขณะที่เรามีแนวคิดบางอย่างเกี่ยวกับวิธีเพิ่มความเร็วในการใช้งาน fork ของเราโดยการลดจำนวนการสลับบริบทระหว่างกระบวนการพาเรนต์และโปรเซสลูกส้อมมักจะไม่มีประสิทธิภาพภายใต้ Win32 โชคดีที่ในสถานการณ์ส่วนใหญ่ตระกูลการโทรของ Cygwin สามารถใช้แทนคู่ส้อม / Exec ได้ด้วยความพยายามเพียงเล็กน้อย การเรียกเหล่านี้จะแมปที่ด้านบนของ Win32 API เป็นผลให้มีประสิทธิภาพมากขึ้น การเปลี่ยนโปรแกรมไดรเวอร์ของคอมไพเลอร์เพื่อเรียก spawn แทน fork เป็นการเปลี่ยนแปลงเล็กน้อยและเพิ่มความเร็วในการรวบรวมขึ้นยี่สิบถึงสามสิบเปอร์เซ็นต์ในการทดสอบของเรา

อย่างไรก็ตามการวางไข่และผู้บริหารนำเสนอชุดความยากลำบากของตนเอง เนื่องจากไม่มีวิธีดำเนินการ exec จริงภายใต้ Win32 Cygwin จึงต้องคิดค้น Process IDs (PIDs) ของตัวเอง ด้วยเหตุนี้เมื่อกระบวนการดำเนินการเรียกผู้บริหารหลายคนจะมี Windows PID หลายตัวที่เชื่อมโยงกับ Cygwin PID เดียว ในบางกรณีต้นขั้วของแต่ละกระบวนการ Win32 เหล่านี้อาจค้างอยู่รอให้กระบวนการ Cygwin ของผู้บริหารออกจากระบบ

ฟังดูเหมือนงานเยอะใช่ไหม และใช่มันเป็น slooooow

แก้ไข: เอกสารล้าสมัยโปรดดูคำตอบที่ยอดเยี่ยมสำหรับการอัปเดต


11
นี่เป็นคำตอบที่ดีหากคุณต้องการเขียนแอป Cygwin บน windows แต่โดยทั่วไปแล้วมันไม่ใช่สิ่งที่ดีที่สุดที่ควรทำ โดยพื้นฐานแล้วกระบวนการ * nix และ Windows และแบบจำลองเธรดนั้นแตกต่างกันมาก CreateProcess () และ CreateThread () เป็น API ที่เทียบเท่ากันโดยทั่วไป
Foredecker

2
นักพัฒนาควรจำไว้ว่านี่เป็นกลไกที่ไม่รองรับและ IIRC มีแนวโน้มที่จะหยุดทำงานเมื่อใดก็ตามที่กระบวนการอื่น ๆ ในระบบใช้การแทรกโค้ด
Harry Johnston

1
ลิงก์การใช้งานอื่นใช้ไม่ได้อีกต่อไป
PythonNut

แก้ไขให้เหลือลิงค์คำตอบอื่นเท่านั้น
Laurynas Biveinis

@Foredecker จริงๆแล้วคุณไม่ควรทำแม้ว่าคุณจะพยายามเขียน "แอป cygwin" ก็ตาม มันพยายามเลียนแบบ Unix forkแต่มันก็ทำได้ด้วยวิธีแก้ปัญหารั่วและคุณต้องเตรียมพร้อมสำหรับสถานการณ์ที่ไม่คาดคิด
Pacerier

66

ฉันไม่รู้รายละเอียดเกี่ยวกับเรื่องนี้อย่างแน่นอนเพราะฉันไม่เคยทำมาก่อน แต่ NT API ดั้งเดิมมีความสามารถในการแยกกระบวนการ (ระบบย่อย POSIX บน Windows ต้องการความสามารถนี้ - ฉันไม่แน่ใจว่าระบบย่อย POSIX ได้รับการสนับสนุนอีกต่อไป)

การค้นหา ZwCreateProcess () ควรให้รายละเอียดเพิ่มเติมแก่คุณตัวอย่างเช่นข้อมูลเล็กน้อยจาก Maxim Shatskih :

พารามิเตอร์ที่สำคัญที่สุดคือ SectionHandle หากพารามิเตอร์นี้เป็น NULL เคอร์เนลจะแยกกระบวนการปัจจุบัน มิฉะนั้นพารามิเตอร์นี้จะต้องเป็นหมายเลขอ้างอิงของวัตถุส่วน SEC_IMAGE ที่สร้างบนไฟล์ EXE ก่อนที่จะเรียก ZwCreateProcess ()

แม้ว่าจะทราบว่า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 เพื่อเลียนแบบ fork วิธีการเปลี่ยนไปอย่างเห็นได้ชัด โดยเฉพาะอย่างยิ่งเราจะไม่สร้างกระบวนการย่อยในสถานะที่ถูกระงับอีกต่อไปเว้นแต่ว่าข้อมูลเฉพาะจะต้องมีการจัดการพิเศษในพาเรนต์ก่อนที่จะคัดลอกไปยังลูก ใน 1.5.25 รีลีสปัจจุบันกรณีเดียวสำหรับเด็กที่ถูกระงับคือซ็อกเก็ตแบบเปิดในพาเรนต์ 1.7.0 ที่กำลังจะมาถึงจะไม่ถูกระงับเลย

เหตุผลหนึ่งที่จะไม่ใช้ ZwCreateProcess คือถึงรุ่น 1.5.25 เรายังคงรองรับผู้ใช้ Windows 9x อย่างไรก็ตามความพยายามสองครั้งในการใช้ ZwCreateProcess บนระบบที่ใช้ NT ล้มเหลวด้วยเหตุผลใดเหตุผลหนึ่ง

มันจะดีมากถ้าสิ่งนี้จะดีกว่าหรือได้รับการจัดทำเป็นเอกสารโดยเฉพาะโครงสร้างข้อมูลสองสามอย่างและวิธีเชื่อมต่อกระบวนการกับระบบย่อย แม้ว่า fork ไม่ใช่แนวคิดของ Win32 แต่ฉันก็ไม่เห็นว่ามันจะเป็นเรื่องเลวร้ายที่จะทำให้ fork ง่ายขึ้น


นี่คือคำตอบที่ผิด CreateProcess () และ CreateThread () เป็นสิ่งที่เทียบเท่ากันทั่วไป
Foredecker

2
Interix พร้อมใช้งานใน Windows Vista Enterprise / Ultimate ในชื่อ "Subsystem for UNIX Applications": en.wikipedia.org/wiki/Interix
bk1e

15
@Foredecker - นี่อาจเป็นคำตอบที่ผิด แต่ CreateProcess () / CreateThread () ก็อาจจะผิดได้เช่นกัน ขึ้นอยู่กับว่าใครกำลังมองหา 'วิธี Win32 ในการทำสิ่งต่างๆ' หรือ 'ใกล้เคียงกับ fork () ความหมายมากที่สุด' CreateProcess () ทำงานแตกต่างจาก fork () อย่างมีนัยสำคัญซึ่งเป็นเหตุผลที่ cygwin จำเป็นต้องทำงานจำนวนมากเพื่อสนับสนุน
Michael Burr

1
@jon: ฉันได้พยายามแก้ไขลิงก์และคัดลอกข้อความที่เกี่ยวข้องลงในคำตอบแล้ว (ดังนั้นลิงก์เสียในอนาคตจึงไม่ใช่ปัญหา) อย่างไรก็ตามคำตอบนี้มาจากเมื่อนานมาแล้วว่าฉันไม่แน่ใจ 100% ว่าคำพูดที่ฉันพบในวันนี้คือสิ่งที่ฉันอ้างถึงในปี 2009
Michael Burr

4
หากผู้คนต้องการ " forkทันทีexec" บางที CreateProcess อาจเป็นผู้สมัคร แต่forkโดยไม่ต้องมักจะเป็นที่พึงประสงค์และนี่คือสิ่งที่คนขับรถคนที่จะขอให้จริงexec fork
Aaron McDaid

37

หน้าต่างไม่ค่อยมีอะไรเหมือนกัน โดยเฉพาะอย่างยิ่งเนื่องจากสามารถใช้ fork เพื่อสร้างเธรดหรือกระบวนการในแนวความคิดใน * nix

ดังนั้นฉันต้องพูดว่า:

CreateProcess()/CreateProcessEx()

และ

CreateThread()(ฉันเคยได้ยินมาว่าสำหรับแอปพลิเคชัน C _beginthreadex()จะดีกว่า)


17

มีคนพยายามใช้ 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'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;
}

4
โปรดทราบว่าการตรวจสอบข้อผิดพลาดส่วนใหญ่ขาดหายไปเช่น ZwCreateThread ส่งคืนค่า NTSTATUS ซึ่งสามารถตรวจสอบได้โดยใช้มาโคร SUCCEEDED และ FAILED
BCran

1
จะเกิดอะไรขึ้นหากforkเกิดข้อขัดข้องโปรแกรมขัดข้องหรือเธรดล่มหรือไม่ หากโปรแกรมขัดข้องแสดงว่านี่ไม่ใช่การฟอร์คจริงๆ แค่อยากรู้เพราะฉันกำลังมองหาวิธีแก้ปัญหาที่แท้จริงและหวังว่านี่อาจเป็นทางเลือกที่ดี
leetNightshade

1
ฉันต้องการทราบว่ามีข้อบกพร่องในรหัสที่ให้มา haveLoadedFunctionsForFork เป็นฟังก์ชันส่วนกลางในส่วนหัว แต่เป็นฟังก์ชันคงที่ในไฟล์ c ทั้งคู่ควรเป็นระดับโลก และขณะนี้ส้อมขัดข้องเพิ่มการตรวจสอบข้อผิดพลาดในขณะนี้
leetNightshade

ไซต์นี้ตายไปแล้วและฉันไม่รู้ว่าจะรวบรวมตัวอย่างในระบบของตัวเองได้อย่างไร ฉันคิดว่าฉันขาดส่วนหัวบางส่วนหรือรวมถึงส่วนหัวที่ไม่ถูกต้องฉัน? (ตัวอย่างไม่ได้แสดง)
Paul Stelian

6

ก่อนที่ Microsoft จะแนะนำตัวเลือก "ระบบย่อย Linux สำหรับ Windows" ใหม่CreateProcess()เป็นสิ่งที่ใกล้เคียงที่สุดที่ Windows ต้องทำfork()แต่ Windows ต้องการให้คุณระบุไฟล์ปฏิบัติการเพื่อเรียกใช้ในกระบวนการนั้น

การสร้างกระบวนการ UNIX แตกต่างจาก Windows ค่อนข้างมาก fork()โดยพื้นฐานแล้วการเรียกของมันจะทำซ้ำกระบวนการปัจจุบันเกือบทั้งหมดโดยแต่ละอย่างจะอยู่ในพื้นที่ที่อยู่ของตัวเองและยังคงเรียกใช้งานแยกกัน แม้ว่ากระบวนการจะแตกต่างกัน แต่ก็ยังรันโปรแกรมเดียวกัน ดูที่นี่สำหรับภาพรวมที่ดีของfork/execรุ่น

ย้อนกลับไปทางอื่นสิ่งที่เทียบเท่ากับ Windows CreateProcess()คือfork()/exec() คู่ของฟังก์ชันใน UNIX

หากคุณกำลังพอร์ตซอฟต์แวร์ไปที่ Windows และคุณไม่สนใจเลเยอร์การแปล Cygwin ก็ให้ความสามารถตามที่คุณต้องการ แต่มันค่อนข้างไม่ดี

แน่นอนกับใหม่ระบบย่อยลินุกซ์ , สิ่งที่ใกล้เคียง Windows มีจะfork()เป็นจริง fork() :-)


2
ดังนั้นเมื่อกำหนด WSL ฉันสามารถใช้forkในแอปพลิเคชันที่ไม่ใช่ WSL โดยเฉลี่ยได้หรือไม่?
Caesar

6

เอกสารต่อไปนี้ให้ข้อมูลบางส่วนเกี่ยวกับการย้ายรหัสจาก UNIX ไปยัง Win32: https://msdn.microsoft.com/en-us/library/y23kc048.aspx

เหนือสิ่งอื่นใดมันบ่งชี้ว่ารูปแบบกระบวนการค่อนข้างแตกต่างกันระหว่างทั้งสองระบบและแนะนำให้พิจารณา CreateProcess และ CreateThread ซึ่งจำเป็นต้องมีลักษณะการทำงานเหมือน fork ()


4

"ทันทีที่คุณต้องการเข้าถึงไฟล์หรือ printf io จะถูกปฏิเสธ"

  • คุณไม่สามารถมีเค้กของคุณและกินมันได้เช่นกัน ... ใน msvcrt.dll printf () ขึ้นอยู่กับ Console API ซึ่งในตัวเองใช้ lpc เพื่อสื่อสารกับระบบย่อยของคอนโซล (csrss.exe) การเชื่อมต่อกับ csrss เริ่มต้นเมื่อเริ่มต้นกระบวนการซึ่งหมายความว่ากระบวนการใด ๆ ที่เริ่มต้นการดำเนินการ "ตรงกลาง" จะมีการข้ามขั้นตอนนั้น หากคุณไม่สามารถเข้าถึงซอร์สโค้ดของระบบปฏิบัติการได้คุณจะพยายามเชื่อมต่อกับ csrss ด้วยตนเองไม่ได้ คุณควรสร้างระบบย่อยของคุณเองแทนและหลีกเลี่ยงฟังก์ชันคอนโซลในแอปพลิเคชันที่ใช้ fork ()

  • เมื่อคุณติดตั้งระบบย่อยของคุณเองแล้วอย่าลืมทำซ้ำแฮนเดิลทั้งหมดของผู้ปกครองสำหรับกระบวนการย่อย ;-)

"นอกจากนี้คุณอาจไม่ควรใช้ฟังก์ชัน Zw * เว้นแต่คุณจะอยู่ในโหมดเคอร์เนลคุณควรใช้ฟังก์ชัน Nt * แทน"

  • สิ่งนี้ไม่ถูกต้อง เมื่อเข้าถึงในโหมดผู้ใช้จะไม่มีความแตกต่างระหว่าง Zw *** Nt *** นี่เป็นเพียงสองชื่อที่ส่งออก (ntdll.dll) ที่แตกต่างกันซึ่งอ้างถึงที่อยู่เสมือน (สัมพัทธ์) เดียวกัน

ZwGetContextThread (NtCurrentThread () & บริบท);

  • การรับบริบทของเธรดปัจจุบัน (กำลังทำงาน) โดยการเรียก ZwGetContextThread ผิดมีแนวโน้มที่จะเกิดข้อผิดพลาดและ (เนื่องจากการเรียกระบบเพิ่มเติม) ไม่ใช่วิธีที่เร็วที่สุดในการทำงานให้สำเร็จ

2
ดูเหมือนจะไม่ตอบคำถามหลัก แต่เป็นการตอบกลับคำตอบอื่น ๆ สองสามคำและน่าจะเป็นการดีกว่าที่จะตอบกลับโดยตรงเพื่อความชัดเจนและเพื่อให้ง่ายต่อการติดตามสิ่งที่เกิดขึ้น
Leigh

ดูเหมือนว่าคุณจะคิดว่า printf เขียนลงคอนโซลเสมอ
Jasen

3

ไม่มีวิธีง่ายๆในการเลียนแบบ fork () บน Windows

ฉันขอแนะนำให้คุณใช้เธรดแทน


ดีในความเป็นธรรมการดำเนินการforkเป็นว่าสิ่งที่ Cygwin ได้ แต่ถ้าคุณเคยอ่านเกี่ยวกับวิธีการที่พวกเขาทำหรือ "ไม่ง่ายเลย" ถือเป็นการแสดงความเข้าใจผิดอย่างร้ายแรง :-)
paxdiablo

3

จำเป็นต้องมีความหมาย fork () ที่เด็กต้องการเข้าถึงสถานะหน่วยความจำจริงของพาเรนต์เมื่อมีการเรียกใช้ส้อม () ทันที ฉันมีซอฟต์แวร์ชิ้นหนึ่งซึ่งอาศัย mutex โดยนัยของการคัดลอกหน่วยความจำตามที่เรียกว่า instant fork () ซึ่งทำให้เธรดไม่สามารถใช้งานได้ (สิ่งนี้ถูกจำลองบนแพลตฟอร์ม * nix ที่ทันสมัยผ่านความหมายของตารางคัดลอกเมื่อเขียน / อัปเดตหน่วยความจำ)

สิ่งที่ใกล้เคียงที่สุดที่มีอยู่ใน Windows ในฐานะ syscall คือ CreateProcess วิธีที่ดีที่สุดที่สามารถทำได้คือให้พาเรนต์ตรึงเธรดอื่น ๆ ทั้งหมดในช่วงเวลาที่กำลังคัดลอกหน่วยความจำไปยังพื้นที่หน่วยความจำของกระบวนการใหม่จากนั้นจึงละลาย ทั้งคลาส Cygwin frok [sic] และรหัส Scilab ที่ Eric des Courtis โพสต์ไม่ได้ทำให้เธรดหยุดนิ่งอย่างที่ฉันเห็น

นอกจากนี้คุณอาจไม่ควรใช้ฟังก์ชัน Zw * เว้นแต่คุณจะอยู่ในโหมดเคอร์เนลคุณควรใช้ฟังก์ชัน Nt * แทน มีสาขาพิเศษที่ตรวจสอบว่าคุณอยู่ในโหมดเคอร์เนลหรือไม่และหากไม่ดำเนินการตรวจสอบขอบเขตทั้งหมดและการตรวจสอบพารามิเตอร์ที่ Nt * ทำอยู่เสมอ ดังนั้นจึงมีประสิทธิภาพน้อยกว่าเล็กน้อยในการเรียกใช้จากโหมดผู้ใช้


ข้อมูลที่น่าสนใจมากเกี่ยวกับสัญลักษณ์ที่ส่งออก Zw * ขอขอบคุณ
Andon M. Coleman

โปรดทราบว่าฟังก์ชัน Zw * จากพื้นที่ผู้ใช้ยังคงแมปกับฟังก์ชัน Nt * ในพื้นที่เคอร์เนลเพื่อความปลอดภัย หรืออย่างน้อยก็ควร
Paul Stelian

3

ดังที่คำตอบอื่น ๆ ได้กล่าวไว้ NT (เคอร์เนลที่อยู่ภายใต้ Windows รุ่นใหม่ล่าสุด) มีคุณสมบัติเทียบเท่า Unix fork () นั่นไม่ใช่ปัญหา

ปัญหาคือการโคลนสถานะทั้งหมดของกระบวนการโดยทั่วไปไม่ใช่เรื่องที่ควรทำ นี่เป็นเรื่องจริงในโลก Unix เช่นเดียวกับใน Windows แต่ในโลก Unix จะใช้ fork () ตลอดเวลาและไลบรารีได้รับการออกแบบมาเพื่อจัดการกับมัน ไลบรารี Windows ไม่ได้

ตัวอย่างเช่นระบบ DLLs kernel32.dll และ user32.dll รักษาการเชื่อมต่อส่วนตัวกับกระบวนการเซิร์ฟเวอร์ Win32 csrss.exe หลังจากส้อมมีสองกระบวนการที่ไคลเอนต์สิ้นสุดของการเชื่อมต่อนั้นซึ่งจะทำให้เกิดปัญหา กระบวนการลูกควรแจ้ง csrss.exe ถึงการมีอยู่ของมันและทำการเชื่อมต่อใหม่ - แต่ไม่มีอินเทอร์เฟซสำหรับทำเช่นนั้นเนื่องจากไลบรารีเหล่านี้ไม่ได้ออกแบบมาโดยคำนึงถึง fork ()

คุณจึงมีสองทางเลือก ประการแรกคือห้ามใช้ kernel32 และ user32 และไลบรารีอื่น ๆ ที่ไม่ได้ออกแบบมาเพื่อแยกรวมถึงไลบรารีใด ๆ ที่เชื่อมโยงโดยตรงหรือโดยอ้อมไปยัง kernel32 หรือ user32 ซึ่งแทบทั้งหมด ซึ่งหมายความว่าคุณไม่สามารถโต้ตอบกับเดสก์ท็อป Windows ได้เลยและติดอยู่ในโลก Unixy ที่แยกจากกันของคุณเอง นี่คือแนวทางที่ดำเนินการโดยระบบย่อย Unix สำหรับ NT ต่างๆ

อีกทางเลือกหนึ่งคือหันไปใช้การแฮ็กที่น่ากลัวบางอย่างเพื่อพยายามทำให้ไลบรารีที่ไม่รู้จักทำงานร่วมกับ fork () นั่นคือสิ่งที่ Cygwin ทำ สร้างกระบวนการใหม่ให้เริ่มต้น (รวมถึงการลงทะเบียนตัวเองด้วย csrss.exe) จากนั้นคัดลอกสถานะไดนามิกส่วนใหญ่จากกระบวนการเก่าและหวังว่าจะได้สิ่งที่ดีที่สุด มัน amazes ฉันว่านี้เคยทำงาน แน่นอนว่ามันใช้งานไม่ได้อย่างน่าเชื่อถือแม้ว่ามันจะไม่ล้มเหลวแบบสุ่มเนื่องจากความขัดแย้งของพื้นที่แอดเดรสไลบรารีใด ๆ ที่คุณใช้อยู่อาจถูกทิ้งไว้ในสถานะเสีย คำกล่าวอ้างของคำตอบที่ได้รับการยอมรับในปัจจุบันว่า Cygwin มี "fully-features fork ()" นั้น ... น่าสงสัย

สรุป: ในสภาพแวดล้อมแบบ Interix คุณสามารถแยกโดยการเรียก fork () มิฉะนั้นโปรดพยายามหย่านมตัวเองจากความปรารถนาที่จะทำ แม้ว่าคุณจะกำหนดเป้าหมายไปที่ Cygwin อย่าใช้ fork () เว้นแต่คุณจะต้องทำอย่างแน่นอน



2

ใกล้เคียงที่สุดที่คุณพูด ... ขอฉันคิดว่า ... นี่ต้องเป็นส้อม () ฉันเดา :)

ดูรายละเอียดได้ที่Interix ใช้ fork () หรือไม่


1
ประวัติอันน่าเศร้าของระบบย่อย Microsoft POSIXนั้นค่อนข้างให้ความกระจ่างเกี่ยวกับ Interix
Roman Starkov

1

หากคุณสนใจแค่การสร้างกระบวนการย่อยและรอมันบางที _spawn * API อยู่ระหว่างดำเนินการ h ก็เพียงพอแล้ว นี่คือข้อมูลเพิ่มเติมเกี่ยวกับสิ่งนั้น:

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.