Câu trả lời:
Cygwin có đầy đủ tính năng fork () trên Windows. Do đó, nếu sử dụng Cygwin là chấp nhận được đối với bạn, thì vấn đề được giải quyết trong trường hợp thực hiện không phải là vấn đề.
Nếu không, bạn có thể xem Cygwin thực hiện fork (). Từ một tài liệu kiến trúc khá cũ của Cygwin :
5.6. Tạo quy trình Cuộc gọi rẽ nhánh trong Cygwin đặc biệt thú vị vì nó không ánh xạ tốt trên API Win32. Điều này làm cho nó rất khó để thực hiện chính xác. Hiện tại, ngã ba Cygwin là một triển khai không sao chép trên ghi tương tự như những gì đã có trong các hương vị ban đầu của UNIX.
Điều đầu tiên xảy ra khi một quá trình cha mẹ tạo ra một tiến trình con là cha mẹ khởi tạo một khoảng trắng trong bảng quy trình Cygwin cho con. Sau đó, nó tạo ra một tiến trình con bị treo bằng cách sử dụng lệnh gọi Win32 CreatProcess. Tiếp theo, tiến trình cha gọi setjmp để lưu bối cảnh của chính nó và đặt một con trỏ tới vùng này trong vùng nhớ chia sẻ Cygwin (được chia sẻ giữa tất cả các tác vụ Cygwin). Sau đó, nó điền vào các phần .data và .bss của trẻ bằng cách sao chép từ không gian địa chỉ của chính nó vào không gian địa chỉ của trẻ bị treo. Sau khi không gian địa chỉ của đứa trẻ được khởi tạo, đứa trẻ được chạy trong khi cha mẹ chờ đợi trên một mutex. Đứa trẻ phát hiện ra nó đã bị rẽ nhánh và nhảy dài bằng cách sử dụng bộ đệm nhảy đã lưu. Đứa trẻ sau đó thiết lập mutex mà cha mẹ đang chờ đợi và chặn trên một mutex khác. Đây là tín hiệu cho cha mẹ sao chép ngăn xếp của nó và heap vào đứa trẻ, sau đó nó giải phóng mutex mà đứa trẻ đang chờ đợi và trở về từ cuộc gọi ngã ba. Cuối cùng, đứa trẻ thức dậy khỏi việc chặn trên mutex cuối cùng, tạo lại bất kỳ khu vực được ánh xạ bộ nhớ nào được chuyển đến nó thông qua khu vực được chia sẻ và trở về từ chính ngã ba.
Mặc dù chúng tôi có một số ý tưởng về cách tăng tốc triển khai ngã ba của chúng tôi bằng cách giảm số lượng chuyển đổi ngữ cảnh giữa quy trình cha mẹ và con, gần như chắc chắn sẽ luôn không hiệu quả trong Win32. May mắn thay, trong hầu hết các trường hợp, các cuộc gọi sinh sản do Cygwin cung cấp có thể được thay thế cho một cặp fork / exec chỉ với một chút nỗ lực. Các cuộc gọi này được ánh xạ rõ ràng trên API Win32. Kết quả là, chúng hiệu quả hơn nhiều. Thay đổi chương trình trình điều khiển của trình biên dịch để gọi sinh sản thay vì ngã ba là một thay đổi nhỏ và tăng tốc độ biên dịch lên hai mươi đến ba mươi phần trăm trong các thử nghiệm của chúng tôi.
Tuy nhiên, sinh sản và thực hiện trình bày các khó khăn riêng của họ. Vì không có cách nào để thực hiện một thực thi thực tế theo Win32, Cygwin phải phát minh ra ID tiến trình (PID) của riêng mình. Kết quả là, khi một quá trình thực hiện nhiều lệnh thực thi, sẽ có nhiều Windows PID được liên kết với một Cygwin PID. Trong một số trường hợp, sơ khai của mỗi quy trình Win32 này có thể kéo dài, chờ quá trình Cygwin thực thi của chúng thoát ra.
Nghe có vẻ nhiều việc phải không? Và vâng, nó là slooooow.
EDIT: tài liệu đã lỗi thời, vui lòng xem câu trả lời tuyệt vời này để cập nhật
fork
nhưng nó hoàn thành việc này bằng một giải pháp rò rỉ và bạn phải chuẩn bị cho những tình huống bất ngờ.
Tôi chắc chắn không biết chi tiết về điều này bởi vì tôi chưa bao giờ thực hiện nó, nhưng API NT gốc có khả năng rẽ nhánh một quy trình (hệ thống con POSIX trên Windows cần khả năng này - Tôi không chắc hệ thống con POSIX thậm chí còn được hỗ trợ nữa).
Một tìm kiếm cho ZwCreateProcess () sẽ cung cấp cho bạn một số chi tiết khác - ví dụ như thông tin này từ Maxim Shatskih :
Tham số quan trọng nhất ở đây là PartHandle. Nếu tham số này là NULL, kernel sẽ rẽ nhánh tiến trình hiện tại. Mặt khác, tham số này phải là một điều khiển của đối tượng phần SEC_IMAGE được tạo trên tệp EXE trước khi gọi ZwCreateProcess ().
Mặc dù lưu ý rằng Corinna Vinschen chỉ ra rằng Cygwin được tìm thấy bằng ZwCreateProcess () vẫn không đáng tin cậy :
Iker Arizmendi đã viết:
> 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
Tài liệu này khá cũ, 10 năm hoặc lâu hơn. Mặc dù chúng tôi vẫn đang sử dụng các cuộc gọi Win32 để mô phỏng ngã ba, phương thức này đã thay đổi đáng chú ý. Đặc biệt, chúng tôi không tạo quy trình con ở trạng thái treo nữa, trừ khi các cơ sở dữ liệu cụ thể cần một xử lý đặc biệt ở cha mẹ trước khi chúng được sao chép sang con. Trong bản phát hành 1.5.25 hiện tại, trường hợp duy nhất cho một đứa trẻ bị đình chỉ là các ổ cắm mở trong cha mẹ. Bản phát hành 1.7.0 sắp tới sẽ không đình chỉ chút nào.
Một lý do để không sử dụng ZwCreateProcess là vì bản phát hành 1.5.25 chúng tôi vẫn hỗ trợ người dùng Windows 9x. Tuy nhiên, hai lần thử sử dụng ZwCreateProcess trên các hệ thống dựa trên NT đã thất bại vì lý do này hay lý do khác.
Sẽ thực sự tốt nếu công cụ này sẽ tốt hơn hoặc hoàn toàn được ghi lại, đặc biệt là một vài cơ sở dữ liệu và cách kết nối một quy trình với một hệ thống con. Mặc dù fork không phải là một khái niệm Win32, tôi không thấy rằng nó sẽ là một điều xấu khi làm cho fork dễ thực hiện hơn.
fork
ngay lập tức exec
", thì có lẽ CreatProcess là một ứng cử viên. Nhưng fork
không exec
có thường là mong muốn và đây là những gì trình điều khiển mọi người yêu cầu thực sự fork
.
Chà, windows không thực sự có thứ gì giống nó cả. Đặc biệt là ngã ba có thể được sử dụng để tạo khái niệm một luồng hoặc một quá trình trong * nix.
Vì vậy, tôi phải nói:
CreateProcess()
/CreateProcessEx()
và
CreateThread()
(Tôi đã nghe nói rằng đối với các ứng dụng C, _beginthreadex()
tốt hơn).
Mọi người đã cố gắng thực hiện fork trên Windows. Đây là thứ gần nhất với nó tôi có thể tìm thấy:
Lấy từ: 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;
}
fork
cố, nó bị sập chương trình, hoặc luồng chỉ bị sập? Nếu nó bị hỏng chương trình, thì đây không thực sự là giả mạo. Chỉ tò mò, bởi vì tôi đang tìm kiếm một giải pháp thực sự, và hy vọng đây có thể là một giải pháp thay thế hợp lý.
Trước khi Microsoft giới thiệu tùy chọn "Hệ thống con Linux cho Windows" mới của họ, CreateProcess()
là thứ gần nhất mà Windows phải có fork()
, nhưng Windows yêu cầu bạn chỉ định một tệp thực thi để chạy trong quy trình đó.
Quá trình tạo UNIX khá khác với Windows. fork()
Cuộc gọi của nó về cơ bản sao chép toàn bộ quá trình hiện tại, gần như toàn bộ trong không gian địa chỉ của riêng họ và tiếp tục chạy chúng một cách riêng biệt. Mặc dù các quy trình là khác nhau, nhưng chúng vẫn đang chạy cùng một chương trình. Xem ở đây để có một cái nhìn tổng quan tốt về fork/exec
mô hình.
Quay trở lại theo cách khác, tương đương với Windows CreateProcess()
là fork()/exec()
cặp chức năng trong UNIX.
Nếu bạn đang chuyển phần mềm sang Windows và bạn không bận tâm đến một lớp dịch, Cygwin đã cung cấp khả năng mà bạn muốn nhưng nó khá đơn giản.
Tất nhiên, với hệ thống con Linux mới , thứ gần nhất mà Windows phải thực sựfork()
là :-) fork()
fork
trong một ứng dụng WSL trung bình không?
Tài liệu sau đây cung cấp một số thông tin về chuyển mã từ UNIX sang Win32: https://msdn.microsoft.com/en-us/l Library / y23kc048.aspx
Trong số những thứ khác, nó chỉ ra rằng mô hình quy trình khá khác nhau giữa hai hệ thống và khuyến nghị xem xét về CreatProcess và CreatThread khi yêu cầu hành vi giống như fork ().
"ngay khi bạn muốn truy cập tập tin hoặc printf thì tôi sẽ bị từ chối"
Bạn không thể có bánh của mình và cũng ăn nó ... trong msvcrt.dll, printf () dựa trên API Console, bản thân nó sử dụng lpc để giao tiếp với hệ thống con giao diện điều khiển (csrss.exe). Kết nối với csrss được bắt đầu khi khởi động quá trình, điều đó có nghĩa là bất kỳ quá trình nào bắt đầu thực hiện "ở giữa" sẽ bị bỏ qua bước đó. Trừ khi bạn có quyền truy cập vào mã nguồn của hệ điều hành, thì không có lý do gì để cố gắng kết nối với csrss bằng tay. Thay vào đó, bạn nên tạo hệ thống con của riêng mình và theo đó tránh các chức năng của bàn điều khiển trong các ứng dụng sử dụng fork ().
khi bạn đã triển khai hệ thống con của riêng mình, đừng quên sao chép tất cả các thẻ điều khiển của cha mẹ cho quy trình con ;-)
"Ngoài ra, có lẽ bạn không nên sử dụng các chức năng Zw * trừ khi bạn ở chế độ kernel, có lẽ bạn nên sử dụng các chức năng Nt *."
ZwGetContextThread (NtCienThread (), & bối cảnh);
ngữ nghĩa của fork () là cần thiết khi đứa trẻ cần truy cập vào trạng thái bộ nhớ thực tế của cha mẹ kể từ khi fork () được gọi ngay lập tức. Tôi có một phần mềm dựa trên mutex ẩn của sao chép bộ nhớ kể từ khi fork () được gọi ngay lập tức, khiến cho các luồng không thể sử dụng. (Điều này được mô phỏng trên các nền tảng * nix hiện đại thông qua ngữ nghĩa sao chép trên ghi / cập nhật-bộ nhớ-bảng.)
Gần nhất tồn tại trên Windows dưới dạng một tòa nhà chọc trời là CreatProcess. Điều tốt nhất có thể được thực hiện là cha mẹ đóng băng tất cả các luồng khác trong thời gian nó sao chép bộ nhớ vào không gian bộ nhớ của quy trình mới, sau đó làm tan chúng. Cả lớp Cygwin frok [sic] cũng không phải mã Scilab mà Eric des Courtis đăng tải đều đóng băng, mà tôi có thể thấy.
Ngoài ra, có lẽ bạn không nên sử dụng các chức năng Zw * trừ khi bạn ở chế độ kernel, có lẽ bạn nên sử dụng các chức năng Nt *. Có thêm một nhánh kiểm tra xem bạn có ở chế độ kernel hay không, và nếu không, thực hiện tất cả các giới hạn kiểm tra và xác minh tham số mà Nt * luôn làm. Do đó, việc gọi họ từ chế độ người dùng sẽ rất kém hiệu quả.
Các tùy chọn tốt nhất của bạn là CreatProcess () hoặc CreateThread () . Có nhiều thông tin hơn về porting ở đây .
Không có cách nào dễ dàng để giả lập fork () trên Windows.
Tôi đề nghị bạn sử dụng chủ đề thay thế.
fork
là chính xác những gì Cygwin đã làm. Nhưng, nếu bạn đã từng đọc về cách họ đã làm điều đó, thì "không có cách nào dễ dàng" là một sự hiểu lầm thô thiển :-)
Gần nhất bạn nói ... Hãy để tôi nghĩ ... Đây phải là ngã ba () tôi đoán :)
Để biết chi tiết, xem Interix có thực hiện fork () không?
Như các câu trả lời khác đã đề cập, NT (hạt nhân nằm dưới các phiên bản Windows hiện đại) có tương đương với Unix fork (). Đó không phải là vấn đề.
Vấn đề là nhân bản toàn bộ trạng thái của một quá trình nói chung không phải là một việc lành mạnh để làm. Điều này đúng trong thế giới Unix giống như trong Windows, nhưng trong thế giới Unix, fork () được sử dụng mọi lúc và các thư viện được thiết kế để đối phó với nó. Thư viện Windows không.
Ví dụ: DLL hệ thống kernel32.dll và user32.dll duy trì kết nối riêng với máy chủ Win32 xử lý csrss.exe. Sau một ngã ba, có hai quá trình ở đầu máy khách của kết nối đó, điều này sẽ gây ra vấn đề. Quá trình con nên thông báo cho csrss.exe về sự tồn tại của nó và tạo một kết nối mới - nhưng không có giao diện nào để thực hiện điều đó, vì các thư viện này không được thiết kế với fork ().
Vì vậy, bạn có hai lựa chọn. Một là cấm sử dụng kernel32 và user32 và các thư viện khác không được thiết kế để rẽ nhánh - bao gồm mọi thư viện liên kết trực tiếp hoặc gián tiếp với kernel32 hoặc user32, hầu như là tất cả chúng. Điều này có nghĩa là bạn hoàn toàn không thể tương tác với máy tính để bàn Windows và bị mắc kẹt trong thế giới Unixy riêng của bạn. Đây là cách tiếp cận được thực hiện bởi các hệ thống con Unix khác nhau cho NT.
Tùy chọn khác là sử dụng một số loại hack khủng khiếp để cố gắng để các thư viện không biết làm việc với fork (). Đó là những gì Cygwin làm. Nó tạo ra một quy trình mới, cho phép nó khởi tạo (bao gồm đăng ký chính nó với csrss.exe), sau đó sao chép hầu hết trạng thái động từ quy trình cũ và hy vọng điều tốt nhất. Điều làm tôi ngạc nhiên là điều này từng hoạt động. Nó chắc chắn không hoạt động đáng tin cậy - ngay cả khi nó không thất bại ngẫu nhiên do xung đột không gian địa chỉ, bất kỳ thư viện nào bạn đang sử dụng có thể bị bỏ lại trong tình trạng bị hỏng. Yêu cầu của câu trả lời được chấp nhận hiện tại rằng Cygwin có một "ngã ba đầy đủ tính năng ()" là ... đáng ngờ.
Tóm tắt: Trong môi trường giống như Interix, bạn có thể rẽ nhánh bằng cách gọi fork (). Nếu không, hãy cố gắng tự cai sữa khỏi mong muốn làm điều đó. Ngay cả khi bạn đang nhắm mục tiêu Cygwin, đừng sử dụng fork () trừ khi bạn thực sự phải làm.
Nếu bạn chỉ quan tâm đến việc tạo một quy trình con và chờ đợi nó, có lẽ _spawn * API trong process.h là đủ. Dưới đây là thông tin thêm về điều đó:
https://docs.microsoft.com/en-us/cpp/c-r nb-l Library / process-and-en môi-control https://en.wikipedia.org/wiki/Process.h