Bir daemon oluştururken çift çatal yapmanın nedeni nedir?


165

Python'da bir arka plan programı oluşturmaya çalışıyorum. Şu anda takip ettiğim bazı iyi kaynaklara sahip aşağıdaki soruyu buldum , ancak neden çift çatalın gerekli olduğunu merak ediyorum. Ben google çizik ve bir gerekli olduğunu bildiren kaynakların bol buldum, ama neden değil.

Bazıları daemonun bir kontrol terminali edinmesini önlemek olduğunu belirtiyor. Bunu ikinci çatal olmadan nasıl yapar? Etkileri nelerdir?



2
Çift çatal yapmanın bir zorluğu, ebeveynin torun işleminin PID'sini kolayca alamamasıdır ( fork()çağrı çocuğun PID'sini ebeveynine döndürür, bu nedenle çocuk işleminin PID'sini almak kolaydır, ancak bu kadar kolay değildir. PID almak torun ) süreci.
Craig McQueen

Yanıtlar:


105

Soruda atıfta bulunulan koda bakıldığında, gerekçe:

İkinci bir çocuğu çatalla ve zombileri önlemek için hemen çık. Bu, ikinci alt sürecin yetim kalmasına neden olur ve init sürecinin temizlenmesinden sorumlu olur. Ve, ilk çocuk kontrol terminali olmayan bir oturum lideri olduğundan, gelecekte bir terminal açarak bir tane elde etmek mümkündür (Sistem V tabanlı sistemler). Bu ikinci çatal, çocuğun artık bir oturum lideri olmadığını garanti eder ve bu daemon'un bir kontrol terminali edinmesini önler.

Bu nedenle, daemon'un init'e yeniden ayrıştırılmasını sağlamak (sadece daemon'u tekmeleme sürecinin uzun ömürlü olması durumunda) ve daemon'un kontrol eden bir tty'yi yeniden kazanma şansını ortadan kaldırmasıdır. Dolayısıyla, bu durumların hiçbiri geçerli değilse, bir çatal yeterli olmalıdır. " Unix Network Programming - Stevens " bu konuda iyi bir bölüme sahip.


28
Bu tamamen doğru değil. Bir daemon oluşturmanın standart yolu basitçe yapmaktır p=fork(); if(p) exit(); setsid(). Bu durumda, ebeveyn de çıkar ve ilk alt süreç onarılır. Çift çatal büyüsü sadece artalan sürecinin bir tty edinmesini önlemek için gereklidir.
parasietje

1
Anladığım kadarıyla, programım başlar ve forksbir childsüreç olursa , bu ilk alt süreç bir olacak session leaderve bir TTY terminali açabilecektir. Ancak bu çocuktan tekrar çatallanır ve bu ilk çocuğu sonlandırırsam, ikinci çatallı çocuk bir olmayacak session leaderve bir TTY terminali açamayacak. Bu ifade doğru mu?
tonix

2
@tonix: sadece çatallamak bir oturum lideri oluşturmaz. Bu tarafından yapılır setsid(). Böylece, ilk çatallı işlem çağrıldıktan sonra bir oturum lideri olur setsid()ve daha sonra tekrar çatallanırız, böylece son, çift çatallı süreç artık bir oturum lideri değildir. setsid()Oturum lideri olmanın gerekliliği dışında, dikkatinizi çeker.
dbmikus

169

Çift çatalı anlamaya çalışıyordum ve bu soruya tökezledim. Bir çok araştırmadan sonra bunu anladım. Umarım aynı soruya sahip olan herkes için işleri daha iyi açıklığa kavuşturacaktır.

Unix'te her işlem bir oturuma ait olan bir gruba aittir. İşte hiyerarşi…

Oturum (SID) → İşlem Grubu (PGID) → İşlem (PID)

Süreç grubundaki ilk süreç süreç grubu lideri, oturumdaki ilk süreç ise oturum lideri olur. Her oturumda ilişkili bir TTY olabilir. Yalnızca bir oturum lideri TTY'nin kontrolünü ele geçirebilir. Bir sürecin gerçekten arka plana alınabilmesi için (arka planda koştu) oturum liderinin öldürüldüğünden emin olmalıyız, böylece oturumun TTY'nin kontrolünü ele geçirme olasılığı kalmaz.

Sander Marechal'ın python örneği daemon programını Ubuntu'mda bu siteden çalıştırdım. İşte yorumlarımın sonuçları.

1. `Parent`    = PID: 28084, PGID: 28084, SID: 28046
2. `Fork#1`    = PID: 28085, PGID: 28084, SID: 28046
3. `Decouple#1`= PID: 28085, PGID: 28085, SID: 28085
4. `Fork#2`    = PID: 28086, PGID: 28085, SID: 28085

Sürecin sonraki oturum lideri olduğunu unutmayın Decouple#1, çünkü PID = SID. Yine de bir TTY'nin kontrolünü ele geçirebilir.

Bunun Fork#2artık oturum lideri olmadığını unutmayın PID != SID. Bu süreç asla bir TTY'nin kontrolünü ele geçiremez. Gerçekten cömertçe.

Kişisel olarak terminoloji çatalı buluyorum - iki kere kafa karıştırıcı. Daha iyi bir deyim çatal-çatal-çatal olabilir.

Ek ilgi alanları:


İki kez çatallama, üst işlem daha uzun süre çalıştığında ve bazı nedenlerden dolayı, işlemin öldüğünü bildiren sinyal için varsayılan işleyiciyi kaldırmayı da zombi oluşturmayı önler.
Trismegistos

Ama ikincisi de ayrıştırmayı çağırabilir ve oturum lideri haline gelebilir ve daha sonra terminal alabilir.
Trismegistos

2
Bu doğru değil. Birincisi fork(), ebeveyni oluşturmayı zaten önler, eğer ebeveyni kapatırsanız.
parasietje

1
Yukarıda belirtilen sonuçları üretmek için minimal bir örnek: gist.github.com/cannium/7aa58f13c834920bb32c
can.

1
Birisi iyi çağırmak olurdu setsid() önce tek fork()? Aslında sanırım bu sorunun cevapları buna cevap veriyor .
Craig McQueen

118

Açıkçası, çift çatalın daemon'u bir çocuk olarak yeniden ebeveynleştirmekle hiçbir ilgisi yoktur init. Çocuğu yeniden ebeveynleştirmek için gerekli olan tek şey ebeveynin çıkması gerektiğidir. Bu sadece tek bir çatalla yapılabilir. Ayrıca, çift çatal yapmak kendi başına daemon sürecini yeniden ebeveyn yapmaz init; arka plan programının ebeveyninin çıkması gerekir . Başka bir deyişle, ebeveyn her zaman uygun bir arka plan programı çatallanırken daemon işleminin yeniden ayrılması için çıkar init.

Peki neden çift çatal? POSIX.1-2008 Bölüm 11.1.3, " Kontrol Terminali " cevabı vardır (vurgu eklenmiştir):

Bir oturum için kontrol terminali , oturum lideri tarafından uygulama tanımlı bir şekilde tahsis edilir . Bir oturum liderinin kontrol terminali yoksa ve O_NOCTTYseçeneği kullanmadan bir oturumla ilişkilendirilmemiş bir terminal cihaz dosyası açarsa (bkz. open()), Terminalin oturum liderinin kontrol terminali olup olmadığı uygulama tarafından tanımlanır. Bir süreç ise değil bir oturum lideri bir terminal dosyasını açar, ya da O_NOCTTYseçenek kullanılır open(), daha sonra terminali çağıran sürecin denetim terminali haline görmemesi gerektiğini .

Bu bize bir daemon süreci böyle bir şey yaparsa ...

int fd = open("/dev/console", O_RDWR);

... sonra arka planda çalışan olabilir kazanmak /dev/consoleonun terminal kontrol ve arka planda çalışan bir oturum lideri olmasına bağlı olarak ve sistem uygulamaya bağlı olarak. Program, ilk önce bir oturum lideri olmadığından emin olursa, yukarıdaki çağrının bir kontrol terminali edinmeyeceğini garanti edebilir .

Normalde, bir arka plan programı başlatılırken, arka plan programını kontrol eden terminalden setsidayırmak için (çağırdıktan sonra alt süreçten fork) çağrılır . Bununla birlikte, çağrı setsid, çağrı sürecinin yeni oturumun oturum lideri olacağı anlamına gelir ve bu da arka plan programının kontrol eden bir terminali yeniden kazanma olasılığını açık bırakır. Çift çatal tekniği, daemon işleminin oturum lideri olmamasını sağlar, bu da openyukarıdaki örnekte olduğu gibi bir çağrının bir daemon işleminin bir kontrol terminalini yeniden kazanmasına neden olmayacağını garanti eder .

Çift çatal tekniği biraz paranoyak. Daemon'un hiçbir zaman bir terminal cihazı dosyası açmayacağını biliyorsanız gerekli olmayabilir . Ayrıca, bazı sistemlerde, bu davranış uygulama tanımlı olduğundan, arka plan programı bir terminal aygıtı dosyası açsa bile gerekli olmayabilir. Ancak, uygulama tanımlı olmayan bir şey, yalnızca bir oturum liderinin kontrol terminalini tahsis edebilmesidir. Bir işlem oturum lideri değilse, denetleyen bir terminal ayıramaz. Bu nedenle, paranoyak olmak ve daemon işleminin, herhangi bir uygulama tanımlı özelliğe bakılmaksızın, yanlışlıkla bir kontrol terminali elde edemeyeceğinden emin olmak istiyorsanız, çift çatal tekniği önemlidir.


3
+1 Bu cevap, sorunun sorulmasından ~ dört yıl sonra çok kötü geldi.
Tim Seguine

12
Ancak bu hala bir
daemonun

7
Anahtar kelime "yanlışlıkla" bir kontrol terminali edinir. Süreç bir terminal açarsa ve terminali kontrol eden süreçler haline gelirse, biri o terminalden bir ^ C verirse, işlemi sonlandırabilir. Bu nedenle, bir işlemi bunun gerçekleşmesini istemeden korumak iyi olabilir. Şahsen ben tek bir çatal ve setsid () sopa olacak yazıyorum kod terminaller açmayacağını biliyorum.
BobDoolittle

1
@BobDoolittle bu "yanlışlıkla" nasıl olabilir? Bir süreç yazılmazsa sadece terminalleri açmayacak. Programcı kodu bilmiyor ve bir tty açıp açamayacağını bilmiyorsanız, çift çatallama yararlı olabilir.
Marius

10
Eğer Cinin yapılandırma dosyasına şöyle bir satır eklemek takdirde neler olabileceğini düşünün @Marius: LogFile=/dev/console. Programların her zaman hangi dosyaları açabilecekleri derleme zamanı kontrolü yoktur;)
Dan Moulding

11

Kötü CTK'dan Alınan :

"Unix'in bazı tatlarında, daemon moduna geçmek için başlangıçta çift çatal yapmak zorundasınız. Bunun nedeni, tek çatalın kontrol terminalinden ayrılması garanti edilmiyor."


3
Tek çatal kontrol terminalinden nasıl ayrılmaz, ancak çift çatal bunu nasıl yapabilir? Bu hangi unix'lerde meydana geliyor?
bdonlan

12
Bir arka plan programı giriş ve çıkış dosyası tanımlayıcılarını (fds) kapatmalıdır, aksi halde, başlatıldığı terminale bağlı olur. Çatallı bir süreç, üst öğeden miras alır. Görünüşe göre, ilk çocuk fds kapatır ama bu her şeyi temizlemez. İkinci çatalda, fds yoktur, bu yüzden ikinci çocuk artık hiçbir şeye bağlanamaz.
Aaron Digulla

4
@Aaron: Hayır, bir arka plan programı, setsidilk çataldan sonra çağırarak kendisini kontrol terminalinden "ayırır" . Daha sonra tekrar çatallayarak ve oturum liderinin (çağrılan işlem ) çıkmasını sağlayarak kontrol terminalinden ayrı kalmasını sağlar setsid.
Dan Moulding

2
@bdonlan: forkKontrol terminalinden ayrılmak değil . Bunu setsidyapar. Ancak setsidbir süreç grubu liderinden çağrılırsa başarısız olur. Bu nedenle , işlem grubu lideri olmayan bir işlemden çağrıldığından emin olmak forkiçin daha önce bir başlangıç yapılmalıdır . İkincisi , son sürecin (arka plan programı olacak) bir oturum lideri olmamasını sağlar. Yalnızca oturum liderleri bir kontrol terminali edinebilir, bu nedenle bu ikinci çatal daemon'un kontrol eden bir terminali yanlışlıkla yeniden kazanmayacağını garanti eder. Bu herhangi bir POSIX işletim sistemi için geçerlidir. setsidsetsidfork
Dan Moulding

@DanMoulding Bu, ikinci çocuğun kontrol terminali edinmeyeceğini garanti etmez, çünkü setid'i çağırabilir ve oturum lideri olabilir ve daha sonra kontrol terminalini alabilir.
Trismegistos

7

Stephens ve Rago tarafından "Unix Ortamında Gelişmiş Programlama" ya göre, ikinci çatal daha çok bir tavsiye niteliğinde ve artalan sürecinin Sistem V tabanlı sistemlerde bir kontrol terminali edinmemesini sağlamak için yapılıyor.


3

Bunun bir nedeni, üst sürecin çocuk için hemen wait_pid () yöntemini sonra unutmasıdır. O zaman büyük çocuk öldüğünde, ebeveyn inittir ve onu () bekleyecek ve zombi durumundan çıkaracaktır.

Sonuç olarak, ebeveyn sürecinin çatallı çocukların farkında olması gerekmez ve ayrıca uzun süren süreçleri kütüphanelerden vb.


2

Daemon () çağrısı başarılı olursa, _exit () üst çağrısına sahiptir. Asıl motivasyon, çocuğun cenaze töreninde ebeveynin fazladan iş yapmasına izin vermek olabilir.

Ayrıca, arka plan programının ebeveyn sürecinin olmadığından ve başlatılmak üzere yeniden bildirildiğinden emin olmanın gerekli olduğuna dair yanlış bir inanca da dayanabilir - ancak ebeveyn tek çatal çantasında öldüğünde bu yine de olur.

Bu yüzden her şeyin sonunda gelenekle kaynaştığını düşünüyorum - ebeveyn yine de kısa sırayla öldüğü sürece tek bir çatal yeterlidir.



-1

Bu şekilde anlamak daha kolay olabilir:

  • İlk çatal ve set kimliği yeni bir oturum oluşturur (ancak işlem kimliği == oturum kimliği).
  • İkinci çatal işlem kimliğinin! = Oturum kimliğinden emin olur.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.