Yanıtlar:
Cygwin , Windows'ta tam özellikli fork () özelliğine sahiptir. Dolayısıyla, Cygwin'i kullanmak sizin için kabul edilebilirse, o zaman sorun performansın bir sorun olmadığı durumda çözülür.
Aksi takdirde Cygwin'in fork () 'u nasıl uyguladığına bakabilirsiniz. Oldukça eski bir Cygwin'in mimarlık doktorundan :
5.6. Süreç Oluşturma Cygwin'deki çatal çağrısı özellikle ilgi çekicidir çünkü Win32 API'sinin üstünde iyi bir şekilde eşleşmez. Bu, doğru bir şekilde uygulanmasını çok zorlaştırır. Şu anda, Cygwin çatalı, UNIX'in ilk çeşitlerinde mevcut olana benzer, yazma üzerinde kopyalama olmayan bir uygulamadır.
Bir üst süreç bir çocuk süreci çatalladığında gerçekleşen ilk şey, ebeveynin çocuk için Cygwin süreç tablosunda bir alan başlatmasıdır. Daha sonra Win32 CreateProcess çağrısını kullanarak askıya alınmış bir alt süreç oluşturur. Daha sonra, ana süreç setjmp'yi kendi bağlamını kaydetmek için çağırır ve Cygwin paylaşılan hafıza alanında (tüm Cygwin görevleri arasında paylaşılır) buna bir işaretçi ayarlar. Ardından, kendi adres alanından askıya alınan çocuğun adres alanına kopyalayarak çocuğun .data ve .bss bölümlerini doldurur. Çocuğun adres alanı başlatıldıktan sonra, ebeveyn bir muteks üzerinde beklerken çocuk çalıştırılır. Çocuk, kaydedilmiş atlama arabelleğini kullanarak çatallanmış ve uzun atlamalar olduğunu keşfeder. Çocuk daha sonra ebeveynin beklediği muteksi ayarlar ve başka bir muteksi engeller. Bu, ebeveynin kendi yığınını ve yığınını alt öğeye kopyalaması ve ardından çocuğun beklediği muteksi serbest bırakması ve fork çağrısından geri dönmesi için sinyaldir. Son olarak, çocuk son mutekste engellemeden uyanır, paylaşılan alan aracılığıyla kendisine geçen bellek eşlemeli alanları yeniden oluşturur ve çatalın kendisinden geri döner.
Üst ve alt süreç arasındaki bağlam geçişlerinin sayısını azaltarak çatal uygulamamızı nasıl hızlandıracağımız konusunda bazı fikirlerimiz olsa da, fork neredeyse her zaman Win32 altında verimsiz olacaktır. Neyse ki, çoğu durumda Cygwin tarafından sağlanan spawn çağrı ailesi çok az bir çabayla fork / exec çifti ile değiştirilebilir. Bu çağrılar, Win32 API'sinin üzerinde temiz bir şekilde eşlenir. Sonuç olarak, çok daha verimlidirler. Derleyicinin sürücü programını fork yerine spawn çağıracak şekilde değiştirmek önemsiz bir değişiklikti ve testlerimizde derleme hızlarını yüzde yirmi ila otuz artırdı.
Ancak, spawn ve exec kendi zorluk setlerini sunar. Win32 altında gerçek bir yürütme yapmanın bir yolu olmadığından, Cygwin kendi İşlem Kimliklerini (PID'ler) icat etmek zorundadır. Sonuç olarak, bir işlem birden çok yürütme çağrısı gerçekleştirdiğinde, tek bir Cygwin PID ile ilişkili birden çok Windows PID olacaktır. Bazı durumlarda, bu Win32 işlemlerinin her birinin taslakları, çalıştırılan Cygwin işleminin çıkmasını bekleyerek oyalanabilir.
Çok iş gibi geliyor, değil mi? Ve evet, slooooow.
DÜZENLEME: doküman güncel değil, lütfen güncelleme için bu mükemmel yanıta bakın
fork
ancak bunu sızdıran bir çözümle gerçekleştirir ve beklenmedik durumlara hazırlıklı olmanız gerekir.
Kesinlikle bunun ayrıntılarını bilmiyorum çünkü bunu hiç yapmadım, ancak yerel NT API'nin bir işlemi çatallama yeteneği var (Windows'taki POSIX alt sistemi bu özelliğe ihtiyaç duyar - POSIX alt sisteminin olup olmadığından emin değilim artık destekleniyor).
ZwCreateProcess () araması size biraz daha ayrıntı vermelidir - örneğin , Maxim Shatskih'den alınan bu bilgi :
Buradaki en önemli parametre SectionHandle'dır. Bu parametre NULL ise, çekirdek mevcut süreci çatallayacaktır. Aksi takdirde, bu parametre, ZwCreateProcess () çağrılmadan önce EXE dosyasında oluşturulan SEC_IMAGE bölüm nesnesinin bir tanıtıcısı olmalıdır.
Corinna Vinschen'in, Cygwin'in ZwCreateProcess () kullanarak bulduğunun hala güvenilmez olduğunu gösterdiğine dikkat edin :
Iker Arizmendi şunları yazdı:
> 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
Bu belge oldukça eski, 10 yıl kadar. Çatallamayı taklit etmek için hala Win32 çağrılarını kullanırken, yöntem gözle görülür şekilde değişti. Özellikle, belirli veri yapılarının alt öğeye kopyalanmadan önce ebeveynde özel bir işleme ihtiyaç duymadıkça, alt süreci artık askıya alınmış durumda oluşturmuyoruz. Mevcut 1.5.25 sürümünde, askıya alınmış bir çocuk için tek durum ebeveyndeki açık soketlerdir. Yaklaşan 1.7.0 sürümü hiç askıya alınmayacaktır.
ZwCreateProcess'i kullanmamanın bir nedeni, 1.5.25 sürümüne kadar Windows 9x kullanıcılarını hala desteklememizdi. Ancak, NT tabanlı sistemlerde ZwCreateProcess'i kullanmak için yapılan iki girişim bir nedenden ötürü başarısız oldu.
Bu şeylerin daha iyi olması veya belgelenmesi, özellikle birkaç veri yapısı ve bir süreci bir alt sisteme nasıl bağlayacağı gerçekten güzel olurdu. Fork bir Win32 konsepti olmasa da, fork'un uygulanmasını kolaylaştırmanın kötü bir şey olacağını sanmıyorum.
fork
hemen exec
" istiyorsa , o zaman belki de CreateProcess bir adaydır. Ancak fork
olmadan exec
genellikle arzu edilir ve bu, insanları gerçek istemeye iten şeydir fork
.
Şey, pencerelerde gerçekten buna benzer bir şey yok. Özellikle fork kavramsal olarak * nix'te bir iş parçacığı veya işlem oluşturmak için kullanılabildiğinden.
Yani şunu söylemeliyim:
CreateProcess()
/CreateProcessEx()
ve
CreateThread()
(C uygulamaları _beginthreadex()
için daha iyi olduğunu duydum ).
İnsanlar Windows'ta fork uygulamayı denediler. Bulabildiğim en yakın şey bu:
Alınan: 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
Çökerse, programı kilitlerse veya iş parçacığı çökerse ne olur ? Programı çökertirse, bu gerçekten çatallaşma değildir. Merak ediyorum, çünkü gerçek bir çözüm arıyorum ve umarım bu iyi bir alternatif olabilir.
Microsoft, yeni "Windows için Linux alt sistemi" seçeneğini CreateProcess()
sunmadan önce fork()
, Windows'un yapması gereken en yakın şeydi , ancak Windows, bu işlemde çalıştırmak için bir yürütülebilir dosya belirtmenizi gerektiriyordu.
UNIX süreci oluşturma, Windows'tan oldukça farklıdır. Onun fork()
çağrı temelde kendi adres alanında neredeyse toplamda her akım sürecini kopyalar ve bunları ayrı ayrı çalışmaya devam eder. İşlemlerin kendileri farklı olsa da, yine de aynı programı çalıştırıyorlar . Modele iyi bir genel bakış için buraya bakın fork/exec
.
Öbür tarafa doğru giderek, Windows'un eşdeğer CreateProcess()
olan fork()/exec()
çifti UNIX içinde fonksiyonların.
Yazılımı Windows'a taşıyorsanız ve bir çeviri katmanına aldırış etmiyorsanız, Cygwin istediğiniz yeteneği sağladı, ancak bu daha çok kludgeydi.
Tabii ki, birlikte yeni Linux alt sistemin Windows zorundadır yakın şey fork()
olduğunu aslında fork()
:-)
fork
olmayan ortalama bir uygulamada kullanabilir miyim?
Aşağıdaki belge, kodu UNIX'ten Win32'ye taşıma hakkında bazı bilgiler sağlar: https://msdn.microsoft.com/en-us/library/y23kc048.aspx
Diğer şeylerin yanı sıra, süreç modelinin iki sistem arasında oldukça farklı olduğunu belirtir ve fork () benzeri davranışın gerekli olduğu durumlarda CreateProcess ve CreateThread'in dikkate alınmasını önerir.
"dosya erişimi veya yazdırmak istediğiniz anda io reddedilir"
Pastanızı yiyip de yiyemezsiniz ... msvcrt.dll'de printf (), konsol alt sistemi (csrss.exe) ile iletişim kurmak için lpc'yi kullanan Konsol API'sini temel alır. Csrss ile bağlantı, süreç başlangıcında başlatılır, yani "ortada" yürütülmeye başlayan herhangi bir işlem bu adımı atlayacaktır. İşletim sisteminin kaynak koduna erişiminiz olmadığı sürece, csrss'e manuel olarak bağlanmaya çalışmanın bir anlamı yoktur. Bunun yerine, kendi alt sisteminizi oluşturmalı ve buna göre fork () kullanan uygulamalarda konsol işlevlerinden kaçınmalısınız.
Kendi alt sisteminizi uyguladıktan sonra, alt süreç için tüm ebeveynin tanıtıcılarını da kopyalamayı unutmayın ;-)
"Ayrıca, çekirdek kipinde değilseniz muhtemelen Zw * işlevlerini kullanmamalısınız, bunun yerine muhtemelen Nt * işlevlerini kullanmalısınız."
ZwGetContextThread (NtCurrentThread (), & bağlam);
fork () semantiği, fork () çağrıldığı anda çocuğun ebeveynin gerçek bellek durumuna erişmesi gerektiğinde gereklidir. Anlık fork () çağrıldıkça bellek kopyalamanın örtük muteksine dayanan bir yazılım parçam var, bu da iş parçacıklarının kullanımını imkansız hale getiriyor. (Bu, modern * nix platformlarında yazma üzerine kopyala / bellek tablosunu güncelle semantiği aracılığıyla taklit edilir.)
Windows'ta sistem çağrısı olarak bulunan en yakın olanı CreateProcess'tir. Ebeveyn için yapılabilecek en iyi şey, belleği yeni sürecin bellek alanına kopyalarken diğer tüm iş parçacıklarını dondurması ve ardından çözdürmesidir. Ne Cygwin frok [sic] sınıfı ne de Eric des Courtis'in gönderdiği Scilab kodu, görebildiğim kadarıyla iş parçacığı dondurma işlemi yapmıyor.
Ayrıca, çekirdek kipinde olmadığınız sürece muhtemelen Zw * işlevlerini kullanmamalısınız, bunun yerine muhtemelen Nt * işlevlerini kullanmalısınız. Çekirdek modunda olup olmadığınızı kontrol eden ve değilse, Nt * 'nin her zaman yaptığı tüm sınır kontrolü ve parametre doğrulamasını gerçekleştiren fazladan bir dal vardır. Bu nedenle, onları kullanıcı modundan aramak çok az verimlidir.
En iyi seçenekleriniz CreateProcess () veya CreateThread () 'dir . Burada taşıma hakkında daha fazla bilgi var .
Windows'ta fork () 'u taklit etmenin kolay bir yolu yoktur.
Bunun yerine ipleri kullanmanızı öneririm.
fork
oldu tam olarak Cygwin'ın yaptıklarını. Hiç kadar okumak Ama eğer nasıl yaptılar, yor "kolay bir yolu yok" brüt misunderstatement :-) olduğunu
En yakın söylediğin ... Bir düşüneyim ... Bu çatal olmalı () Sanırım :)
Ayrıntılar için bkz. Interix fork () kullanıyor mu?
Diğer yanıtların da belirttiği gibi, NT (Windows'un modern sürümlerinin altında yatan çekirdek) Unix fork () ile eşdeğerdir. Sorun bu değil.
Sorun, bir sürecin tüm durumunu klonlamanın genellikle yapılacak mantıklı bir şey olmamasıdır. Bu, Windows'ta olduğu gibi Unix dünyasında da doğrudur, ancak Unix dünyasında, fork () her zaman kullanılır ve kitaplıklar bununla başa çıkmak için tasarlanmıştır. Windows kitaplıkları değildir.
Örneğin, sistem DLL'leri kernel32.dll ve user32.dll, Win32 sunucu işlemi csrss.exe'ye özel bir bağlantı sağlar. Bir çatallaşmadan sonra, bu bağlantının istemci tarafında sorunlara neden olacak iki işlem vardır. Alt süreç, csrss.exe'ye varlığını bildirmeli ve yeni bir bağlantı kurmalıdır - ancak bunu yapacak bir arayüz yoktur, çünkü bu kütüphaneler fork () akılda tutularak tasarlanmamıştır.
Yani iki seçeneğiniz var. Birincisi, kernel32 ve user32'nin ve çatallanacak şekilde tasarlanmamış diğer kitaplıkların - doğrudan ya da dolaylı olarak kernel32 veya user32'ye bağlanan tüm kitaplıklar da dahil olmak üzere - kullanımını yasaklamaktır. Bu, Windows masaüstü ile hiçbir şekilde etkileşim kuramayacağınız ve kendi ayrı Unixy dünyanızda sıkışıp kaldığınız anlamına gelir. Bu, NT için çeşitli Unix alt sistemleri tarafından benimsenen yaklaşımdır.
Diğer seçenek ise, fork () ile çalışmak üzere kütüphanelerin farkında olmadan çalışmasını sağlamak için bir tür korkunç hack'e başvurmaktır. Cygwin böyle yapar. Yeni bir süreç yaratır, başlatılmasına izin verir (kendini csrss.exe ile kaydettirmek dahil), sonra dinamik durumun çoğunu eski süreçten kopyalar ve en iyisini umar. Bu beni şaşırtıyor hiç çalışır. Kesinlikle güvenilir bir şekilde çalışmaz - bir adres alanı çakışması nedeniyle rastgele başarısız olmasa bile, kullandığınız herhangi bir kitaplık sessizce bozuk bir durumda kalabilir. Cygwin'in "tam özellikli bir çatala ()" sahip olduğu şeklindeki mevcut kabul edilen cevabın iddiası ... şüphelidir.
Özet: Interix benzeri bir ortamda fork () 'u çağırarak fork yapabilirsiniz. Aksi takdirde, lütfen bunu yapma arzusundan kendinizi kurtarmaya çalışın. Cygwin'i hedefliyor olsanız bile, kesinlikle mecbur kalmadıkça fork () kullanmayın.
Yalnızca bir alt işlem oluşturmayı ve onu beklemeyi önemsiyorsanız, belki de process.h'deki _spawn * API'leri yeterlidir. İşte bununla ilgili daha fazla bilgi:
https://docs.microsoft.com/en-us/cpp/c-runtime-library/process-and-environment-control https://en.wikipedia.org/wiki/Process.h