Perbedaan antara fork (), vfork (), exec () dan clone ()

197

Saya mencari untuk menemukan perbedaan antara keempat ini di Google dan saya berharap akan ada sejumlah besar informasi tentang ini, tetapi benar-benar tidak ada perbandingan yang kuat antara keempat panggilan.

Saya mulai mencoba mengkompilasi tampilan dasar sekilas perbedaan antara panggilan sistem dan inilah yang saya dapatkan. Apakah semua informasi ini benar / apakah saya melewatkan sesuatu yang penting?

Fork : Panggilan garpu pada dasarnya membuat duplikat dari proses saat ini, identik dalam hampir setiap cara (tidak semuanya disalin, misalnya, batas 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 induk PID (PPID). Karena kedua proses sekarang menjalankan kode yang persis sama, mereka dapat mengetahui yang mana dengan kode pengembalian garpu - anak mendapat 0, orang tua mendapat PID anak. Ini semua, tentu saja, dengan asumsi panggilan garpu berfungsi - jika tidak, tidak ada anak yang dibuat dan orang tua mendapat kode kesalahan.

Vfork: Perbedaan mendasar antara vfork dan fork adalah ketika proses baru dibuat dengan vfork (), proses induk ditangguhkan sementara, dan proses anak mungkin meminjam ruang alamat orangtua. Keadaan aneh ini berlanjut hingga proses anak keluar, atau panggilan execve (), di mana proses induk berlanjut.

Ini berarti bahwa proses turunan vfork () harus berhati-hati untuk menghindari variabel tak terduga memodifikasi proses induk. Secara khusus, proses anak tidak boleh kembali dari fungsi yang berisi panggilan vfork (), dan itu tidak boleh memanggil keluar () (jika perlu keluar, harus menggunakan _exit (); sebenarnya, ini juga berlaku untuk anak dari garpu normal ()).

Exec :Panggilan exec adalah cara untuk mengganti seluruh proses saat ini dengan program baru. Itu memuat program ke dalam ruang proses saat ini dan menjalankannya dari titik masuk. exec () menggantikan proses saat ini dengan executable yang ditunjuk oleh fungsi. Kontrol tidak pernah kembali ke program semula kecuali ada kesalahan exec ().

Clone :Clone, seperti garpu, menciptakan proses baru. Tidak seperti garpu, panggilan ini memungkinkan proses anak untuk berbagi bagian dari konteks eksekusi dengan proses panggilan, seperti ruang memori, tabel deskriptor file, dan tabel penangan sinyal.

Ketika proses anak dibuat dengan klon, ia mengeksekusi aplikasi fungsi fn (arg). (Ini berbeda dari garpu, di mana eksekusi berlanjut pada anak dari titik panggilan garpu asli.) Argumen fn adalah penunjuk ke fungsi yang dipanggil oleh proses anak pada awal pelaksanaannya. Argumen argumen diteruskan ke fungsi fn.

Ketika aplikasi fungsi fn (arg) kembali, proses anak berakhir. Integer yang dikembalikan oleh fn adalah kode keluar untuk proses anak. Proses anak juga dapat berakhir secara eksplisit dengan memanggil keluar (2) atau setelah menerima sinyal fatal.

Informasi yang diperoleh:

Terima kasih telah meluangkan waktu untuk membaca ini! :)

pengguna476033
sumber
2
Mengapa vfork tidak boleh memanggil exit ()? Atau tidak kembali? Tidak keluar () cukup gunakan _exit ()? Saya juga mencoba untuk memahami :)
LazerSharks
2
@Gnuey: karena berpotensi (jika diimplementasikan secara berbeda dari fork(), yang ada di Linux, dan mungkin semua BSD) meminjam ruang alamat orang tuanya. Apa pun yang dilakukannya, selain menelepon execve()atau _exit(), memiliki potensi besar untuk mengacaukan orangtua. Secara khusus, exit()panggilan atexit()penangan dan "finalizer" lainnya, misalnya: ia menyiram aliran stdio. Kembali dari seorang vfork()anak akan berpotensi (peringatan yang sama seperti sebelumnya) mengacaukan tumpukan orangtua.
ninjalj
Saya bertanya-tanya apa yang terjadi pada utas proses induk; Apakah semuanya dikloning atau hanya utas yang menyebut panggilan forksyscall?
Mohammad Jafar Mashhadi
@LazerSharks vfork menghasilkan proses seperti di mana memori dibagikan tanpa perlindungan copy-on-write, sehingga melakukan hal-hal stack dapat merusak proses induk.
Jasen

Jawaban:

159
  • vfork()adalah optimasi usang. Sebelum manajemen memori yang baik, fork()buat salinan lengkap dari memori orang tua, jadi itu cukup mahal. karena dalam banyak kasus a fork()diikuti oleh exec(), yang membuang peta memori saat ini dan membuat yang baru, itu adalah pengeluaran yang tidak perlu. Saat ini, fork()jangan menyalin memori; itu hanya ditetapkan sebagai "copy on write", jadi fork()+ exec()seefisien vfork()+ exec().

  • clone()adalah syscall yang digunakan oleh fork(). dengan beberapa parameter, itu menciptakan proses baru, dengan yang lain, itu menciptakan utas. perbedaan di antara mereka hanyalah struktur data (ruang memori, keadaan prosesor, tumpukan, PID, file terbuka, dll) yang dibagikan atau tidak.

Javier
sumber
22
vforkmenghindari kebutuhan untuk sementara melakukan lebih banyak memori hanya supaya seseorang dapat mengeksekusi exec, dan itu masih lebih efisien daripada fork, bahkan jika tidak hampir setinggi gelar. Dengan demikian, seseorang dapat menghindari keharusan terlalu banyak memori sehingga program besar sebong bisa menelurkan proses anak. Jadi, bukan hanya peningkatan kinerja, tetapi mungkin membuatnya layak sama sekali.
Deduplicator
5
Sebenarnya, saya telah menyaksikan sendiri bagaimana fork () jauh dari murah ketika RSS Anda besar. Saya kira ini karena kernel masih harus menyalin semua tabel halaman.
Martina Ferrari
4
Itu harus menyalin semua tabel halaman, mengatur semua copy-on-write memori yang dapat ditulis dalam kedua proses , flush TLB, dan kemudian harus mengembalikan semua perubahan ke induk (dan flush TLB lagi) pada exec.
zwol
3
vfork masih berguna di cygwin (dll kernel emulating, yang berjalan di atas Microsoft Windows). cygwin tidak dapat mengimplementasikan garpu yang efisien, karena OS yang mendasarinya tidak memilikinya.
ctrl-alt-delor
80
  • execve() mengganti gambar yang dapat dieksekusi saat ini dengan yang lain diambil dari file yang dapat dieksekusi.
  • fork() menciptakan proses anak.
  • vfork()adalah versi yang dioptimalkan secara historis fork(), dimaksudkan untuk digunakan ketika execve()dipanggil langsung setelahnya fork(). Ternyata bekerja dengan baik dalam sistem non-MMU (di mana fork()tidak dapat bekerja secara efisien) dan ketika fork()proses dengan jejak memori besar untuk menjalankan beberapa program kecil (pikirkan JavaRuntime.exec() ). POSIX telah membuat standar posix_spawn()untuk menggantikan dua penggunaan yang lebih modern ini vfork().
  • posix_spawn() tidak sama dengan a fork()/execve() , dan juga memungkinkan beberapa juggling fd di antaranya. Itu seharusnya diganti fork()/execve(), terutama untuk platform non-MMU.
  • pthread_create() membuat utas baru.
  • clone()adalah panggilan khusus Linux, yang dapat digunakan untuk mengimplementasikan apa saja dari fork()ke pthread_create(). Ini memberi banyak kontrol. Terinspirasi padarfork() .
  • rfork()adalah panggilan khusus Plan-9. Seharusnya panggilan umum, memungkinkan beberapa derajat berbagi, antara proses penuh dan utas.
ninjalj
sumber
2
Terima kasih telah menambahkan lebih banyak informasi daripada yang diminta, itu membantu saya menghemat waktu saya
Neeraj
5
Plan 9 menggoda.
JJ
1
Bagi mereka yang tidak dapat mengingat apa arti MMU: "Unit manajemen memori" - bacaan lebih lanjut di Wikipedia
mgarey
43
  1. fork()- Membuat proses anak baru, yang merupakan salinan lengkap dari proses induk. Proses anak dan orang tua menggunakan ruang alamat virtual yang berbeda, yang awalnya diisi oleh halaman memori yang sama. Kemudian, ketika kedua proses dieksekusi, ruang alamat virtual mulai berbeda semakin banyak, karena sistem operasi melakukan penyalinan malas dari halaman memori yang sedang ditulis oleh salah satu dari kedua proses ini dan memberikan salinan independen dari halaman yang dimodifikasi dari memori untuk setiap proses. Teknik ini disebut Copy-On-Write (COW).
  2. vfork()- membuat proses anak baru, yang merupakan salinan "cepat" dari proses induk. Berbeda dengan panggilan sistem fork(), proses anak dan orang tua berbagi ruang alamat virtual yang sama. CATATAN! Menggunakan ruang alamat virtual yang sama, orang tua dan anak menggunakan tumpukan yang sama, penunjuk tumpukan dan penunjuk instruksi, seperti dalam kasus klasik fork()! Untuk mencegah gangguan yang tidak diinginkan antara orang tua dan anak, yang menggunakan tumpukan yang sama, eksekusi proses induk dibekukan sampai anak akan memanggil salah satu exec()(membuat ruang alamat virtual baru dan transisi ke tumpukan yang berbeda) atau _exit()(penghentian proses eksekusi ). vfork()adalah optimalisasi fork()untuk model "fork-and-exec". Ini dapat dilakukan 4-5 kali lebih cepat daripada fork(), karena tidak sepertifork() (bahkan dengan SAP tetap diingat), implementasivfork() system call tidak termasuk pembuatan ruang alamat baru (alokasi dan pengaturan direktori halaman baru).
  3. clone()- menciptakan proses anak baru. Berbagai parameter panggilan sistem ini, menentukan bagian mana dari proses induk harus disalin ke proses anak dan bagian mana yang akan dibagikan di antara mereka. Sebagai hasilnya, pemanggilan sistem ini dapat digunakan untuk membuat semua jenis entitas eksekusi, mulai dari utas dan finishing dengan proses yang sepenuhnya independen. Bahkan, clone()system call adalah basis yang digunakan untuk implementasi pthread_create()dan semua keluargafork() panggilan sistem.
  4. exec()- me-reset semua memori dari proses, memuat dan mem-parsing biner yang dapat dieksekusi, mengatur stack baru dan melewati kontrol ke titik masuknya executable yang dimuat. Panggilan sistem ini tidak pernah mengembalikan kontrol ke penelepon dan berfungsi untuk memuat program baru ke proses yang sudah ada. Panggilan sistem ini dengan panggilan fork()sistem bersama-sama membentuk model manajemen proses UNIX klasik yang disebut "fork-and-exec".
ZarathustrA
sumber
2
Perhatikan bahwa persyaratan BSD dan POSIX untuk vforkbegitu lemah sehingga akan sah untuk membuat vforksinonim dari fork(dan POSIX.1-2008 menghapus vforksepenuhnya dari spesifikasi). Jika Anda menguji kode Anda pada sistem yang mensinonimkannya (mis. Sebagian besar pasca-4.4 BSD selain dari NetBSD, kernel Linux pra-2.2.0-pre6, dll.), Itu dapat berfungsi bahkan jika Anda melanggar vforkkontrak, kemudian meledak jika Anda menjalankannya di tempat lain. Beberapa dari mereka yang mensimulasikan dengan fork(misalnya OpenBSD) masih menjamin orang tua tidak kembali berjalan sampai anak execs atau _exits. Ini sangat non-portabel.
ShadowRanger
2
mengenai kalimat terakhir dari poin ke-3 Anda: Saya perhatikan di Linux menggunakan strace bahwa meskipun pembungkus glibc untuk fork () memanggil syscall klon, pembungkus untuk vfork () memanggil vfork syscall
ilstam
7

Garpu (), vfork () dan klon () semuanya memanggil do_fork () untuk melakukan pekerjaan nyata, tetapi dengan parameter yang berbeda.

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.

Untuk garpu, anak dan ayah memiliki tabel halaman VM independen, tetapi karena efisiensi, garpu tidak akan benar-benar menyalin halaman, itu hanya mengatur semua halaman yang dapat ditulis untuk dibaca hanya untuk proses anak. Jadi ketika proses anak ingin menulis sesuatu di halaman itu, pengecualian halaman terjadi dan kernel akan mengalokasikan halaman baru yang dikloning dari halaman lama dengan izin menulis. Itu disebut "copy on write".

Untuk vfork, memori virtual persis oleh anak dan ayah --- hanya karena itu, ayah dan anak tidak dapat bangun secara bersamaan karena mereka akan saling mempengaruhi. Jadi ayah akan tidur di akhir "do_fork ()" dan bangun ketika child call keluar () atau execve () karena kemudian akan memiliki tabel halaman baru. Berikut adalah kode (dalam do_fork ()) yang ayah tidur.

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

Berikut adalah kode (dalam mm_release () dipanggil oleh exit () dan execve ()) yang membangunkan ayah.

up(tsk->p_opptr->vfork_sem);

Untuk sys_clone (), ini lebih fleksibel karena Anda dapat memasukkan clone_flags apa pun ke dalamnya. Jadi pthread_create () panggil panggilan sistem ini dengan banyak clone_flags:

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

Ringkasan: fork (), vfork () dan clone () akan membuat proses anak dengan berbagai sumber daya berbagi dengan proses ayah. Kita juga dapat mengatakan vfork () dan clone () dapat membuat utas (sebenarnya mereka adalah proses karena mereka memiliki task_struct independen) karena mereka berbagi tabel halaman VM dengan proses ayah.

pengguna991800
sumber
-4

di fork (), proses anak atau orang tua akan mengeksekusi berdasarkan pemilihan cpu .. Tapi di vfork (), pasti anak akan mengeksekusi terlebih dahulu. setelah anak dihentikan, orang tua akan mengeksekusi.

Raj Kannan B.
sumber
3
Salah. vfork()hanya bisa diimplementasikan sebagai fork().
ninjalj
setelah AnyFork (), tidak ditentukan siapa yang menjalankan orang tua / anak pertama.
AjayKumarBasuthkar
5
@ Raj: Anda memiliki beberapa kesalahpahaman konseptual jika Anda berpikir setelah melakukan forking ada gagasan tersirat dari urutan serial. Forking menciptakan proses baru dan kemudian mengembalikan kontrol ke kedua proses (masing-masing mengembalikan yang berbeda pid) - sistem operasi dapat menjadwalkan proses baru untuk berjalan secara paralel jika hal seperti itu masuk akal (misalnya, beberapa prosesor). Jika karena alasan tertentu Anda memerlukan proses ini untuk dieksekusi dalam urutan serial tertentu, maka Anda perlu sinkronisasi tambahan yang tidak disediakan forking; terus terang, Anda mungkin bahkan tidak ingin garpu di tempat pertama.
Andon M. Coleman
Sebenarnya @AjayKumarBasuthkar dan @ninjalj, Anda berdua salah. dengan vfork(), anak berlari pertama. Itu ada di halaman manual; eksekusi orang tua ditunda sampai anak meninggal atau execs. Dan ninjalj mencari kode sumber kernel. Tidak ada cara untuk menerapkan vfork()sebagai fork()karena mereka melewati argumen yang berbeda untuk do_fork()dalam kernel. Anda dapat, bagaimanapun, menerapkan vforkdengan clonesyscall
Zac Wimer
@ZacWimer: lihat komentar ShadowRanger ke jawaban lain stackoverflow.com/questions/4856255/... Linux lama memang menyinkronkannya, karena tampaknya BSD selain NetBSD (yang cenderung porting ke banyak sistem non-MMU) lakukan. Dari manual Linux: Di 4.4BSD ia dibuat identik dengan fork (2) tetapi NetBSD memperkenalkannya lagi; lihat ⟨netbsd.org/Documentation/kernel/vfork.html⟩ . Di Linux, sudah setara dengan fork (2) hingga 2.2.0-pre6 atau lebih.
ninjalj