Ketika suatu proses dimulai dari shell, mengapa shell itu bercabang sendiri sebelum menjalankan proses?
Misalnya, ketika pengguna memasukkan grep blabla foo
, mengapa shell tidak dapat memanggil exec()
grep tanpa shell anak?
Juga, ketika shell bercabang sendiri dalam emulator terminal GUI, apakah shell memulai emulator terminal lain? (seperti pts/13
memulai pts/14
)
sumber
exec grep blabla foo
. Tentu saja, dalam kasus khusus ini, itu tidak akan sangat berguna (karena jendela terminal Anda akan menutup begitu grep selesai), tetapi kadang-kadang berguna (misalnya jika Anda memulai shell lain, mungkin melalui ssh / sudo / layar, dan jangan berniat untuk kembali ke yang asli, atau jika proses shell Anda menjalankan ini adalah sub-shell yang tidak pernah dimaksudkan untuk menjalankan lebih dari satu perintah).bash -c 'grep foo bar'
dan memanggil exec ada bentuk optimasi bash lakukan untuk Anda secara otomatisSesuai
pts
, periksa sendiri: di shell, jalankanuntuk mengetahui id proses Anda (PID), saya punya misalnya
Kemudian jalankan misalnya
sleep 60
dan kemudian, di terminal lainJadi tidak, secara umum Anda memiliki tty yang sama terkait dengan proses. (Perhatikan bahwa ini adalah milik Anda
sleep
karena memiliki shell sebagai induknya).sumber
TL; DR : Karena ini adalah metode optimal untuk membuat proses baru dan menjaga kontrol dalam shell interaktif
fork () diperlukan untuk proses dan pipa
Untuk menjawab bagian spesifik dari pertanyaan ini, jika
grep blabla foo
dipanggil viaexec()
langsung di induk, orangtua akan mengambil ada, dan PID dengan semua sumber daya akan diambil alih olehgrep blabla foo
.Namun, mari kita bicara secara umum tentang
exec()
danfork()
. Alasan utama untuk perilaku tersebut adalah karenafork()/exec()
merupakan metode standar untuk menciptakan proses baru di Unix / Linux, dan ini bukan hal khusus bash; metode ini telah ada sejak awal dan dipengaruhi oleh metode yang sama dari sistem operasi yang sudah ada saat itu. Mengutip jawaban goldilocks pada pertanyaan terkait,fork()
untuk membuat proses baru lebih mudah karena kernel memiliki lebih sedikit pekerjaan yang harus dilakukan sejauh mengalokasikan sumber daya, dan banyak properti (seperti deskriptor file, lingkungan, dll) - semua bisa diwarisi dari proses induk (dalam hal ini daribash
).Kedua, sejauh shell interaktif berjalan, Anda tidak dapat menjalankan perintah eksternal tanpa forking. Untuk meluncurkan executable yang hidup pada disk (misalnya,
/bin/df -h
), Anda harus memanggil salah satuexec()
fungsi keluarga, sepertiexecve()
, yang akan menggantikan induk dengan proses baru, mengambil alih PID dan deskriptor file yang ada, dll. Untuk shell interaktif, Anda ingin kontrol kembali ke pengguna dan membiarkan shell interaktif induk melanjutkan. Dengan demikian, cara terbaik adalah membuat subproses viafork()
, dan membiarkan proses itu diambil alih viaexecve()
. Jadi shell interaktif PID 1156 akan menelurkan seorang anak melaluifork()
dengan PID 1157, lalu panggilexecve("/bin/df",["df","-h"],&environment)
, yang/bin/df -h
dijalankan dengan PID 1157. Sekarang shell hanya perlu menunggu proses untuk keluar dan mengembalikan kontrol ke sana.Jika Anda harus membuat pipa di antara dua perintah atau lebih, katakanlah
df | grep
, Anda memerlukan cara untuk membuat dua deskriptor file (yang membaca dan menulis ujung pipa yang berasal daripipe()
syscall), lalu membiarkan dua proses baru mewarisinya. Itu dilakukan forking proses baru dan kemudian dengan menyalin ujung tulis pipa melaluidup2()
panggilan kestdout
alias fd 1 (jadi jika akhir penulisan adalah fd 4, kita lakukandup2(4,1)
). Kapanexec()
akan munculdf
proses anak tidak akan memikirkan apa-apastdout
dan menulis padanya tanpa sadar (kecuali jika aktif memeriksa) bahwa outputnya benar-benar berjalan pipa. Proses yang sama terjadigrep
, kecuali kitafork()
, mengambil membaca ujung pipa dengan fd 3 dandup(3,0)
sebelum pemijahangrep
denganexec()
. Selama ini proses induk masih ada, menunggu untuk mendapatkan kembali kontrol setelah pipa selesai.Dalam kasus perintah bawaan, umumnya shell tidak
fork()
, dengan pengecualiansource
perintah. Subshell membutuhkanfork()
.Singkatnya, ini adalah mekanisme yang perlu dan bermanfaat.
Kekurangan forking dan optimalisasi
Sekarang, ini berbeda untuk cangkang non-interaktif , seperti
bash -c '<simple command>'
. Meskipunfork()/exec()
merupakan metode optimal di mana Anda harus memproses banyak perintah, itu membuang-buang sumber daya ketika Anda hanya memiliki satu perintah tunggal. Mengutip Stéphane Chazelas dari pos ini :Oleh karena itu, banyak cangkang (bukan hanya
bash
) digunakanexec()
untuk membiarkannyabash -c ''
diambil alih oleh satu perintah sederhana itu. Dan tepat untuk alasan yang disebutkan di atas, meminimalkan pipa dalam skrip shell lebih baik. Seringkali Anda dapat melihat pemula melakukan sesuatu seperti ini:Tentu saja, ini akan
fork()
3 proses. Ini adalah contoh sederhana, tetapi pertimbangkan file besar, dalam kisaran Gigabytes. Akan jauh lebih efisien dengan satu proses:Buang-buang sumber daya sebenarnya bisa menjadi bentuk serangan Denial of Service, dan khususnya bom fork dibuat melalui fungsi shell yang menyebut diri mereka sendiri dalam pipa, yang memalsukan banyak salinan dari diri mereka sendiri. Saat ini, ini dimitigasi melalui pembatasan jumlah maksimum proses dalam cgroup pada systemd , yang Ubuntu juga gunakan sejak versi 15.04.
Tentu saja bukan berarti forking itu buruk. Ini masih merupakan mekanisme yang bermanfaat seperti yang dibahas sebelumnya, tetapi jika Anda dapat pergi dengan lebih sedikit proses, dan secara berurutan lebih sedikit sumber daya dan dengan demikian kinerja yang lebih baik, maka Anda harus menghindari
fork()
jika mungkin.Lihat juga
sumber
Untuk setiap perintah (contoh: grep) yang Anda terbitkan pada bash prompt, Anda sebenarnya bermaksud untuk memulai proses baru dan kemudian kembali ke bash prompt setelah eksekusi.
Jika proses shell (bash) memanggil exec () untuk menjalankan grep, proses shell akan diganti dengan grep. Grep akan berfungsi dengan baik tetapi setelah eksekusi, kontrol tidak dapat kembali ke shell karena proses bash sudah diganti.
Untuk alasan ini, bash memanggil fork (), yang tidak menggantikan proses saat ini.
sumber