Saya kira pertanyaan itu menjelaskan semuanya.
Saya ingin bercabang di Windows. Operasi apa yang paling mirip dan bagaimana cara menggunakannya.
Saya kira pertanyaan itu menjelaskan semuanya.
Saya ingin bercabang di Windows. Operasi apa yang paling mirip dan bagaimana cara menggunakannya.
Jawaban:
Cygwin memiliki garpu berfitur lengkap () di Windows. Jadi jika menggunakan Cygwin dapat diterima untuk Anda, maka masalahnya teratasi jika kinerja kasus tidak menjadi masalah.
Jika tidak, Anda dapat melihat bagaimana Cygwin mengimplementasikan fork (). Dari dokumen arsitektur Cygwin yang cukup tua :
5.6. Proses Pembuatan Panggilan garpu di Cygwin sangat menarik karena tidak dipetakan dengan baik di atas Win32 API. Ini membuatnya sangat sulit untuk diterapkan dengan benar. Saat ini, percabangan Cygwin adalah implementasi non-copy-on-write yang mirip dengan yang ada di versi awal UNIX.
Hal pertama yang terjadi ketika proses orang tua membagi proses anak adalah bahwa orang tua menginisialisasi spasi dalam tabel proses Cygwin untuk anak tersebut. Ini kemudian membuat proses anak ditangguhkan menggunakan panggilan Win32 CreateProcess. Selanjutnya, proses induk memanggil setjmp untuk menyimpan konteksnya sendiri dan menetapkan penunjuk ke ini di area memori bersama Cygwin (dibagi di antara semua tugas Cygwin). Ini kemudian mengisi bagian .data dan .bss anak dengan menyalin dari ruang alamatnya sendiri ke ruang alamat anak yang ditangguhkan. Setelah ruang alamat anak diinisialisasi, anak dijalankan sementara orang tua menunggu di mutex. Anak tersebut menemukan bahwa ia telah bercabang dan longjump menggunakan buffer lompat yang disimpan. Anak itu kemudian menyetel mutex yang ditunggunya oleh induknya dan memblokirnya di mutex lain. Ini adalah sinyal bagi induk untuk menyalin tumpukan dan heap-nya ke dalam anak, setelah itu melepaskan mutex yang ditunggu oleh anak dan kembali dari panggilan fork. Akhirnya, anak tersebut terbangun dari pemblokiran pada mutex terakhir, membuat ulang area yang dipetakan memori yang diteruskan kepadanya melalui area bersama, dan kembali dari fork itu sendiri.
Meskipun kami memiliki beberapa ide tentang bagaimana mempercepat implementasi fork kami dengan mengurangi jumlah pengalih konteks antara proses induk dan anak, fork hampir pasti selalu tidak efisien di bawah Win32. Untungnya, dalam banyak situasi, keluarga panggilan yang disediakan oleh Cygwin dapat diganti dengan pasangan fork / exec dengan hanya sedikit usaha. Panggilan ini dipetakan dengan rapi di atas Win32 API. Hasilnya, mereka jauh lebih efisien. Mengubah program driver kompiler untuk memanggil spawn dan bukan fork adalah perubahan yang sepele dan meningkatkan kecepatan kompilasi sebesar dua puluh hingga tiga puluh persen dalam pengujian kami.
Namun, spawn dan exec menghadirkan kesulitan mereka sendiri. Karena tidak ada cara untuk melakukan eksekusi sebenarnya di bawah Win32, Cygwin harus menciptakan ID Proses (PID) sendiri. Akibatnya, ketika suatu proses melakukan beberapa panggilan exec, akan ada beberapa PID Windows yang terkait dengan satu Cygwin PID. Dalam beberapa kasus, rintisan dari masing-masing proses Win32 ini mungkin tertinggal, menunggu proses Cygwin yang dijalankan mereka untuk keluar.
Kedengarannya banyak pekerjaan, bukan? Dan ya, itu slooooow.
EDIT: dokumen sudah usang, silakan lihat jawaban luar biasa ini untuk pembaruan
fork
namun menyelesaikan ini dengan solusi yang bocor dan Anda harus siap menghadapi situasi yang tidak terduga.
Saya tentu tidak tahu detailnya karena saya belum pernah melakukannya, tetapi NT API asli memiliki kemampuan untuk melakukan fork suatu proses (subsistem POSIX pada Windows membutuhkan kemampuan ini - Saya tidak yakin apakah subsistem POSIX bahkan didukung lagi).
Pencarian untuk ZwCreateProcess () akan memberi Anda beberapa detail lebih lanjut - misalnya sedikit informasi dari Maxim Shatskih ini :
Parameter terpenting di sini adalah SectionHandle. Jika parameter ini NULL, kernel akan memotong proses saat ini. Jika tidak, parameter ini harus menjadi pegangan dari objek bagian SEC_IMAGE yang dibuat pada file EXE sebelum memanggil ZwCreateProcess ().
Meskipun perhatikan bahwa Corinna Vinschen menunjukkan bahwa Cygwin ditemukan menggunakan ZwCreateProcess () masih tidak dapat diandalkan :
Iker Arizmendi menulis:
> 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
Dokumen ini agak tua, 10 tahun atau lebih. Meskipun kami masih menggunakan panggilan Win32 untuk meniru fork, metode ini telah berubah secara signifikan. Terutama, kami tidak lagi membuat proses anak dalam status ditangguhkan, kecuali jika struktur data tertentu memerlukan penanganan khusus di induknya sebelum disalin ke turunan. Dalam rilis 1.5.25 saat ini, satu-satunya kasus untuk anak yang ditangguhkan adalah soket terbuka di induknya. Rilis 1.7.0 mendatang tidak akan ditangguhkan sama sekali.
Salah satu alasan untuk tidak menggunakan ZwCreateProcess adalah hingga rilis 1.5.25 kami masih mendukung pengguna Windows 9x. Namun, dua upaya untuk menggunakan ZwCreateProcess pada sistem berbasis NT gagal karena satu dan lain alasan.
Akan sangat menyenangkan jika hal ini akan lebih baik atau sama sekali didokumentasikan, terutama beberapa struktur data dan bagaimana menghubungkan proses ke subsistem. Meskipun fork bukanlah konsep Win32, saya tidak melihat bahwa akan menjadi hal yang buruk untuk membuat fork lebih mudah diimplementasikan.
fork
dengan segera exec
", maka mungkin CreateProcess adalah kandidatnya. Tetapi fork
tanpa exec
sering diinginkan dan ini adalah apa yang mendorong orang untuk meminta secara nyata fork
.
Nah, jendela sebenarnya tidak memiliki sesuatu yang seperti itu. Terutama karena garpu dapat digunakan secara konseptual untuk membuat utas atau proses di * nix.
Jadi, saya harus mengatakan:
CreateProcess()
/CreateProcessEx()
dan
CreateThread()
(Saya pernah mendengar bahwa untuk aplikasi C, _beginthreadex()
lebih baik).
Orang-orang telah mencoba menerapkan garpu di Windows. Ini adalah hal terdekat yang dapat saya temukan:
Diambil dari: 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
macet, apakah program macet, atau utas macet begitu saja? Jika program membuat crash, maka ini sebenarnya bukan forking. Hanya penasaran, karena saya sedang mencari solusi nyata, dan berharap ini bisa menjadi alternatif yang layak.
Sebelum Microsoft memperkenalkan opsi "Linux subsystem for Windows" mereka yang baru, CreateProcess()
adalah hal yang paling dekat dengan Windows fork()
, tetapi Windows mengharuskan Anda menentukan executable untuk dijalankan dalam proses itu.
Proses pembuatan UNIX sangat berbeda dengan Windows. Its fork()
panggilan pada dasarnya duplikat proses saat ini hampir secara total, masing-masing dalam ruang alamat mereka sendiri, dan terus berjalan secara terpisah. Meskipun prosesnya sendiri berbeda, mereka masih menjalankan program yang sama . Lihat di sini untuk gambaran umum yang baik tentang fork/exec
model tersebut.
Kembali ke arah lain, padanan dari Windows CreateProcess()
adalah fork()/exec()
pasangan fungsi di UNIX.
Jika Anda mem-porting perangkat lunak ke Windows dan Anda tidak keberatan dengan lapisan terjemahan, Cygwin menyediakan kemampuan yang Anda inginkan tetapi itu agak kludgey.
Tentu saja, dengan yang baru subsistem Linux , hal yang paling dekat Windows memiliki untuk fork()
ini benar-benar fork()
:-)
fork
rata-rata aplikasi non-WSL?
Dokumen berikut ini menyediakan beberapa informasi tentang kode port dari UNIX ke Win32: https://msdn.microsoft.com/en-us/library/y23kc048.aspx
Antara lain, ini menunjukkan bahwa model proses sangat berbeda antara kedua sistem dan merekomendasikan pertimbangan CreateProcess dan CreateThread di mana perilaku seperti fork () diperlukan.
"segera setelah Anda ingin mengakses file atau printf maka io ditolak"
Anda tidak dapat memiliki kue dan memakannya juga ... di msvcrt.dll, printf () didasarkan pada API Konsol, yang dengan sendirinya menggunakan lpc untuk berkomunikasi dengan subsistem konsol (csrss.exe). Koneksi dengan csrss dimulai saat proses start-up, yang berarti bahwa setiap proses yang memulai eksekusinya "di tengah" akan melewatkan langkah tersebut. Kecuali Anda memiliki akses ke kode sumber sistem operasi, tidak ada gunanya mencoba menyambung ke csrs secara manual. Sebagai gantinya, Anda harus membuat subsistem Anda sendiri, dan karenanya menghindari fungsi konsol dalam aplikasi yang menggunakan fork ().
setelah Anda mengimplementasikan subsistem Anda sendiri, jangan lupa untuk juga menduplikasi semua pegangan induk untuk proses anak ;-)
"Selain itu, Anda mungkin sebaiknya tidak menggunakan fungsi Zw * kecuali Anda dalam mode kernel, Anda mungkin harus menggunakan fungsi Nt * sebagai gantinya."
ZwGetContextThread (NtCurrentThread (), & konteks);
semantik fork () diperlukan jika anak memerlukan akses ke status memori aktual dari induk saat fork () instan dipanggil. Saya memiliki perangkat lunak yang bergantung pada mutex implisit dari penyalinan memori saat garpu instan () dipanggil, yang membuat utas tidak mungkin digunakan. (Ini diemulasikan pada platform * nix modern melalui semantik copy-on-write / update-memory-table.)
Yang terdekat yang ada di Windows sebagai syscall adalah CreateProcess. Hal terbaik yang dapat dilakukan adalah agar induknya membekukan semua utas lainnya selama ia menyalin memori ke ruang memori proses baru, lalu mencairkannya. Baik kelas Cygwin frok [sic] maupun kode Scilab yang diposting Eric des Courtis tidak melakukan pembekuan thread, yang bisa saya lihat.
Selain itu, Anda mungkin sebaiknya tidak menggunakan fungsi Zw * kecuali Anda dalam mode kernel, Anda mungkin harus menggunakan fungsi Nt * sebagai gantinya. Ada cabang tambahan yang memeriksa apakah Anda dalam mode kernel dan, jika tidak, melakukan semua pemeriksaan batas dan verifikasi parameter yang selalu dilakukan Nt *. Jadi, sangat kurang efisien untuk memanggilnya dari mode pengguna.
Pilihan terbaik Anda adalah CreateProcess () atau CreateThread () . Ada lebih banyak informasi tentang porting di sini .
Tidak ada cara mudah untuk meniru fork () di Windows.
Saya menyarankan Anda untuk menggunakan utas sebagai gantinya.
fork
adalah persis apa yang Cygwin lakukan. Tapi, jika Anda pernah membaca tentang bagaimana mereka melakukannya, "tidak mudah" adalah kesalahpahaman yang besar :-)
Yang paling dekat yang Anda katakan ... Biar saya pikirkan ... Ini pasti garpu () kurasa :)
Untuk detailnya, lihat Apakah Interix mengimplementasikan fork ()?
Seperti jawaban lain yang telah disebutkan, NT (kernel yang mendasari versi modern Windows) memiliki persamaan dengan Unix fork (). Bukan itu masalahnya.
Masalahnya adalah bahwa mengkloning seluruh status proses secara umum bukanlah hal yang waras untuk dilakukan. Ini sama benarnya di dunia Unix seperti di Windows, tetapi di dunia Unix, fork () digunakan sepanjang waktu, dan perpustakaan dirancang untuk menghadapinya. Perpustakaan Windows tidak.
Misalnya, sistem DLL kernel32.dll dan user32.dll mempertahankan sambungan pribadi ke proses server Win32 csrss.exe. Setelah bercabang, ada dua proses di ujung klien dari koneksi tersebut, yang akan menyebabkan masalah. Proses anak harus memberi tahu csrss.exe keberadaannya dan membuat koneksi baru - tetapi tidak ada antarmuka untuk melakukannya, karena pustaka ini tidak dirancang dengan fork ().
Jadi Anda punya dua pilihan. Salah satunya adalah melarang penggunaan kernel32 dan user32 dan pustaka lain yang tidak dirancang untuk bercabang - termasuk pustaka apa pun yang menautkan secara langsung atau tidak langsung ke kernel32 atau user32, yang hampir semuanya. Ini berarti Anda tidak dapat berinteraksi dengan desktop Windows sama sekali, dan terjebak di dunia Unixy Anda yang terpisah. Ini adalah pendekatan yang diambil oleh berbagai subsistem Unix untuk NT.
Opsi lainnya adalah menggunakan semacam peretasan yang mengerikan untuk mencoba membuat perpustakaan yang tidak sadar bekerja dengan fork (). Itulah yang dilakukan Cygwin. Ini menciptakan proses baru, memungkinkannya menginisialisasi (termasuk mendaftarkan dirinya dengan csrss.exe), lalu menyalin sebagian besar status dinamis dari proses lama dan berharap yang terbaik. Sungguh mengherankan saya bahwa ini pernah berhasil. Itu pasti tidak bekerja dengan andal - bahkan jika tidak gagal secara acak karena konflik ruang alamat, perpustakaan apa pun yang Anda gunakan mungkin diam-diam dibiarkan dalam keadaan rusak. Klaim jawaban yang diterima saat ini bahwa Cygwin memiliki "garpu berfitur lengkap ()" adalah ... meragukan.
Ringkasan: Dalam lingkungan seperti Interix, Anda bisa bercabang dengan memanggil fork (). Jika tidak, cobalah untuk melepaskan diri Anda dari keinginan untuk melakukannya. Bahkan jika Anda menargetkan Cygwin, jangan gunakan fork () kecuali Anda benar-benar harus melakukannya.
Jika Anda hanya peduli tentang membuat subproses dan menunggunya, mungkin _spawn * API dalam proses.h sudah cukup. Berikut informasi lebih lanjut tentang itu:
https://docs.microsoft.com/en-us/cpp/c-runtime-library/process-and-environment-control https://en.wikipedia.org/wiki/Process.h