Fork (), vfork (), exec () ve clone () arasındaki fark


198

Google'da bu dördü arasındaki farkı bulmak istiyordum ve bu konuda çok fazla bilgi olmasını bekliyordum, ancak dört çağrı arasında gerçekten sağlam bir karşılaştırma yoktu.

Bu sistem çağrıları arasındaki farklara bir bakışta bir tür temel bakışta derlemeye çalıştım ve işte ne aldım. Tüm bu bilgiler doğru mu / önemli bir şey eksik mi?

Fork : Çatal çağrısı temel olarak mevcut işlemin neredeyse her şekilde aynı olanını yapar (her şey kopyalanmaz, örneğin bazı uygulamalarda kaynak sınırları, ancak fikir mümkün olduğunca yakın bir kopya oluşturmaktır).

Yeni işlem (alt öğe) farklı bir işlem kimliği (PID) alır ve eski işlemin (üst öğenin) PID'sini üst PID'si (PPID) olarak alır. İki işlem artık tam olarak aynı kodu çalıştırdığından, hangisinin çatalın dönüş koduyla hangisinin olduğunu 0 söyleyebilir - çocuk 0 alır, ebeveyn çocuğun PID'sini alır. Tabii ki, çatal çağrısının işe yaradığını varsayarsak, hepsi değilse, hiçbir çocuk oluşturulmaz ve ebeveyn bir hata kodu alır.

Vfork: Vfork ve fork arasındaki temel fark, vfork () ile yeni bir işlem oluşturulduğunda, üst işlemin geçici olarak askıya alınması ve alt işlemin üst öğenin adres alanını ödünç almasıdır. Bu garip durum, alt süreç ya da üst süreç devam edinceye kadar execve () çağrılarına kadar devam eder.

Bu, vfork () işlevinin alt işleminin, üst işlemin değişkenlerini beklenmedik biçimde değiştirmemesi için dikkatli olması gerektiği anlamına gelir. Özellikle, alt işlem vfork () çağrısını içeren işlevden geri dönmemeli ve exit () çağrılmamalıdır (çıkması gerekiyorsa _exit () kullanmalıdır; aslında, bu çocuk için de geçerlidir normal çatalın ()).

Exec :Exec çağrısı temelde mevcut sürecin tamamını yeni bir programla değiştirmenin bir yoludur. Programı geçerli işlem alanına yükler ve giriş noktasından çalıştırır. exec () işlevi, işlemin işaret ettiği bir yürütülebilir dosyayla değiştirir. Bir exec () hatası olmadıkça, kontrol hiçbir zaman orijinal programa geri dönmez.

Clone :Klon, çatal olarak yeni bir süreç yaratır. Çataldan farklı olarak, bu çağrılar alt işlemin yürütme bağlamının bir bölümünü bellek alanı, dosya tanımlayıcıları tablosu ve sinyal işleyicileri tablosu gibi çağrı işlemiyle paylaşmasına izin verir.

Alt işlem klonla oluşturulduğunda, fn (arg) işlev uygulamasını yürütür. (Bu, yürütmenin çocukta orijinal çatal çağrısı noktasından devam ettiği çataldan farklıdır.) Fn argümanı, yürütme başlangıcında alt işlem tarafından çağrılan bir işleve işaret eder. Arg argümanı fn işlevine iletilir.

Fn (arg) işlevi uygulaması geri döndüğünde, alt işlem sonlandırılır. Fn tarafından döndürülen tam sayı, alt işlemin çıkış kodudur. Alt süreç ayrıca exit (2) öğesini çağırarak veya ölümcül bir sinyal aldıktan sonra açıkça sonlanabilir.

Bilgi Alındı ​​Formu:

Bunu okumak için zaman ayırdığınız için teşekkür ederiz! :)


2
Neden vfork exit () öğesini çağırmamalıdır? Yoksa geri dönmeyecek misin? Exit () sadece _exit () kullanmıyor mu? Ben de anlamaya çalışıyorum :)
LazerSharks

2
@Gnuey: çünkü potansiyel olarak (farklı bir şekilde uygulanmışsa fork(), Linux'ta olduğu ve muhtemelen tüm BSD'lerde) ebeveyninin adres alanını ödünç alıyor. Her şey o çağırarak yanında yapar execve()ya _exit(), ebeveyn kadar karışıklık için büyük bir potansiyele sahiptir. Özellikle, işleyicileri ve diğer "sonlandırıcıları" exit()çağırır atexit(), örneğin: stdio akışlarını temizler. Bir vfork()çocuktan dönmek, potansiyel olarak (önceki ile aynı uyarı) ebeveynin yığınını kirletir.
ninjalj

Üst sürecin iş parçacıklarına ne olacağını merak ediyordum; Hepsi klonlanmış mı yoksa yalnızca forksistem çağrısını çağıran evre mi?
Mohammad Jafar Mashhadi

@LazerSharks vfork, belleğin yazma üzerine kopyalama koruması olmadan paylaşıldığı bir iş parçacığı benzeri işlem üretir, bu nedenle yığın işi yapmak üst işlemi çöpe atabilir.
Jasen

Yanıtlar:


159
  • vfork()eski bir optimizasyon. İyi bellek yönetiminden önce fork(), ebeveynin belleğinin tam bir kopyasını yaptı, bu yüzden oldukça pahalıydı. çoğu durumda a'nın fork()ardından gelen ve exec()o anki bellek haritasını atan ve yeni bir harita oluşturan a'nın ardından gereksiz bir harcama yapıldı. Günümüzde fork()hafızayı kopyalamıyor; basitçe bu yüzden, "yazma sırasında kopyalama" şeklinde ayarlanır fork()+ exec()kadar verimli adildir vfork()+ exec().

  • clone()tarafından kullanılan sistem çağrısıdır fork(). bazı parametrelerle yeni bir süreç yaratır, diğerleriyle birlikte bir iş parçacığı oluşturur. aralarındaki fark sadece hangi veri yapılarının (bellek alanı, işlemci durumu, yığın, PID, açık dosyalar, vb.) paylaşılıp paylaşılmadığıdır.



22
vforkbir kişinin çalışabilmesi için geçici olarak çok daha fazla bellek verme ihtiyacını ortadan kaldırır ve neredeyse bir dereceye kadar olmasa bile execyine de daha verimlidir fork. Böylece, büyük bir programın bir çocuk sürecini ortaya çıkarabilmesi için hafızayı aşırı işlemekten kaçınabilirsiniz. Yani, sadece bir performans artışı değil, aynı zamanda uygulanabilir.
Tekilleştirici

5
Aslında, RSS büyük olduğunda fork () ne kadar ucuz olduğunu ilk elden tanık oldum. Bunun, çekirdeğin hala tüm sayfa tablolarını kopyalaması gerektiği için olduğunu düşünüyorum.
Martina Ferrari

4
Tüm sayfa tablolarını kopyalamalı, yazılabilir tüm kopyalanacak yazma kopyalarını her iki işlemde de ayarlamalı , TLB'yi temizlemeli ve sonra üst öğedeki tüm değişiklikleri geri almalı (ve TLB'yi yeniden temizlemelidir) exec.
zwol

3
vfork hala cygwin (Microsoft'un Windows üzerinde çalışan bir çekirdek taklit dll) yararlıdır. Altta yatan işletim sisteminde bir tane olmadığı için cygwin etkili bir çatal uygulayamaz.
ctrl-alt-delor

80
  • execve() geçerli yürütülebilir görüntüyü yürütülebilir dosyadan yüklenen başka bir görüntüyle değiştirir.
  • fork() bir alt süreç oluşturur.
  • vfork(), hemen sonra çağrıldığında fork()kullanılması gereken execve(), geçmişe göre optimize edilmiş bir sürümüdür fork(). MMU olmayan sistemlerde ( fork()verimli bir şekilde çalışamayacağı yerlerde ) ve fork()küçük bir program çalıştırmak için büyük bir bellek ayak izine sahip işlemler yaparken ( Java'ları düşünün Runtime.exec()) iyi çalıştığı ortaya çıktı . POSIX, posix_spawn()bu son iki modern kullanımı yerine standartlaştırmıştır vfork().
  • posix_spawn()a'nın eşdeğerini yapar fork()/execve()ve aynı zamanda bazı fd hokkabazlıklarına izin verir. Çoğunlukla fork()/execve()MMU olmayan platformların yerine geçmesi gerekiyordu .
  • pthread_create() yeni bir iş parçacığı oluşturur.
  • clone()bir şey uygulamak için kullanılabilecek bir Linux özgü çagrısıdır fork()için pthread_create(). Çok fazla kontrol sağlıyor. İlham kaynağı rfork().
  • rfork()Plan-9'a özgü bir aramadır. Tam süreçler ve iş parçacıkları arasında birkaç derecelik paylaşıma izin veren genel bir çağrı olması gerekiyordu.

2
Aslında istendiğinden daha fazla bilgi eklediğiniz için teşekkürler, zamanımı kurtarmama yardımcı oldu
Neeraj

5
Plan 9 böyle bir alay.
JJ

1
MMU'nun ne anlama geldiğini hatırlayamayanlar için: "Bellek yönetim birimi" - Wikipedia'da
mgarey

43
  1. fork()- üst sürecin tam bir kopyası olan yeni bir alt süreç oluşturur. Alt ve üst süreçler, başlangıçta aynı bellek sayfaları tarafından doldurulmuş farklı sanal adres alanları kullanır. Daha sonra, her iki işlem yürütüldükçe, sanal adres alanları daha da farklılaşmaya başlar, çünkü işletim sistemi bu iki işlemden biri tarafından yazılan bellek sayfalarının tembel bir kopyasını gerçekleştirir ve değiştirilen sayfaların bağımsız bir kopyasını atar. Her işlem için bellek. Bu tekniğe Yazarken Kopyala (COW) denir.
  2. vfork()- ana sürecin "hızlı" bir kopyası olan yeni bir alt süreç oluşturur. Sistem çağrısının aksine fork(), alt ve üst süreçler aynı sanal adres alanını paylaşır. NOT! Hem ebeveyn hem de çocuk aynı sanal adres alanını kullanarak, klasikte olduğu gibi aynı yığını, yığın işaretçisini ve talimat işaretçisini kullanır fork()! Aynı yığını kullanan üst öğe ve alt öğe arasında istenmeyen paraziti önlemek için, alt işlem exec()(yeni bir sanal adres alanı ve farklı bir yığına geçiş oluşturma) veya _exit()(işlem yürütmesinin sonlandırılması ) çağrılıncaya kadar üst işlemin yürütülmesi dondurulur ). "fork-and-exec" modelinin vfork()optimizasyonudur fork(). Bundan 4-5 kat daha hızlı yapılabilir fork(), çünküfork()(COW akılda tutulduğunda bile), vfork()sistem çağrısının uygulanması, yeni bir adres alanının oluşturulmasını (yeni sayfa dizinlerinin tahsisi ve kurulması) içermez.
  3. clone()- yeni bir alt süreç oluşturur. Bu sistem çağrısının çeşitli parametreleri, üst sürecin hangi bölümlerinin alt sürece kopyalanması gerektiğini ve aralarında hangi bölümlerin paylaşılacağını belirtir. Sonuç olarak, bu sistem çağrısı, iş parçacıklarından başlayarak ve tamamen bağımsız süreçlerle biten her türlü yürütme varlığını oluşturmak için kullanılabilir. Aslında, clone()sistem çağrısı, sistem çağrılarının pthread_create()ve tüm aile fork()çağrılarının uygulanması için kullanılan temeldir.
  4. exec()- işlemin tüm belleğini sıfırlar, belirtilen çalıştırılabilir ikili dosyayı yükler ve ayrıştırır, yeni yığını ayarlar ve denetimi yüklenen yürütülebilir dosyanın giriş noktasına geçirir. Bu sistem çağrısı kontrolü asla arayana geri döndürmez ve mevcut bir sürece yeni bir programın yüklenmesine hizmet eder. Sistem çağrısı ile fork()birlikte yapılan bu sistem çağrısı, "fork-and-exec" adlı klasik UNIX süreç yönetimi modelini oluşturur.

2
BSD ve POSIX gereksinimlerinin vforko kadar zayıf olduğunu ve vforkeş anlamlı hale forkgetirmenin yasal olacağını unutmayın (ve POSIX.1-2008 vforkspesifikasyondan tamamen kaldırılır ). Kodunuzu, eşanlamlı hale getiren bir sistemde test ederseniz (örneğin NetBSD'den önceki en fazla 4.4 sonrası BSD, 2.2.0 öncesi-6 Linux çekirdeği vb.), vforkSözleşmeyi ihlal etseniz bile patlayabilir, sonra patlayabilir eğer başka bir yerde çalıştırırsan. Bunu taklit edenlerden bazıları fork(örn. OpenBSD), ebeveynin çocuk execya da çocuklara kadar çalışmaya devam etmediğini garanti eder _exit. Gülünç taşınabilir değil.
ShadowRanger

2
3. noktanızın son cümlesiyle ilgili olarak: Linux'ta fork () için glibc sarmalayıcısının (klon syscall) klonunu varken, vfork () için sarmalayıcı vfork syscall
ilstam

7

Fork (), vfork () ve clone (), do_fork () 'u gerçek işi yapmak için çağırır, ancak farklı parametrelerle.

asmlinkage int sys_fork(struct pt_regs regs)
{
    return do_fork(SIGCHLD, regs.esp, &regs, 0);
}

asmlinkage int sys_clone(struct pt_regs regs)
{
    unsigned long clone_flags;
    unsigned long newsp;

    clone_flags = regs.ebx;
    newsp = regs.ecx;
    if (!newsp)
        newsp = regs.esp;
    return do_fork(clone_flags, newsp, &regs, 0);
}
asmlinkage int sys_vfork(struct pt_regs regs)
{
    return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0);
}
#define CLONE_VFORK 0x00004000  /* set if the parent wants the child to wake it up on mm_release */
#define CLONE_VM    0x00000100  /* set if VM shared between processes */

SIGCHLD means the child should send this signal to its father when exit.

Çatal için, çocuk ve babanın bağımsız VM sayfa tablosu vardır, ancak verimlilikten dolayı çatal gerçekten herhangi bir sayfayı kopyalamayacaktır, sadece tüm yazılabilir sayfaları çocuk işlemi için salt okunur olarak ayarlamıştır. Dolayısıyla, alt süreç o sayfaya bir şeyler yazmak istediğinde, bir sayfa istisnası olur ve çekirdek, eski sayfadan yazma izni ile klonlanmış yeni bir sayfa tahsis eder. Buna "yazarken kopyala" denir.

Vfork için, sanal bellek tam olarak çocuk ve baba tarafından --- sadece bundan dolayı, baba ve çocuk birbirlerini etkileyecekleri için aynı anda uyanık olamazlar. Böylece baba "do_fork ()" sonunda uyuyacak ve çocuk çağrısı exit () veya execve () 'dan sonra uyanacaktır, o zamandan beri yeni sayfa tablosu olacaktır. İşte babanın uyuduğu kod (do_fork ()).

if ((clone_flags & CLONE_VFORK) && (retval > 0))
down(&sem);
return retval;

Babayı uyandıran exit () ve execve () tarafından çağrılan kod (mm_release () cinsinden).

up(tsk->p_opptr->vfork_sem);

Sys_clone () için, herhangi bir clone_flags girebileceğiniz için daha esnektir. Yani pthread_create () bu sistem çağrısını birçok clone_flags ile çağırır:

int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM);

Özet: fork (), vfork () ve clone (), baba işlemiyle farklı kaynak paylaşımına sahip alt süreçler oluşturacaktır. VM sayfa tablosunu baba işlemiyle paylaştıkları için vfork () ve clone () 'in iş parçacıkları oluşturabileceğini de söyleyebiliriz (aslında bağımsız görev_yapılarına sahip oldukları için süreçlerdir).


-4

fork () 'da, çocuk ya da ana işlem cpu seçimine dayalı olarak yürütülür. çocuk sonlandırıldıktan sonra, ebeveyn yürütülür.


3
Yanlış. vfork()olarak uygulanabilir fork().
ninjalj

AnyFork () öğesinden sonra, ilk üst / alt öğeyi kimin çalıştırdığı tanımlanmaz.
AjayKumarBasuthkar

5
@Raj: Çatalladıktan sonra örtülü bir seri düzen kavramı olduğunu düşünüyorsanız bazı kavramsal yanlış anlamalar yaşarsınız. Forking, yeni bir işlem oluşturur ve ardından her iki işleme de denetim döndürür (her biri farklı bir değer döndürür pid) - işletim sistemi, böyle bir şey mantıklıysa (örneğin birden çok işlemci) yeni işlemi paralel olarak çalışacak şekilde zamanlayabilir. Herhangi bir nedenle bu işlemlerin belirli bir seri sırada yürütülmesi gerekiyorsa, çatallamanın sağlamadığı ek senkronizasyona ihtiyacınız vardır; Açıkçası, muhtemelen ilk etapta çatal bile istemezsiniz.
Andon M. Coleman

Aslında @AjayKumarBasuthkar ve @ninjalj, ikiniz de yanılıyorsunuz. ile vfork(), çocuk önce koşar. Adam sayfalarında; çocuk ölene ya da ölene kadar ebeveynlerin infazı askıya alınır exec. Ve ninjalj çekirdek kaynak kodunu arar. Uygulamak için bir yolu yoktur vfork()olarak fork()onlar için farklı argümanlar iletmemizdir do_fork()çekirdek içinde. Bununla vforkbirlikte, clonesistem çağrısı ile uygulayabilirsiniz
Zac Wimer

@ZacWimer: Başka bir cevabım için ShadowRanger yorumuna bakınız stackoverflow.com/questions/4856255/... Eski Linux yaptığı gibi görünüşte NetBSD dışındaki BSD yapmak (eğilimindedir olmayan MMU sistemleri bir sürü uyarlanması planlanan), synonimize onları. Linux kılavuzundan: 4.4BSD'de fork (2) ile eşanlamlı hale getirildi, ancak NetBSD tekrar tanıttı; bkz. ⟨netbsd.org/Documentation/ kernel/ vfork.html⟩. Linux'ta, 2.2.0-pre6'ya kadar çatal (2) ile eşdeğerdir.
ninjalj
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.