Windows'un fork () yapması gereken en yakın şey nedir?


124

Sanırım soru her şeyi söylüyor.

Windows'ta fork yapmak istiyorum. En benzer işlem nedir ve nasıl kullanırım.

Yanıtlar:


86

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


11
Windows üzerinde bir Cygwin uygulaması yazmak istiyorsanız bu iyi bir cevaptır. Ancak genel olarak yapılacak en iyi şey bu değil. Temelde, * nix ve Windows işlem ve iş parçacığı modelleri oldukça farklıdır. CreateProcess () ve CreateThread () genel olarak eşdeğer API'lerdir
Foredecker

2
Geliştiriciler, bunun desteklenmeyen bir mekanizma olduğunu ve IIRC'nin sistemdeki diğer bazı işlemler kod enjeksiyonu kullandığında gerçekten de kırılmaya meyilli olduğunu akılda tutmalıdır.
Harry Johnston

1
Farklı uygulama bağlantısı artık geçerli değil.
PythonNut

Yalnızca diğer yanıt bağlantısını bırakacak şekilde düzenlendi
Laurynas Biveinis

@Foredecker, Aslında bir "cygwin uygulaması" yazmaya çalışsanız bile bunu yapmamalısınız . Unix'i taklit etmeye çalışır, forkancak bunu sızdıran bir çözümle gerçekleştirir ve beklenmedik durumlara hazırlıklı olmanız gerekir.
Pacerier

66

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.


Bu yanlış cevap. CreateProcess () ve CreateThread () genel eşdeğerlerdir.
Foredecker

2
Interix, Windows Vista Enterprise / Ultimate'da "UNIX Uygulamaları için Alt Sistem" olarak mevcuttur: en.wikipedia.org/wiki/Interix
bk1e

15
@Foredecker - bu yanlış bir cevap olabilir, ancak CreateProcess () / CreateThread () de yanlış olabilir. Bu, birinin 'Win32 bir şeyler yapmanın yolu' veya 'fork () semantiğine mümkün olduğunca yakın' olup olmamasına bağlıdır. CreateProcess (), fork () 'dan önemli ölçüde farklı davranır, bu da cygwin'in onu desteklemek için çok fazla iş yapması gerektiğinin sebebidir.
Michael Burr

1
@jon: Bağlantıları düzeltmeyi ve ilgili metni yanıta kopyalamayı denedim (bu nedenle gelecekteki bozuk bağlantılar sorun olmaz). Ancak, bu cevap yeterince uzun zaman öncesine ait, bugün bulduğum alıntıdan 2009'da bahsettiğim şey olduğundan% 100 emin değilim.
Michael Burr

4
İnsanlar " forkhemen exec" istiyorsa , o zaman belki de CreateProcess bir adaydır. Ancak forkolmadan execgenellikle arzu edilir ve bu, insanları gerçek istemeye iten şeydir fork.
Aaron McDaid

37

Ş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 ).


17

İ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;
}

4
Hata kontrolünün çoğunun eksik olduğuna dikkat edin - örneğin ZwCreateThread, SUCCEEDED ve FAILED makroları kullanılarak kontrol edilebilen bir NTSTATUS değeri döndürür.
BCran

1
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.
leetNightshade

1
Sağlanan kodda bir hata olduğunu belirtmek isterim. haveLoadedFunctionsForFork, başlıktaki genel bir işlevdir, ancak c dosyasında statik bir işlevdir. İkisi de küresel olmalı. Ve şu anda fork çöküyor, şimdi hata kontrolü ekleniyor.
leetNightshade

Site öldü ve örneği kendi sistemimde nasıl derleyebileceğimi bilmiyorum. Bazı başlıkları özlediğimi veya yanlış olanları eklediğimi varsayıyorum, öyle mi? (örnek onları göstermiyor.)
Paul Stelian

6

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() :-)


2
Öyleyse, WSL verildiğinde, WSL forkolmayan ortalama bir uygulamada kullanabilir miyim?
Sezar

6

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.


4

"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."

  • Bu yanlış. Kullanıcı modunda erişildiğinde, Zw *** Nt *** arasında kesinlikle hiçbir fark yoktur; bunlar yalnızca aynı (göreceli) sanal adrese başvuran iki farklı (ntdll.dll) dışa aktarılan addır.

ZwGetContextThread (NtCurrentThread (), & bağlam);

  • ZwGetContextThread'i çağırarak mevcut (çalışan) iş parçacığının bağlamını elde etmek yanlıştır, büyük olasılıkla çökebilir ve (ekstra sistem çağrısı nedeniyle) görevi gerçekleştirmenin en hızlı yolu da değildir.

2
Bu, ana soruyu yanıtlıyor gibi görünmüyor, ancak birkaç farklı yanıtı yanıtlıyor ve muhtemelen netlik açısından her birine doğrudan yanıt vermek ve neler olup bittiğini takip etmeyi kolaylaştırmak için daha iyi olacaktır.
Leigh

printf'in her zaman konsola yazdığını düşünüyorsunuz.
Jasen

3

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.


Zw * dışa aktarılan sembollerle ilgili çok ilginç bilgiler, teşekkür ederim.
Andon M. Coleman

Güvenlik için, kullanıcı alanından gelen Zw * işlevlerinin hala çekirdek uzayındaki Nt * işlevleriyle eşleştiğini unutmayın. Ya da en azından yapmalılar.
Paul Stelian


2

Windows'ta fork () 'u taklit etmenin kolay bir yolu yoktur.

Bunun yerine ipleri kullanmanızı öneririm.


Eh, adalet içinde hayata geçirilmesinden forkoldu 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
paxdiablo


2

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.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.