Mengapa kita perlu bercabang untuk menciptakan proses baru?

95

Di Unix setiap kali kita ingin membuat proses baru, kita memotong proses saat ini, membuat proses anak baru yang persis sama dengan proses induk; kemudian kami melakukan panggilan sistem exec untuk mengganti semua data dari proses induk dengan yang untuk proses baru.

Mengapa kita membuat salinan dari proses induk di tempat pertama dan tidak membuat proses baru secara langsung?

sarthak
sumber
2
Lihat juga unix.stackexchange.com/questions/31118/…
Ellen Spertus

Jawaban:

61

Jawaban singkatnya adalah, forkada di Unix karena mudah masuk ke sistem yang ada saat itu, dan karena sistem pendahulunya di Berkeley menggunakan konsep garpu.

Dari Evolusi Sistem Berbagi Waktu Unix (teks yang relevan telah disorot ):

Kontrol proses dalam bentuk modernnya dirancang dan diimplementasikan dalam beberapa hari. Sungguh menakjubkan betapa mudahnya dipasang ke sistem yang ada; pada saat yang sama mudah untuk melihat bagaimana beberapa fitur yang sedikit tidak biasa dari desain hadir tepat karena mereka mewakili perubahan kecil dan mudah dikodekan dengan apa yang ada . Contoh yang baik adalah pemisahan fungsi fork dan exec. Model yang paling umum untuk penciptaan proses baru melibatkan menentukan program untuk dieksekusi; di Unix, proses bercabang terus menjalankan program yang sama dengan induknya sampai ia menjalankan eksekutif eksplisit. Pemisahan fungsi-fungsi ini tentu saja tidak unik untuk Unix, dan pada kenyataannya ia hadir dalam sistem pembagian waktu Berkeley, yang dikenal oleh Thompson.. Namun, tampaknya masuk akal untuk menganggap bahwa itu ada di Unix terutama karena kemudahan yang garpu dapat diimplementasikan tanpa mengubah banyak hal lain . Sistem sudah menangani banyak (yaitu dua) proses; ada tabel proses, dan proses-proses itu ditukar antara memori utama dan disk. Implementasi awal dari garpu hanya diperlukan

1) Perluasan tabel proses

2) Penambahan fork call yang menyalin proses saat ini ke area swap disk, menggunakan primitif swap IO yang sudah ada, dan membuat beberapa penyesuaian pada tabel proses.

Bahkan, panggilan fork PDP-7 membutuhkan tepat 27 baris kode rakitan. Tentu saja, perubahan lain dalam sistem operasi dan program pengguna diperlukan, dan beberapa di antaranya agak menarik dan tidak terduga. Tetapi fork-exec gabungan akan jauh lebih rumit , jika saja karena exec tidak ada; fungsinya sudah dilakukan, menggunakan IO eksplisit, oleh shell.

Sejak kertas itu, Unix telah berevolusi. forkdiikuti execbukan lagi satu-satunya cara untuk menjalankan program.

  • vfork diciptakan untuk menjadi garpu yang lebih efisien untuk kasus di mana proses baru bermaksud untuk melakukan exec tepat setelah garpu. Setelah melakukan vfork, proses induk dan anak berbagi ruang data yang sama, dan proses induk ditangguhkan hingga proses anak mengeksekusi program atau keluar.

  • posix_spawn membuat proses baru dan mengeksekusi file dalam satu panggilan sistem. Dibutuhkan banyak parameter yang memungkinkan Anda secara selektif berbagi file terbuka pemanggil dan menyalin disposisi sinyalnya dan atribut lainnya ke proses baru.

Tandai Plotnick
sumber
5
Jawaban yang bagus tapi saya akan menambahkan bahwa vfork tidak boleh digunakan lagi. Perbedaan kinerja sekarang marjinal dan penggunaannya bisa berbahaya. Lihat pertanyaan SO ini stackoverflow.com/questions/4856255/…, situs ini ewontfix.com/7 , dan "Advanced Unix Programming" halaman 299 tentang vfork
Raphael Ahrens
4
Intrik (pengaturan struktur data) yang diperlukan untuk digunakan posix_spawn()untuk melakukan pekerjaan post-fork replumbing yang sama yang dapat dilakukan dengan mudah menggunakan fork()dan kode inline membuat argumen yang meyakinkan untuk fork()menjadi lebih mudah digunakan.
Jonathan Leffler
34

[Aku akan mengulangi sebagian dari jawabanku dari sini .]

Mengapa tidak hanya memiliki perintah yang menciptakan proses baru dari awal? Bukankah tidak masuk akal dan tidak efisien untuk menyalin satu yang hanya akan segera diganti?

Bahkan, itu mungkin tidak seefisien karena beberapa alasan:

  1. "Copy" yang diproduksi oleh fork()adalah sedikit abstraksi, karena kernel menggunakan copy-on-write sistem ; semua yang benar-benar harus dibuat adalah peta memori virtual. Jika salinan kemudian segera memanggil exec(), sebagian besar data yang akan disalin jika telah dimodifikasi oleh aktivitas proses tidak pernah benar-benar harus disalin / dibuat karena proses tidak melakukan apa pun yang memerlukan penggunaannya.

  2. Berbagai aspek penting dari proses anak (misalnya, lingkungannya) tidak harus diduplikasi secara individual atau ditetapkan berdasarkan analisis konteks yang kompleks, dll. Mereka hanya diasumsikan sama dengan proses pemanggilan, dan ini adalah sistem yang cukup intuitif yang kita kenal.

Untuk menjelaskan # 1 sedikit lebih jauh, memori yang "disalin" tetapi tidak pernah diakses tidak pernah benar-benar disalin, setidaknya dalam banyak kasus. Pengecualian dalam konteks ini mungkin jika Anda melakukan proses bercabang, maka proses induk harus keluar sebelum anak diganti dengan sendiri exec(). Saya katakan mungkin karena banyak dari orang tua bisa di-cache jika ada memori bebas yang cukup, dan saya tidak yakin sejauh mana ini akan dieksploitasi (yang akan tergantung pada implementasi OS).

Tentu saja, itu tidak di permukaan membuat menggunakan salinan lebih efisien daripada menggunakan batu tulis kosong - kecuali "batu tulis kosong" secara harfiah bukan apa-apa, dan harus melibatkan alokasi. Sistem dapat memiliki templat proses kosong / baru generik yang disalin dengan cara yang sama, 1 tetapi kemudian tidak akan benar-benar menyimpan apa pun dibandingkan garpu copy-on-write. Jadi # 1 hanya menunjukkan bahwa menggunakan proses kosong "baru" tidak akan lebih efisien.

Butir # 2 menjelaskan mengapa menggunakan garpu kemungkinan lebih efisien. Lingkungan anak diwarisi dari orang tuanya, bahkan jika itu adalah eksekusi yang sama sekali berbeda. Misalnya, jika proses induk adalah shell, dan anak itu browser web, $HOMEmasih sama untuk keduanya, tetapi karena keduanya kemudian dapat mengubahnya, ini harus dua salinan terpisah. Yang ada di anak diproduksi oleh aslinya fork().

1. Strategi yang mungkin tidak masuk akal secara literal, tetapi poin saya adalah menciptakan sebuah proses melibatkan lebih dari menyalin gambar itu ke dalam memori dari disk.

goldilocks
sumber
3
Meskipun kedua poin tersebut benar, tidak ada yang mendukung mengapa metode forking dipilih alih-alih mengembalikan proses baru dari yang dapat dieksekusi.
SkyDan
3
Saya pikir ini menjawab pertanyaan. Garpu digunakan karena, dalam kasus di mana membuat proses baru adalah cara yang paling efisien, biaya menggunakan garpu malah sepele (kemungkinan kurang dari 1% dari biaya pembuatan proses). Di sisi lain, ada banyak tempat di mana garpu secara dramatis lebih efisien atau jauh lebih sederhana dari API (seperti menangani file menangani). Keputusan yang dibuat Unix adalah hanya mendukung satu API, membuat spesifikasinya lebih sederhana.
Cort Ammon
1
@SkyDan Anda benar, ini lebih merupakan jawaban mengapa tidak daripada mengapa , yang Mark Plotnick jawab lebih langsung - yang saya artikan bukan hanya bahwa ini adalah pilihan termudah, tetapi juga bahwa itu mungkin yang paling efisien pilihan (menurut kutipan Dennis Richie: "panggilan garpu PDP-7 membutuhkan tepat 27 jalur perakitan ... exec karena itu tidak ada; fungsinya sudah dilakukan"). Jadi ini "mengapa tidak" benar-benar merenung tentang dua strategi di mana satu dangkal tampak lebih sederhana dan lebih efisien, ketika mungkin tidak (saksikan nasib meragukan ...
goldilocks
1
Goldilocks benar. Ada situasi di mana forking dan memodifikasi lebih murah daripada membuat yang baru dari awal. Contoh paling ekstrem, tentu saja, adalah kapan pun Anda menginginkan perilaku garpu itu sendiri. fork()dapat sangat cepat (seperti yang disebutkan GL, pada urutan 27 jalur perakitan). Melihat ke arah lain, jika Anda ingin "membuat proses dari awal," fork()harganya hanya sedikit lebih dari mulai dari proses yang dibuat kosong (27 baris perakitan + biaya penutupan pegangan file). Jadi, forkmenangani kedua garpu dan membuat dengan baik, sementara createhanya bisa menangani membuat dengan baik.
Cort Ammon
2
Jawaban Anda merujuk pada peningkatan perangkat keras: memori virtual, copy-on-write. Sebelum ini, forksebenarnya disalin semua memori proses, dan itu sangat mahal.
Barmar
6

Saya pikir alasan Unix hanya memiliki forkfungsi untuk menciptakan proses baru adalah hasil dari filosofi Unix

Mereka membangun satu fungsi yang melakukan satu hal dengan baik. Itu menciptakan proses anak.

Apa yang dilakukan seseorang dengan proses baru kemudian tergantung pada programmer. Dia dapat menggunakan salah satu exec*fungsi dan memulai program yang berbeda, atau dia tidak dapat menggunakan exec dan menggunakan dua contoh dari program yang sama, yang dapat bermanfaat.

Jadi Anda mendapatkan tingkat kebebasan yang lebih besar karena Anda bisa menggunakannya

  1. garpu tanpa exec *
  2. garpu dengan exec * atau
  3. hanya exec * tanpa garpu

dan selain itu Anda hanya perlu mengingat forkdan exec*panggilan fungsi, yang pada tahun 1970-an harus Anda lakukan.

Raphael Ahrens
sumber
3
Saya mengerti bagaimana garpu bekerja, dan bagaimana menggunakannya. Tetapi mengapa saya ingin membuat proses baru, ketika saya bisa melakukan hal yang sama tetapi dengan sedikit usaha? Sebagai contoh, guru saya memberi saya tugas di mana saya harus membuat proses untuk setiap nomor yang diteruskan ke argv, untuk memeriksa apakah nomor tersebut prima. Tetapi bukankah itu hanya jalan memutar untuk akhirnya melakukan hal yang sama? Saya bisa saja menggunakan array dan menggunakan fungsi untuk setiap angka ... Jadi mengapa kita membuat proses anak, alih-alih melakukan semua pemrosesan dalam proses utama?
user1534664
2
Saya berani mengatakan bahwa Anda mengerti bagaimana garpu bekerja, dan bagaimana menggunakannya, karena Anda pernah memiliki seorang guru yang memberi Anda tugas di mana Anda harus membuat banyak proses (dengan jumlah yang ditentukan pada saat run-time), mengendalikan mereka, mengoordinasi mereka, dan berkomunikasi di antara mereka. Tentu saja tidak ada yang akan melakukan hal sepele seperti itu di kehidupan nyata. Tetapi, jika Anda memiliki masalah besar yang mudah terurai menjadi bagian-bagian yang dapat ditangani secara paralel (misalnya, deteksi tepi dalam sebuah gambar), forking memungkinkan Anda menggunakan beberapa inti CPU secara bersamaan.
Scott
5

Ada dua filosofi penciptaan proses: bercabang dengan warisan, dan berkreasi dengan argumen. Unix menggunakan garpu, jelas. (OSE, misalnya, dan VMS menggunakan metode create.) Unix memiliki karakteristik bawaan BANYAK, dan lebih banyak lagi ditambahkan secara berkala. Melalui pewarisan, karakteristik baru ini dapat ditambahkan TANPA MENGUBAH PROGRAM YANG ADA! Menggunakan model buat-dengan-argumen, menambahkan karakteristik baru berarti menambahkan argumen baru ke panggilan buat. Model Unix lebih sederhana.

Ini juga memberi model fork-without-exec yang sangat berguna, di mana suatu proses dapat membagi dirinya menjadi beberapa bagian. Ini sangat penting ketika tidak ada async I / O, dan berguna ketika mengambil keuntungan dari banyak CPU dalam suatu sistem. (Pra-utas.) Saya telah melakukan ini banyak selama bertahun-tahun, bahkan baru-baru ini. Pada dasarnya itu memungkinkan untuk menggabungkan beberapa 'program' ke dalam satu program, sehingga sama sekali tidak ada ruang untuk korupsi atau ketidakcocokan versi, dll.

Model fork / exec juga memberikan kemampuan bagi anak tertentu untuk mewarisi lingkungan yang secara radikal aneh, mengatur antara fork dan exec. Hal-hal seperti deskriptor file yang diwarisi, khususnya. (Perpanjangan dari stdio fd's.) Model create tidak menawarkan kemampuan untuk mewarisi apa pun yang tidak dibayangkan oleh pencipta panggilan buat.

Beberapa sistem juga dapat mendukung kompilasi dinamis kode asli, di mana proses ini berlaku menulis program kode asli sendiri. Dengan kata lain, ia menginginkan program baru yang sedang menulis sendiri, TANPA harus melalui siklus kode-sumber / kompiler / penghubung, dan menempati ruang disk. (Saya percaya ada sistem bahasa Verilog yang melakukan ini.) Model garpu mendukung ini, model buatan biasanya tidak.

Jim Cathey
sumber
Deskriptor file bukan "perpanjangan stdio"; pointer file stdio adalah pembungkus di sekitar file deskriptor. File deskriptor yang diutamakan, dan itu adalah pegangan dasar Unix I / O. Tetapi, jika tidak, ini adalah poin yang bagus.
Scott
2

Fungsi garpu () tidak hanya untuk menyalin proses ayah, itu mengembalikan nilai yang merujuk bahwa proses adalah proses ayah atau anak, gambar di bawah ini menjelaskan bagaimana Anda dapat menggunakan garpu () sebagai ayah dan putra:

masukkan deskripsi gambar di sini

seperti yang ditunjukkan ketika proses adalah ayah garpu () mengembalikan proses ID anak yang PID lain itu kembali0

misalnya Anda dapat memanfaatkannya jika Anda memiliki proses (server web) yang menerima permintaan dan pada setiap permintaan itu dibuat son processuntuk memproses permintaan ini, di sini ayah dan anak-anaknya memiliki pekerjaan yang berbeda.

SO, tidak menjalankan salinan proses bukanlah hal yang tepat seperti garpu ().

Networker
sumber
5
Meskipun benar, ini tidak menjawab pertanyaan. Mengapa perlu forking untuk proses pembuatan, jika saya ingin menjalankan executable yang berbeda?
SkyDan
1
Saya setuju dengan SkyDan - ini tidak menjawab pertanyaan. posix_spawn adalah versi yang agak lebih bagus dari apa yang mungkin telah dibayangkan 30 tahun yang lalu (sebelum Posix ada) sebagai fungsi fork_execve ; yang menciptakan proses baru, menginisialisasi gambarnya dari yang dapat dieksekusi, bahkan tanpa mengisyaratkan menyalin gambar dari proses induk (kecuali untuk daftar argumen, lingkungan, dan atribut proses (misalnya, direktori kerja)), dan mengembalikan PID dari proses baru ke pemanggil (proses induk) .
Scott
1
Ada cara lain untuk memberikan informasi "orangtua" kepada seorang anak. Teknik nilai pengembalian kebetulan menjadi cara paling efisien untuk melakukannya dari fork jika Anda menganggap Anda ingin forkdi tempat pertama
Cort Ammon
0

Pengalihan I / O paling mudah diimplementasikan setelah garpu dan sebelum exec. Anak, karena menyadari bahwa ia adalah anak, dapat menutup deskriptor file, membuka yang baru, dup () atau dup2 () mereka untuk memasukkannya ke nomor fd yang tepat, dll, semua tanpa mempengaruhi orangtua. Setelah melakukan itu, dan mungkin perubahan variabel lingkungan yang diinginkan (juga tidak mempengaruhi induk), ia dapat mengeksekusi program baru di lingkungan yang disesuaikan.

Richard Hamilton
sumber
Yang Anda lakukan di sini adalah mengulangi paragraf ketiga dari jawaban Jim Cathey dengan sedikit lebih detail.
Scott
-2

Saya pikir semua orang di sini tahu bahwa cara kerja garpu, tetapi pertanyaannya adalah mengapa kita perlu membuat duplikat yang tepat dari orang tua menggunakan garpu? Answer ==> Ambil contoh server (tanpa garpu), sementara klien-1 mengakses server, jika pada saat yang sama klien kedua-2 tiba dan ingin mengakses server tetapi server tidak memberikan izin kepada yang baru tiba client-2 karena server sibuk melayani client-1 sehingga client-2 harus menunggu. Setelah semua layanan ke client-1 selesai, client-2 sekarang dapat mengakses server. Sekarang pertimbangkan apakah pada saat bersamaan klien-3 tiba, jadi klien-3 harus menunggu sampai semua layanan ke klien-2 selesai. Ambil skenario di mana ribuan klien perlu mengakses server pada saat yang sama ... maka semua klien harus tunggu (server sibuk !!).

Ini dihindari dengan membuat (menggunakan garpu) salinan duplikat yang tepat (yaitu anak) dari server, di mana setiap anak (yang merupakan duplikat yang tepat dari induknya yaitu server) didedikasikan untuk klien yang baru tiba, sehingga secara bersamaan semua klien mengakses sama server.

Harshil Mania
sumber
Inilah sebabnya mengapa proses server tidak boleh single-threaded, menangani permintaan klien secara berurutan ketika mereka dapat ditangani secara bersamaan - misalnya, dalam proses terpisah. Tetapi model server multi-utas dapat dengan mudah diimplementasikan dengan proses pendengar yang menerima permintaan dari klien dan menciptakan proses baru untuk menjalankan program layanan-klien. Satu-satunya keuntungan yang ditawarkan oleh forkpanggilan yang menyalin proses induk adalah bahwa Anda tidak harus memiliki dua program terpisah - tetapi memiliki program terpisah (misalnya, inetd) dapat membuat sistem lebih modular.
Scott