Çatal ve exec arasındaki farklar


Yanıtlar:


364

Yeni süreçlere başlamak için çok basit bir yol sağladığı için UNIX ruhunu kullanmak forkve execörneklendirmek.

forkÇağrı temelde özdeş, mevcut sürecin yinelenen yapar neredeyse her şekilde. Her şey kopyalanmaz (örneğin, bazı uygulamalarda kaynak sınırları), ancak amaç, olabildiğince yakın bir kopya oluşturmaktır.

Yeni işlem (alt) farklı bir işlem kimliği (PID) alır ve üst PID (PPID) olarak eski işlemin (üst öğe) PID'sine sahiptir. İki işlem artık tam olarak aynı kodu çalıştırdığından, hangisinin dönüş koduyla hangisinin fork0 olduğunu, ebeveynin çocuğun PID'sini aldığını söyleyebilirler . Tabii ki, bu, forkçağrının işe yaradığı varsayılarak varsayılır - eğer değilse, hiçbir çocuk oluşturulmaz ve ebeveyn bir hata kodu alır.

execÇağrı temelde yeni bir program ile tüm geçerli süreci değiştirmek için bir yoldur. Programı geçerli işlem alanına yükler ve giriş noktasından çalıştırır.

Yani, forkve execgenellikle mevcut sürecin bir çocuk olarak çalışan yeni bir program almak için sırayla kullanılmaktadır. Kabuklar genellikle findkabuk çatalları gibi bir programı çalıştırmaya çalıştığınızda bunu yapar , daha sonra çocuk findprogramı belleğe yükler, tüm komut satırı argümanlarını, standart G / Ç vb.

Ancak birlikte kullanılmaları gerekmez. Örneğin, programın hem ebeveyn hem de alt kod içerip içermediğini forkbilmeden bir programın kendisi için mükemmel kabul edilebilir exec(ne yaptığınıza dikkat etmeniz gerekir, her uygulamanın kısıtlamaları olabilir). Bu, forkebeveyn dinlemeye geri dönerken belirli bir isteği işlemek için bir TCP bağlantı noktasını ve kendilerinin bir kopyasını dinleyen daemonlar için oldukça fazla (ve hala) kullanıldı .

Benzer şekilde, bitmiş olduğunu biliyorum ve programlar sadece gerekmez başka bir programı çalıştırmak istiyorum fork, execve sonra waitçocuk için. Çocuğu doğrudan işlem alanlarına yükleyebilirler.

Bazı UNIX uygulamalarında, forküzerine yazma kopyası adını verdikleri optimize edilmiş bir uygulama vardır . Bu, forkprogram o alandaki bir şeyi değiştirmeye çalışana kadar işlem alanının kopyalanmasını geciktiren bir hiledir . Bu, yalnızca kullanarak bu programlar için yararlıdır forkve execbunlar bütün bir süreç alanını kopyalamak gerekmez ki.

Eğer exec edilir Aşağıdaki denilen fork(ve bu çoğunlukla böyle olur), bu süreç uzaya bir yazma neden olur ve daha sonra alt sürecin kopyalanır.

Bütün bir aile olduğunu Not execaramaları ( execl, execle, execvevb) ama execbağlamda burada bunlardan herhangi biri anlamına gelir.

Aşağıdaki diyagram fork/exec, bashkabuğun lskomutla bir dizini listelemek için kullanıldığı tipik işlemi göstermektedir :

+--------+
| pid=7  |
| ppid=4 |
| bash   |
+--------+
    |
    | calls fork
    V
+--------+             +--------+
| pid=7  |    forks    | pid=22 |
| ppid=4 | ----------> | ppid=7 |
| bash   |             | bash   |
+--------+             +--------+
    |                      |
    | waits for pid 22     | calls exec to run ls
    |                      V
    |                  +--------+
    |                  | pid=22 |
    |                  | ppid=7 |
    |                  | ls     |
    V                  +--------+
+--------+                 |
| pid=7  |                 | exits
| ppid=4 | <---------------+
| bash   |
+--------+
    |
    | continues
    V

52

fork()mevcut işlemi iki işleme böler. Ya da başka bir deyişle, güzel doğrusal düşünmek kolay program aniden bir kod parçası çalışan iki ayrı program olur:

 int pid = fork();

 if (pid == 0)
 {
     printf("I'm the child");
 }
 else
 {
     printf("I'm the parent, my child is %i", pid);
     // here we can kill the child, but that's not very parently of us
 }

Bu biraz aklınızı başınızdan alabilir. Şimdi, iki işlem tarafından yürütülen özdeş durumun aynısı olan bir kod parçanız var. Alt işlem, fork()çağrının kaldığı yerden başlamak da dahil olmak üzere, onu oluşturan işlemin tüm kodunu ve belleğini devralır . Tek fark, fork()ebeveyn veya çocuk olup olmadığınızı size bildiren dönüş kodudur. Üst öğe sizseniz, dönüş değeri çocuğun kimliğidir.

execkavramak biraz daha kolay, sadece exechedef yürütülebilir dosyayı kullanarak bir işlem yürütmenizi söylersiniz ve aynı kodu çalıştıran veya aynı durumu devralan iki işleminiz yoktur. @Steve Hawkins'in dediği gibi, mevcut işlemde hedef yürütülebilir dosyayı yürütmek execiçin kullanılabilir fork.


6
ayrıca arama pid < 0ve fork()arama başarısız olduğunda da durum var
Jonathan Fingland

3
Bu hiç aklımdan uçmuyor :-) Paylaşılan bir kütüphane veya DLL her kullanıldığında iki işlem tarafından yürütülen bir kod parçası olur.
paxdiablo

31

Marc Rochkind'in "Gelişmiş Unix Programlaması" ndan bazı kavramların , fork()/ exec()özellikle Windows CreateProcess()modeline alışkın olan biri için farklı rollerinin anlaşılmasında yardımcı olduğunu düşünüyorum :

Bir program, diskte düzenli dosyada tutulur talimatlar ve verilerin bir topluluğudur. (1.1.2 Programlar, Süreçler ve Konular'dan)

.

Bir programı çalıştırmak için, çekirdekten önce bir programın yürütüldüğü bir ortam olan yeni bir işlem yaratması istenir . (ayrıca 1.1.2 Programlar, İşlemler ve Konular'dan)

.

Bir işlem ve program arasındaki ayrımı tam olarak anlamadan exec veya fork sistem çağrılarını anlamak imkansızdır. Bu şartlar sizin için yeniyse, geri dönüp Bölüm 1.1.2'yi gözden geçirmek isteyebilirsiniz. Şimdi ilerlemeye hazırsanız, tek bir cümledeki farkı özetleyeceğiz: Süreç, talimat, kullanıcı verileri ve sistem veri bölümlerinin yanı sıra çalışma zamanında edinilen diğer birçok kaynaktan oluşan bir yürütme ortamıdır bir program ise, bir sürecin talimat ve kullanıcı-veri segmentlerini başlatmak için kullanılan talimatları ve verileri içeren bir dosyadır. (5.3 execSistem Çağrılarından)

Bir program ile bir süreç arasındaki farkı anladıktan sonra, fork()ve exec()fonksiyonunun davranışı şöyle özetlenebilir:

  • fork() geçerli işlemin bir kopyasını oluşturur
  • exec() geçerli işlemdeki programı başka bir programla değiştirir

(bu aslında paxdiablo'nun çok daha ayrıntılı cevabının basitleştirilmiş bir 'aptallar için' versiyonudur )


29

Fork, çağrı sürecinin bir kopyasını oluşturur. genellikle yapıyı takip eder resim açıklamasını buraya girin

int cpid = fork( );

if (cpid = = 0) 
{

  //child code

  exit(0);

}

//parent code

wait(cpid);

// end

(alt işlem metni (kod), veri, yığın çağırma işlemi ile aynıdır) alt işlem if bloğundaki kodu yürütür.

EXEC, mevcut işlemi yeni işlemin kodu, verileri, yığını ile değiştirir. genellikle yapıyı takip eder resim açıklamasını buraya girin

int cpid = fork( );

if (cpid = = 0) 
{   
  //child code

  exec(foo);

  exit(0);    
}

//parent code

wait(cpid);

// end

(exec çağrısından sonra unix çekirdeği alt işlem metnini, verileri, yığını ve foo işlemiyle ilgili metin / verileri doldurur) bu nedenle alt işlem farklı kodlarla (foo'nun kodu {ebeveyn ile aynı değildir})


1
Soruyla biraz ilgisi yok, ancak yukarıdaki bu kod, alt süreç önce kodunu bitirirse bir yarış durumuna neden olmaz mı? Bu durumda, ebeveyn süreci sonsuza dek çocuğun kendini sonlandırmasını beklerdi, değil mi?
stdout

7

Birlikte yeni bir alt süreç oluşturmak için kullanılırlar. İlk olarak, çağrı forkgeçerli işlemin (alt işlem) bir kopyasını oluşturur. Ardından, execüst işlemin kopyasını yeni işlemle "değiştirmek" için alt işlem içinden çağrılır.

Süreç böyle bir şeye gider:

child = fork();  //Fork returns a PID for the parent process, or 0 for the child, or -1 for Fail

if (child < 0) {
    std::cout << "Failed to fork GUI process...Exiting" << std::endl;
    exit (-1);
} else if (child == 0) {       // This is the Child Process
    // Call one of the "exec" functions to create the child process
    execvp (argv[0], const_cast<char**>(argv));
} else {                       // This is the Parent Process
    //Continue executing parent process
}

2
7. satırda exec () işlevinin alt işlemi oluşturduğu belirtilmektedir. Gerçekten fork () zaten alt işlemi oluşturmuş ve exec () çağrısı yeni oluşturulmuş yeni programın yerini alır
cbinder

4

fork () geçerli işlemin bir kopyasını oluşturur ve yeni alt öğede yürütme fork () çağrısından hemen sonra başlar. Fork () işlevinden sonra, fork () işlevinin dönüş değeri dışında aynıdır. (Daha fazla ayrıntı için RTFM.) Bu iki işlem daha da farklı olabilir, biri paylaşılan dosya tanıtıcıları dışında biri diğerine müdahale edemez.

exec (), geçerli işlemi yenisiyle değiştirir. Fork () ile bir ilgisi yoktur, ancak bir exec () genellikle geçerli olanı değiştirmek yerine farklı bir alt süreç başlatmak olduğunda fork () 'u takip eder.


3

Arasındaki temel fark, fork()ve exec(), yani

fork()Sistem çağrısı Çalışmakta programın bir klon yaratır. Orijinal program fork () fonksiyon çağrısından sonraki kod satırıyla çalışmaya devam eder. Klon ayrıca bir sonraki kod satırında yürütülmeye başlar. İ aldığım o aşağıdaki koda bak http://timmurphy.org/2014/04/26/using-fork-in-cc-a-minimum-working-example/

#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
    printf("--beginning of program\n");
    int counter = 0;
    pid_t pid = fork();
    if (pid == 0)
    {
        // child process
        int i = 0;
        for (; i < 5; ++i)
        {
            printf("child process: counter=%d\n", ++counter);
        }
    }
    else if (pid > 0)
    {
        // parent process
        int j = 0;
        for (; j < 5; ++j)
        {
            printf("parent process: counter=%d\n", ++counter);
        }
    }
    else
    {
        // fork failed
        printf("fork() failed!\n");
        return 1;
    }
    printf("--end of program--\n");
    return 0;
}

Bu program ing'den önce sıfıra ayarlanmış bir sayaç değişkeni bildirir fork(). Çatal çağrısından sonra, paralel olarak çalışan iki işlemimiz var, her ikisi de kendi sayaç sürümünü arttırıyor. Her işlem tamamlanıp çıkılacaktır. Süreçler paralel ilerlediğinden, hangisinin önce biteceğini bilmenin hiçbir yolu yoktur. Bu programı çalıştırmak, aşağıda gösterilene benzer bir şey yazdıracak, ancak sonuçlar bir işlemden diğerine değişebilir.

--beginning of program
parent process: counter=1
parent process: counter=2
parent process: counter=3
child process: counter=1
parent process: counter=4
child process: counter=2
parent process: counter=5
child process: counter=3
--end of program--
child process: counter=4
child process: counter=5
--end of program--

exec()Sistem aramaların aile başka bir kod parçası ile bir sürecin şu anda yürütülen kodu yerine geçer. Süreç PID'sini korur, ancak yeni bir program haline gelir. Örneğin, aşağıdaki kodu göz önünde bulundurun:

#include <stdio.h> 
#include <unistd.h> 
main() {
 char program[80],*args[3];
 int i; 
printf("Ready to exec()...\n"); 
strcpy(program,"date"); 
args[0]="date"; 
args[1]="-u"; 
args[2]=NULL; 
i=execvp(program,args); 
printf("i=%d ... did it work?\n",i); 
} 

Bu program execvp(), kodunu tarih programıyla değiştirmek için işlevi çağırır . Kod exec1.c adlı bir dosyada depolanırsa, yürütülmesi aşağıdaki çıktıyı üretir:

Ready to exec()... 
Tue Jul 15 20:17:53 UTC 2008 

Program, exec exec () 'ye hazır satırını çıkarır. . . ‖ Ve execvp () işlevini çağırdıktan sonra, kodunu tarih programıyla değiştirir. Unutmayın çizgi -. . . işe yaramadı mı? görüntülenmez, çünkü bu noktada kod değiştirilmiştir. Bunun yerine, ―date -u.‖ yürütme çıktısını görüyoruz.


1

resim açıklamasını buraya girinfork():

Çalışan işlemin bir kopyasını oluşturur. Çalışan sürece üst süreç , yeni oluşturulan sürece alt süreç denir . İkisini ayırt etmenin yolu döndürülen değere bakmaktır:

  1. fork() Üst öğedeki alt işlemin işlem tanımlayıcısını (pid) döndürür

  2. fork() alt öğede 0 değerini döndürür.

exec():

Bir süreç içinde yeni bir süreç başlatır. Mevcut işleme yeni bir program yükler ve mevcut programı değiştirir.

fork()+ exec():

Yeni bir program başlatırken, öncelikle fork()yeni bir işlem oluşturmak ve daha sonra exec()çalıştırması gereken program ikili dosyasını (yani belleğe yükleyin ve yürütmek).

int main( void ) 
{
    int pid = fork();
    if ( pid == 0 ) 
    {
        execvp( "find", argv );
    }

    //Put the parent to sleep for 2 sec,let the child finished executing 
    wait( 2 );

    return 0;
}

0

Başlıca örneği anlamak fork()ve exec()kavramdır kabuk , kullanıcılar genellikle sistemle kabuğu içine giriş yaptıktan sonra yürütür o komut yorumlayıcı programı ilk kelimeyi yorumladığı komut satırında bir şekilde komut adı

Birçok komut için, kabuk çatalları ve alt işlem , komut satırında kalan sözcükleri komuta parametre olarak ele alan adla ilişkili komutu yürütür .

Kabuk komutları üç tip sağlar. Birincisi, bir komut, kaynak kodun derlenmesi ile üretilen nesne kodunu (örneğin bir C programı) içeren yürütülebilir bir dosya olabilir . İkinci olarak, bir komut bir dizi kabuk komut satırı içeren yürütülebilir bir dosya olabilir. Son olarak, bir komut dahili bir kabuk komutu olabilir. ( Cd , ls vb. Yürütülebilir bir dosya yerine )

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.