Perbedaan antara garpu dan exec

199

Apa perbedaan antara forkdan exec?

Sashi
sumber
3
Ringkasan yang baik dan terperinci dari fungsi fork, exec, dan fungsi kontrol proses lainnya ada di yolinux.com/TUTORIALS/ForkExecProcesses.html
Jonathan Fingland
9
@Justin, karena kita ingin SO untuk menjadi yang tempat untuk pergi untuk pemrograman pertanyaan.
paxdiablo
4
@ Polaris878: oh, ya sekarang! : D
Janusz Lenar
jadi forkpada dasarnya kloning: O
Sebastian Hojas

Jawaban:

364

Penggunaan forkdan execcontoh semangat UNIX dalam hal ini memberikan cara yang sangat sederhana untuk memulai proses baru.

The forkpanggilan pada dasarnya membuat duplikat dari proses saat ini, identik dalam hampir segala hal. Tidak semuanya disalin (misalnya, batasan sumber daya dalam beberapa implementasi) tetapi idenya adalah untuk membuat salinan sedekat mungkin.

Proses baru (anak) mendapatkan ID proses (PID) yang berbeda dan memiliki PID dari proses lama (induk) sebagai PID induknya (PPID). Karena kedua proses sekarang menjalankan kode yang persis sama, mereka dapat mengetahui yang mana dengan kode pengembalian fork- anak mendapat 0, orang tua mendapat PID anak. Ini semua, tentu saja, dengan asumsi forkpanggilan berfungsi - jika tidak, tidak ada anak yang dibuat dan orang tua mendapat kode kesalahan.

The execpanggilan adalah cara untuk dasarnya menggantikan proses saat ini seluruh dengan program baru. Itu memuat program ke dalam ruang proses saat ini dan menjalankannya dari titik masuk.

Jadi, forkdan execsering digunakan secara berurutan untuk menjalankan program baru sebagai anak dari proses saat ini. Kerang biasanya melakukan ini setiap kali Anda mencoba menjalankan program seperti find- garpu garpu, kemudian anak memuat findprogram ke dalam memori, mengatur semua argumen baris perintah, I / O standar dan sebagainya.

Tapi mereka tidak diharuskan untuk digunakan bersama. Sangat dapat diterima untuk suatu program untuk forkdirinya sendiri tanpa exec, jika, misalnya, program tersebut berisi kode induk dan anak (Anda harus berhati-hati dengan apa yang Anda lakukan, setiap implementasi mungkin memiliki batasan). Ini digunakan cukup banyak (dan masih) untuk daemon yang hanya mendengarkan pada port TCP dan forksalinannya sendiri untuk memproses permintaan tertentu sementara orangtua kembali mendengarkan.

Demikian pula, program yang tahu mereka sudah selesai dan hanya ingin menjalankan program lain tidak perlu fork, execdan kemudian waituntuk anak. Mereka hanya dapat memuat anak langsung ke ruang proses mereka.

Beberapa implementasi UNIX telah dioptimalkan forkyang menggunakan apa yang mereka sebut copy-on-write. Ini adalah trik untuk menunda penyalinan ruang proses forkhingga program mencoba mengubah sesuatu di ruang itu. Ini berguna untuk program-program yang hanya menggunakan forkdan tidak execkarena mereka tidak perlu menyalin seluruh ruang proses.

Jika exec ini disebut berikut fork(dan ini adalah apa yang terjadi kebanyakan), yang menyebabkan tulis ke ruang proses dan kemudian disalin untuk proses anak.

Perhatikan bahwa ada seluruh keluarga execpanggilan ( execl, execle, execvedan sebagainya), tetapi execdalam konteks di sini berarti salah satu dari mereka.

Diagram berikut menggambarkan fork/execoperasi khas di mana bashshell digunakan untuk mendaftar direktori dengan lsperintah:

+--------+
| 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
paxdiablo
sumber
52

fork()membagi proses saat ini menjadi dua proses. Atau dengan kata lain, program linier Anda yang mudah dipikirkan tiba-tiba menjadi dua program terpisah yang menjalankan satu kode:

 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
 }

Ini bisa meledakkan pikiran Anda. Sekarang Anda memiliki satu kode dengan kondisi identik yang dijalankan oleh dua proses. Proses anak mewarisi semua kode dan memori dari proses yang baru saja dibuat itu, termasuk mulai dari mana fork()panggilan baru saja tinggalkan. Satu-satunya perbedaan adalah fork()kode pengembalian untuk memberi tahu Anda jika Anda adalah orang tua atau anak. Jika Anda adalah orang tua, nilai kembali adalah id anak.

execsedikit lebih mudah untuk dipahami, Anda hanya mengatakan execuntuk mengeksekusi proses menggunakan target yang dapat dieksekusi dan Anda tidak memiliki dua proses menjalankan kode yang sama atau mewarisi keadaan yang sama. Seperti kata @Steve Hawkins, execdapat digunakan setelah Anda forkmengeksekusi dalam proses saat ini target yang dapat dieksekusi.

Doug T.
sumber
6
ada juga kondisi ketika pid < 0dan fork()panggilan gagal
Jonathan Fingland
3
Itu sama sekali tidak mengejutkan saya :-) Sepotong kode yang dieksekusi oleh dua proses terjadi setiap kali shared library atau DLL digunakan.
paxdiablo
31

Saya pikir beberapa konsep dari "Advanced Unix Programming" oleh Marc Rochkind sangat membantu dalam memahami peran yang berbeda dari fork()/ exec(), terutama untuk seseorang yang terbiasa dengan CreateProcess()model Windows :

Sebuah Program adalah kumpulan instruksi dan data yang disimpan dalam file biasa pada disk. (dari 1.1.2 Program, Proses, dan Utas)

.

Untuk menjalankan program, kernel pertama kali diminta untuk membuat proses baru , yang merupakan lingkungan di mana suatu program dijalankan. (juga dari 1.1.2 Program, Proses, dan Utas)

.

Tidak mungkin untuk memahami panggilan sistem eksekutif atau garpu tanpa sepenuhnya memahami perbedaan antara suatu proses dan program. Jika ketentuan ini baru bagi Anda, Anda mungkin ingin kembali dan meninjau Bagian 1.1.2. Jika Anda siap untuk melanjutkan sekarang, kami akan meringkas perbedaan dalam satu kalimat: Proses adalah lingkungan eksekusi yang terdiri dari segmen instruksi, data pengguna, dan data sistem, serta banyak sumber daya lain yang diperoleh saat runtime , sedangkan program adalah file yang berisi instruksi dan data yang digunakan untuk menginisialisasi instruksi dan segmen data pengguna dari suatu proses. (dari 5.3 execPanggilan Sistem)

Setelah Anda memahami perbedaan antara program dan proses, perilaku fork()dan exec()fungsi dapat diringkas sebagai:

  • fork() membuat duplikat dari proses saat ini
  • exec() mengganti program dalam proses saat ini dengan program lain

(pada dasarnya ini adalah versi 'for dummies' yang disederhanakan dari jawaban paxdiablo yang jauh lebih terperinci )

Michael Burr
sumber
29

Fork membuat salinan proses panggilan. umumnya mengikuti struktur masukkan deskripsi gambar di sini

int cpid = fork( );

if (cpid = = 0) 
{

  //child code

  exit(0);

}

//parent code

wait(cpid);

// end

(untuk teks proses anak (kode), data, tumpukan sama dengan proses panggilan) proses anak mengeksekusi kode dalam blok if.

EXEC menggantikan proses saat ini dengan kode, data, tumpukan proses baru. umumnya mengikuti struktur masukkan deskripsi gambar di sini

int cpid = fork( );

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

  exec(foo);

  exit(0);    
}

//parent code

wait(cpid);

// end

(setelah exec panggil unix kernel menghapus teks proses anak, data, tumpukan dan isi dengan teks / data terkait proses foo) sehingga proses anak adalah dengan kode yang berbeda (kode foo {tidak sama dengan induk})

Sandesh Kobal
sumber
1
Ini agak tidak berhubungan dengan pertanyaan tetapi bukankah kode ini di atas menyebabkan kondisi lomba jika proses anak kebetulan menyelesaikan kode itu terlebih dahulu? Dalam hal ini, proses orangtua akan berkeliaran selamanya menunggu anak untuk mengakhiri sendiri, kan?
stdout
7

Mereka digunakan bersama untuk membuat proses anak baru. Pertama, panggilan forkmenciptakan salinan dari proses saat ini (proses anak). Kemudian, execdipanggil dari dalam proses anak untuk "mengganti" salinan proses induk dengan proses baru.

Prosesnya kira-kira seperti ini:

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
}
Steve Hawkins
sumber
2
Pada baris ke-7 disebutkan bahwa fungsi exec () menciptakan proses anak .. Apakah benar demikian karena fork () telah membuat proses anak dan exec () memanggil hanya menggantikan program dari proses baru yang baru saja dibuat
cbinder
4

fork () membuat salinan dari proses saat ini, dengan eksekusi pada anak baru dimulai dari setelah panggilan fork (). Setelah fork (), mereka identik, kecuali untuk nilai balik dari fungsi fork (). (RTFM untuk lebih jelasnya.) Kedua proses kemudian dapat menyimpang lebih jauh, dengan satu tidak dapat mengganggu yang lain, kecuali mungkin melalui semua file menangani.

exec () menggantikan proses saat ini dengan yang baru. Ini tidak ada hubungannya dengan fork (), kecuali bahwa exec () sering mengikuti fork () ketika yang diinginkan adalah meluncurkan proses anak yang berbeda, daripada menggantikan yang sekarang.

Warren Young
sumber
3

Perbedaan utama antara fork()dan exec()adalah,

The fork()system call menciptakan klon dari program yang sedang berjalan. Program asli melanjutkan eksekusi dengan baris kode berikutnya setelah panggilan fungsi fork (). Klon juga memulai eksekusi pada baris kode berikutnya. Lihatlah kode berikut yang saya dapatkan dari 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;
}

Program ini mendeklarasikan variabel penghitung, atur ke nol, sebelum fork()ing. Setelah panggilan fork, kami memiliki dua proses yang berjalan secara paralel, keduanya menambah versi counter mereka sendiri. Setiap proses akan berjalan sampai selesai dan keluar. Karena proses berjalan secara paralel, kami tidak memiliki cara untuk mengetahui mana yang akan selesai terlebih dahulu. Menjalankan program ini akan mencetak sesuatu yang mirip dengan yang ditunjukkan di bawah ini, meskipun hasilnya dapat bervariasi dari satu lari ke yang berikutnya.

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

The exec()keluarga panggilan sistem menggantikan kode saat ini melaksanakan proses dengan sepotong kode. Proses mempertahankan PID-nya tetapi menjadi program baru. Misalnya, pertimbangkan kode berikut:

#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); 
} 

Program ini memanggil execvp()fungsi untuk mengganti kodenya dengan program tanggal. Jika kode disimpan dalam file bernama exec1.c, maka mengeksekusinya menghasilkan output berikut:

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

Program mengeluarkan baris ―Ready to exec (). . . ‖ Dan setelah memanggil fungsi execvp (), ganti kodenya dengan program tanggal. Perhatikan bahwa garis -. . . tidak berfungsi‖ tidak ditampilkan, karena pada saat itu kode telah diganti. Sebagai gantinya, kami melihat output dari mengeksekusi ―date -u.‖

Abdulhakim Zeinu
sumber
1

masukkan deskripsi gambar di sinifork():

Ini menciptakan salinan proses yang sedang berjalan. Proses yang berjalan disebut proses induk & proses yang baru dibuat disebut proses anak . Cara untuk membedakan keduanya adalah dengan melihat nilai yang dikembalikan:

  1. fork() mengembalikan pengidentifikasi proses (pid) dari proses anak di orang tua

  2. fork() mengembalikan 0 pada anak.

exec():

Ini memulai proses baru dalam suatu proses. Itu memuat program baru ke dalam proses saat ini, menggantikan yang sudah ada.

fork()+ exec():

Ketika meluncurkan program baru adalah pertama fork(), menciptakan proses baru, dan kemudian exec()(yaitu memuat ke dalam memori dan mengeksekusi) program biner yang seharusnya dijalankan.

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;
}
Yogeesh HT
sumber
0

Contoh utama untuk memahami fork()dan exec()konsep adalah shell , program interpreter perintah yang biasanya dijalankan pengguna setelah masuk ke sistem. Shell mengartikan kata pertama dari baris perintah sebagai nama perintah

Untuk banyak perintah, shell garpu dan proses anak eksekutif perintah terkait dengan nama mengobati kata-kata yang tersisa pada baris perintah sebagai parameter untuk perintah.

The shell memungkinkan tiga jenis perintah. Pertama, perintah dapat berupa file yang dapat dieksekusi yang berisi kode objek yang dihasilkan oleh kompilasi kode sumber (misalnya program C). Kedua, perintah bisa berupa file yang dapat dieksekusi yang berisi urutan baris perintah shell. Akhirnya, sebuah perintah dapat berupa perintah shell internal (bukan file yang dapat dieksekusi ex-> cd , ls dll.)

krpra
sumber