TL; DR : Çünkü bu, yeni süreçler oluşturmak ve kontrolü etkileşimli kabukta tutmak için en uygun yöntemdir.
Çatal () işlemleri ve borular için gereklidir
Eğer bu sorunun belirli bir bölümünü cevaplamak için grep blabla foo
çağrılacak olan exec()
doğrudan ebeveyn de, ebeveyn varlığını ele geçirmek ve kaynaklar tarafından ele alınacağını tüm ile PID olur grep blabla foo
.
Ancak, genel olarak exec()
ve hakkında konuşalım fork()
. Böyle bir davranışın ana nedeni fork()/exec()
, Unix / Linux'ta yeni bir süreç oluşturmanın standart yöntemi olmasıdır ve bu, net bir şey değildir; bu yöntem başlangıçtan beri uygulanmış ve aynı yöntemden zamanın mevcut işletim sistemlerinden etkilenmiştir. Biraz paraphrase için goldilocks'un ilgili bir soruya vereceği cevaba göre , fork()
yeni süreç oluşturmak daha kolaydır çünkü çekirdeğin kaynakları ayırmak için yapması gereken daha az işi vardır ve çoğu özellik (örneğin dosya tanımlayıcıları, çevre, vb.) ana süreçten miras alınması (bu durumdabash
).
İkincisi, etkileşimli mermiler gittiğinde, çatal olmadan harici bir komut çalıştıramazsınız. Diskte yaşayan bir yürütülebilir dosyayı başlatmak için (örneğin, /bin/df -h
), üst öğeyi yeni işlemle değiştirecek olan exec()
aile işlevlerinden birini çağırmanız gerekir; örneğin, execve()
PID'sini ve mevcut dosya tanımlayıcılarını vb. Etkileşimli kabuk için, kontrolün kullanıcıya geri dönmesini ve ana etkileşimli kabuğun devam etmesini istersiniz. Bu nedenle, en iyi yol, bir alt işlem aracılığıyla oluşturmak fork()
ve bu işlemin ele geçirilmesine izin vermektir execve()
. Bu nedenle etkileşimli kabuk PID 1156 fork()
, PID 1157 ile bir çocuğu doğurur , sonra çağrı execve("/bin/df",["df","-h"],&environment)
yapar, bu da PID 1157 /bin/df -h
ile çalıştırılır.
İki veya daha fazla komut arasında bir boru oluşturmak zorunda kalmanız durumunda df | grep
, iki dosya tanımlayıcısı ( pipe()
sistem çağrısından gelen borunun sonunu okuyan ve yazan ) oluşturmak için bir yönteme ihtiyacınız var, o halde bir şekilde iki yeni işlemin miras almasına izin verin. Bu işlem yeni bir işlem gerektiriyor ve sonra borunun yazma ucunu aka fd 1 dup2()
çağrısıyla kopyalayarak yapılır stdout
(eğer yazma ucu fd 4 ise, yaparız dup2(4,1)
). exec()
Yumurtlama zamanı geldiğinde df
, çocuk süreci hiçbir şey düşünmeyecek stdout
ve çıktısının gerçekten bir boruya girdiğini bilmeden (aktif olarak kontrol etmediği sürece) yazacaktır. Aynı işlem grep
, biz hariç fork()
, fd 3 ile borunun sonunu okur ve dup(3,0)
yumurtlamadan önce grep
olur.exec()
. Tüm bu zaman ana süreci hala orada, boru hattı tamamlandıktan sonra kontrolü tekrar kazanmayı bekliyor.
Yerleşik komutlarda, genellikle kabuk, komut fork()
hariç , olmaz source
. Deniz kabukları gerektirir fork()
.
Kısacası, bu gerekli ve kullanışlı bir mekanizmadır.
Çatal ve dezavantajların dezavantajları
Şimdi, bu gibi etkileşimli olmayan kabuklar için farklıbash -c '<simple command>'
. fork()/exec()
Birçok komutu işlemeniz gereken en uygun yöntem olmasına rağmen , yalnızca bir tek komutunuz olduğunda bu bir kaynak israfıdır. Alıntı Stéphane Chazelas gelen bu yazı :
Forking pahalıdır, CPU zamanında, hafızaya, tahsis edilmiş dosya tanımlayıcılarına sahiptir ... Çıkmadan önce sadece başka bir işlemi beklemekten bahseden bir kabuk işlemine sahip olmak sadece bir kaynak israfıdır. Ayrıca, komutu uygulayacak olan ayrı işlemin çıkış durumunun doğru bir şekilde rapor edilmesini zorlaştırır (örneğin, işlem öldüğü zaman).
Bu nedenle, birçok kabuk (sadece değil bash
) bu basit komutla ele geçirilmesine exec()
izin vermek bash -c ''
için kullanılır. Ve tam olarak yukarıda belirtilen nedenlerden dolayı, kabuk komut dosyalarındaki boru hatlarının en aza indirilmesi daha iyidir. Genellikle yeni başlayanların böyle bir şey yaptığını görebilirsiniz:
cat /etc/passwd | cut -d ':' -f 6 | grep '/home'
Tabii ki, bu fork()
3 süreç olacak . Bu basit bir örnektir, ancak Gigabyte aralığında büyük bir dosya düşünün. Tek bir işlemle çok daha verimli olurdu:
awk -F':' '$6~"/home"{print $6}' /etc/passwd
Kaynakların israfı aslında bir Hizmet Reddi saldırısı olabilir ve özellikle de kendilerini bombalayan ve kendilerine ait birden fazla kopyasını alan kabuk bombalarıyla çatal bombaları yaratılır. Günümüzde, bu Ubuntu'nun 15.04 sürümünden bu yana kullandığı sistem gruplarındaki gruplardaki maksimum işlem sayısını sınırlamak suretiyle azaltılmaktadır .
Tabii ki bu çatallama demek sadece kötü değil. Daha önce tartışıldığı gibi hala faydalı bir mekanizmadır, ancak daha az işlemle ve art arda daha az kaynakla ve dolayısıyla daha iyi performansla kurtulmanız fork()
durumunda, mümkünse kaçınmalısınız .
Ayrıca bakınız