डेमन बनाते समय दोहरा कांटा प्रदर्शन करने का क्या कारण है?


165

मैं अजगर में एक डेमॉन बनाने की कोशिश कर रहा हूं। मैंने निम्नलिखित प्रश्न पाया है , जिसमें कुछ अच्छे संसाधन हैं जो मैं वर्तमान में अनुसरण कर रहा हूं, लेकिन मैं इस बात के लिए उत्सुक हूं कि दोहरा कांटा क्यों आवश्यक है। मैंने Google के चारों ओर खरोंच किया है और बहुत सारे संसाधनों को यह घोषित करते हुए पाया है कि एक आवश्यक है, लेकिन क्यों नहीं।

कुछ उल्लेख करते हैं कि यह डेमन को नियंत्रित टर्मिनल प्राप्त करने से रोकना है। दूसरे कांटे के बिना यह कैसे होगा? नतीजे क्या हैं?



2
दोहरे कांटे को करने में एक कठिनाई यह है कि माता-पिता को आसानी से पोते की प्रक्रिया की पीआईडी ​​नहीं मिल सकती है ( fork()कॉल बच्चे के पीआईडी ​​को माता-पिता को लौटा देता है, इसलिए बच्चे की प्रक्रिया का पीआईडी ​​प्राप्त करना आसान है, लेकिन इतना आसान नहीं है पोते की प्रक्रिया के पीआईडी ​​प्राप्त करें )।
क्रेग मैकक्यून

जवाबों:


105

प्रश्न में संदर्भित कोड को देखते हुए, औचित्य है:

एक दूसरे बच्चे को कांटा और लाश को रोकने के लिए तुरंत बाहर निकलें। यह दूसरी बच्चे की प्रक्रिया को अनाथ करने का कारण बनता है, जिससे सफाई प्रक्रिया को जिम्मेदार ठहराया जाता है। और, चूंकि पहला बच्चा एक नियंत्रित टर्मिनल के बिना एक सत्र का नेता है, इसलिए भविष्य में एक टर्मिनल (सिस्टम वी-आधारित सिस्टम) खोलकर इसे हासिल करना संभव है। यह दूसरा कांटा इस बात की गारंटी देता है कि बच्चा अब सत्र का नेता नहीं है, डेमॉन को कभी भी नियंत्रित टर्मिनल प्राप्त करने से रोकता है।

इसलिए यह सुनिश्चित करना है कि डेमन init पर फिर से सम्‍मिलित है (बस अगर डेमॉन को किक करने की प्रक्रिया लंबे समय तक जीवित रहती है), और डेमन के किसी भी संयोग को नियंत्रित करने वाले ट्टी को हटा देता है। इसलिए यदि इनमें से कोई भी मामला लागू नहीं होता है, तो एक कांटा पर्याप्त होना चाहिए। " यूनिक्स नेटवर्क प्रोग्रामिंग - स्टीवंस " इस पर एक अच्छा खंड है।


28
यह पूरी तरह सही नहीं है। डेमन बनाने का मानक तरीका बस करना है p=fork(); if(p) exit(); setsid()। इस मामले में, माता-पिता भी बाहर निकल जाते हैं और पहले बच्चे की प्रक्रिया का पुनर्मूल्यांकन किया जाता है। डबल-फोर्क जादू केवल डेमॉन को ट्टी प्राप्त करने से रोकने के लिए आवश्यक है।
परसेंटीज

1
इसलिए, जैसा कि मैं इसे समझता हूं, अगर मेरा कार्यक्रम शुरू होता है और forksएक childप्रक्रिया होती है, तो यह बहुत पहले बच्चे की प्रक्रिया session leaderएक टीटीएस टर्मिनल खोलने में सक्षम होगी। लेकिन अगर मैं इस बच्चे से फिर से कांटा लगाऊं और इस पहले बच्चे को समाप्त कर दूं, तो दूसरा कांटा बच्चा नहीं होगा session leaderऔर वह टीटीवाई टर्मिनल नहीं खोल पाएगा। क्या यह कथन सही है?
टॉनिक्स

2
@tonix: केवल फोर्किंग सत्र नेता नहीं बनाता है। द्वारा किया जाता है setsid()। तो, पहले कांटे की प्रक्रिया कॉल करने के बाद एक सत्र का नेता बन जाता है setsid()और फिर हम फिर से कांटा करते हैं ताकि अंतिम, डबल-कांटेक्ट प्रक्रिया अब सत्र का नेता न हो। setsid()एक सत्र के नेता होने की आवश्यकता के अलावा , आप हाजिर हैं।
dbmikus

169

मैं डबल कांटा समझने की कोशिश कर रहा था और इस सवाल पर लड़खड़ा गया। बहुत खोजबीन के बाद यह पता चला है। उम्मीद है कि यह उन लोगों के लिए बेहतर चीजों को स्पष्ट करने में मदद करेगा जिनके पास एक ही प्रश्न है।

यूनिक्स में हर प्रक्रिया एक समूह की होती है जो बदले में एक सत्र से संबंधित होती है। यहाँ पदानुक्रम है ...

सत्र (SID) → प्रक्रिया समूह (PGID) → प्रक्रिया (PID)

प्रक्रिया समूह में पहली प्रक्रिया प्रक्रिया समूह नेता बन जाती है और सत्र में पहली प्रक्रिया सत्र नेता बन जाती है। हर सत्र में एक टीटीवाई जुड़ा हो सकता है। केवल एक सत्र का नेता ही TTY का नियंत्रण ले सकता है। एक प्रक्रिया के लिए सही मायने में निर्धारित किया जाना चाहिए (पृष्ठभूमि में भाग गया) हमें यह सुनिश्चित करना चाहिए कि सत्र नेता को मार दिया जाए ताकि सत्र की कभी भी टीटीवाई पर नियंत्रण होने की कोई संभावना न हो।

मैंने अपने उबंटू में इस साइट से सैंडर मारेचल के अजगर उदाहरण डेमन प्रोग्राम चलाया । यहाँ मेरी टिप्पणियों के साथ परिणाम हैं।

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

ध्यान दें कि यह प्रक्रिया सत्र के बाद का नेता है Decouple#1, क्योंकि यह है PID = SID। यह अभी भी एक TTY का नियंत्रण ले सकता है।

ध्यान दें कि Fork#2अब सत्र का नेता नहीं है PID != SID। यह प्रक्रिया कभी भी TTY का नियंत्रण नहीं ले सकती है। सच में बड़बड़ाया।

मुझे व्यक्तिगत रूप से शब्दावली कांटा दो बार भ्रामक लगता है। एक बेहतर मुहावरा कांटा-सड़ना-कांटा हो सकता है।

ब्याज की अतिरिक्त लिंक:


जब माता-पिता प्रक्रिया लंबे समय तक चलती है और किसी कारणवश डिफ़ॉल्ट हैंडलर को हटाने से संकेत मिलता है कि इस प्रक्रिया की मृत्यु हो गई, तो दो बार फोर्किंग भी लाश बनाने से रोकता है।
त्रिशिगीस्टोस

लेकिन दूसरा भी डिकॉयल कह सकता है और सत्र नेता बन सकता है और फिर टर्मिनल का अधिग्रहण कर सकता है।
Trismegistos

2
यह सच नहीं है। पहले से fork()ही पहले से ही लाश बनाने से रोकता है, बशर्ते आप माता-पिता को बंद कर दें।
पारिजात

1
उपरोक्त उद्धृत परिणामों का उत्पादन करने के लिए एक न्यूनतम उदाहरण: gist.github.com/cannium/7aa58f13c834920bb32c
कर सकते हैं।

1
क्या सिंगल setsid() से पहले कॉल करना अच्छा रहेगा fork()? वास्तव में मुझे इस प्रश्न के उत्तर का अनुमान है कि उत्तर ।
क्रेग मैकक्वीन

118

कड़ाई से बोलते हुए, डबल-कांटे का डेमन को बच्चे के रूप में पुन: पालन-पोषण करने से कोई लेना-देना नहीं है init। बच्चे को फिर से बनाने के लिए आवश्यक है कि माता-पिता को बाहर निकलना चाहिए। यह केवल एक ही कांटा के साथ किया जा सकता है। इसके अलावा, अपने आप से डबल-कांटा करने से डेमॉन प्रक्रिया फिर से नहीं होती है init; डेमन के माता-पिता को बाहर निकलना चाहिए । दूसरे शब्दों में, माता-पिता हमेशा उचित डेमॉन का उपयोग करते समय बाहर निकलते हैं ताकि डेमन प्रक्रिया फिर से सम्‍मिलित हो init

तो डबल कांटा क्यों? POSIX.1-2008 धारा 11.1.3, " कंट्रोलिंग टर्मिनल ", का उत्तर है (जोर दिया गया):

एक सत्र के लिए नियंत्रण टर्मिनल सत्र नेता द्वारा कार्यान्वयन-परिभाषित तरीके से आवंटित किया जाता है। यदि एक सत्र के नेता के पास कोई नियंत्रण टर्मिनल नहीं है, और एक टर्मिनल डिवाइस फ़ाइल खोलता है जो पहले से ही सत्र O_NOCTTY(विकल्प open()) का उपयोग किए बिना सत्र से जुड़ा नहीं है , तो यह कार्यान्वयन-परिभाषित है कि क्या टर्मिनल सत्र नेता का नियंत्रण टर्मिनल बन जाता है। यदि एक प्रक्रिया जो सत्र के नेता नहीं है , एक टर्मिनल फ़ाइल खोलती है, या O_NOCTTYविकल्प का उपयोग किया जाता है open(), तो वह टर्मिनल कॉलिंग प्रक्रिया का नियंत्रक टर्मिनल नहीं बनेगा

यह हमें बताता है कि अगर एक डेमन प्रक्रिया कुछ इस तरह से ...

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

... तो डेमॉन प्रक्रिया अपने नियंत्रण टर्मिनल के रूप में अधिग्रहण कर सकती है/dev/console , यह इस बात पर निर्भर करता है कि डेमन प्रक्रिया एक सत्र नेता है, और सिस्टम कार्यान्वयन पर निर्भर करता है। कार्यक्रम यह गारंटी दे सकता है कि उपरोक्त कॉल एक नियंत्रित टर्मिनल का अधिग्रहण नहीं करेगा यदि कार्यक्रम पहले यह सुनिश्चित करता है कि यह सत्र का नेता नहीं है।

आम तौर पर, एक डेमन को लॉन्च करते समय, अपने कंट्रोलिंग टर्मिनल से डेमॉन को अलग करने के लिए setsid(कॉल करने के बाद बच्चे की प्रक्रिया से) कहा जाता है fork। हालांकि, कॉलिंग setsidका अर्थ यह भी है कि कॉलिंग प्रक्रिया नए सत्र का सत्र नेता होगी, जो इस संभावना को छोड़ देती है कि डेमन एक नियंत्रित टर्मिनल को फिर से प्राप्त कर सकता है। डबल-फोर्क तकनीक यह सुनिश्चित करती है कि डेमॉन प्रक्रिया सत्र का नेता नहीं है, जो तब गारंटी देता है कि openऊपर दिए गए उदाहरण के अनुसार कॉल करने के लिए, डेमन प्रक्रिया में नियंत्रण टर्मिनल की पुनः प्राप्ति नहीं होगी।

डबल-फोर्क तकनीक थोड़ा पागल है। यह आवश्यक नहीं हो सकता है यदि आप जानते हैं कि डेमन कभी टर्मिनल डिवाइस फ़ाइल नहीं खोलेगा। इसके अलावा, कुछ प्रणालियों पर यह आवश्यक नहीं हो सकता है, भले ही डेमॉन एक टर्मिनल डिवाइस फ़ाइल खोले, क्योंकि यह व्यवहार कार्यान्वयन-परिभाषित है। हालांकि, एक चीज जो कार्यान्वयन-परिभाषित नहीं है, वह यह है कि केवल एक सत्र का नेता ही नियंत्रण टर्मिनल को आवंटित कर सकता है। यदि कोई प्रक्रिया सत्र नेता नहीं है, तो यह एक नियंत्रित टर्मिनल आवंटित नहीं कर सकता है। इसलिए, यदि आप पागल होना चाहते हैं और निश्चित हैं कि डेमॉन प्रक्रिया अनजाने में एक नियंत्रित टर्मिनल का अधिग्रहण नहीं कर सकती है, भले ही किसी भी कार्यान्वयन-परिभाषित बारीकियों की परवाह किए बिना, तो डबल-फोर्क तकनीक आवश्यक है।


3
+1 बहुत बुरा सवाल आने के चार साल बाद यह जवाब आया।
टिम सेगुइने

12
लेकिन फिर भी यह स्पष्ट नहीं है कि यह बहुत महत्वपूर्ण क्यों है कि एक डेमन एक नियंत्रित टर्मिनल को फिर से प्राप्त नहीं कर सकता है
उलोपी

7
कीवर्ड "अनजाने में" एक नियंत्रित टर्मिनल का अधिग्रहण करता है। यदि प्रक्रिया टर्मिनल को खोलने के लिए होती है, और यह टर्मिनल को नियंत्रित करने वाली प्रक्रियाएं बन जाती है, तो इससे कि कोई व्यक्ति उस टर्मिनल से ^ C जारी करता है, तो यह प्रक्रिया को समाप्त कर सकता है। इसलिए किसी प्रक्रिया को अनजाने में होने से बचाने के लिए यह अच्छा हो सकता है। व्यक्तिगत रूप से मैं कोड के लिए एक एकल कांटा और सेटसिड () पर लिखूंगा जो मैं लिखता हूं कि मुझे पता है कि टर्मिनलों को खोलना नहीं होगा।
BobDoolittle

1
@BusDoolittle यह "अनजाने में" कैसे हो सकता है? यदि यह ऐसा करने के लिए नहीं लिखा गया है तो एक प्रक्रिया खुले हुए टर्मिनलों को समाप्त नहीं करेगी। हो सकता है कि यदि प्रोग्रामर कोड नहीं जानता हो और उसे पता न चले तो डबल-फोर्किंग उपयोगी है।
मारीस

10
@ मरियम कल्पना कीजिए कि अगर आप अपने डेमन के कॉन्फ़िगरेशन फ़ाइल में इस तरह की एक पंक्ति जोड़ते हैं तो क्या हो सकता है LogFile=/dev/console:। कार्यक्रम हमेशा संकलन-समय पर नियंत्रण नहीं रखते हैं कि वे किन फ़ाइलों को खोल सकते हैं?)
दान मोल्डिंग

11

खराब सीटीके से लिया गया :

"यूनिक्स के कुछ स्वादों पर, आपको डेमॉन मोड में जाने के लिए, स्टार्टअप पर एक डबल-फोर्क करने के लिए मजबूर किया जाता है। ऐसा इसलिए है क्योंकि सिंगल फ़ॉरकिंग को कंट्रोलिंग टर्मिनल से अलग करने की गारंटी नहीं है।"


3
कंट्रोलिंग टर्मिनल से सिंगल कांटा कैसे अलग नहीं हो सकता लेकिन डबल कांटा ऐसा कर सकता है? इस पर क्या यूनिक्स होते हैं?
बोल्डन

12
एक डेमॉन को इनपुट और आउटपुट फ़ाइल डिस्क्रिप्टर (एफडीएस) को बंद करना होगा, अन्यथा, यह अभी भी टर्मिनल से जुड़ा हुआ होगा, इसमें शुरू किया गया था। एक जाली प्रक्रिया माता-पिता से विरासत में मिली है। जाहिर है, पहले बच्चे को बंद कर देता है, लेकिन यह सब कुछ साफ नहीं करता है। दूसरे कांटे पर, fds मौजूद नहीं है, इसलिए दूसरा बच्चा किसी भी चीज़ से जुड़ा नहीं हो सकता है।
आरोन दिगुल्ला

4
@ ऐरॉन: नहीं, एक डेमन ठीक से अपने setsidप्रारंभिक टर्मिनल से कॉल करने के बाद अपने नियंत्रण टर्मिनल से अलग हो जाता है। यह तो सुनिश्चित करता है कि यह रहता है फिर से forking और सत्र नेता (प्रक्रिया है कि कहा जाता है होने से एक को नियंत्रित करने के लिए टर्मिनल से अलग setsid) से बाहर निकलें।
डैन मोल्डिंग

2
@bdonlan: ऐसा नहीं है forkकि नियंत्रण टर्मिनल से अलग हो जाता है। यह है setsidकि यह करता है। लेकिन setsidअगर यह एक प्रक्रिया समूह के नेता से कहा जाता है विफल हो जाएगा। इसलिए यह सुनिश्चित करने के लिए कि प्रक्रिया समूह के नेता नहीं हैं, से forkपहले एक प्रारंभिक किया जाना चाहिए । दूसरा यह सुनिश्चित करता है कि अंतिम प्रक्रिया (जो डेमन होगी) एक सत्र का नेता नहीं है। केवल सत्र नेता ही एक नियंत्रित टर्मिनल का अधिग्रहण कर सकते हैं, इसलिए यह दूसरा कांटा गारंटी देता है कि डेमन अनजाने में एक नियंत्रित टर्मिनल को दोबारा प्राप्त नहीं करेगा। यह किसी भी POSIX OS का सच है। setsidsetsidfork
डैन मोल्डिंग

@DanMoulding यह गारंटी नहीं देता है कि दूसरा बच्चा टर्मिनल को नियंत्रित करने का अधिग्रहण नहीं करेगा क्योंकि यह सेटसाइड कह सकता है और सत्र नेता बन सकता है और फिर टर्मिनल को नियंत्रित कर सकता है।
२०:०४ पर Trismegistos

7

स्टीफंस और रागो द्वारा "यूनिक्स पर्यावरण में उन्नत प्रोग्रामिंग" के अनुसार, दूसरा कांटा एक सिफारिश अधिक है, और यह गारंटी देने के लिए किया जाता है कि डेमन सिस्टम वी-आधारित सिस्टम पर एक नियंत्रित टर्मिनल का अधिग्रहण नहीं करता है।


3

एक कारण यह है कि माता-पिता की प्रक्रिया बच्चे के लिए तुरंत इंतज़ार कर सकती है (और) और फिर इसके बारे में भूल जाओ। जब तब ग्रैंड-चाइल्ड की मृत्यु हो जाती है, तो यह माता-पिता के लिए अयोग्य होता है, और यह इसके लिए इंतजार करेगा () - और इसे ज़ोंबी अवस्था से बाहर ले जाएगा।

इसका परिणाम यह है कि अभिभावक प्रक्रिया के लिए कांटे के बच्चों के बारे में जागरूक होने की आवश्यकता नहीं है, और यह भी संभव है कि लंबे समय से चल रही प्रक्रियाओं को लिबास आदि से कांटा जाए।


2

डेमॉन () कॉल में पेरेंट कॉल _exit () है यदि यह सफल होता है। मूल प्रेरणा माता-पिता को कुछ अतिरिक्त काम करने की अनुमति देने की हो सकती है, जबकि बच्चा निंदा कर रहा है।

यह गलत धारणा पर भी आधारित हो सकता है कि यह सुनिश्चित करने के लिए यह आवश्यक है कि डेमन की कोई मूल प्रक्रिया नहीं है और यह init के लिए पुन: प्रस्तुत है - लेकिन यह वैसे भी होगा जब एक बार एकल कांटा मामले में माता-पिता की मृत्यु हो जाती है।

तो मुझे लगता है कि यह सब अंत में परंपरा के अनुसार उबलता है - एक भी कांटा पर्याप्त है जब तक कि माता-पिता किसी भी तरह से कम क्रम में मर जाते हैं।


2

इसकी एक सभ्य चर्चा http://www.developerweb.net/forum/showthread.php?t=325 पर दिखाई देती है

वहां से मम्पीकिन का हवाला देते हुए:

... सेटसाइड के बारे में सोचें () कॉल को "नया" करने के तरीके के रूप में करते हैं (टर्मिनल से अलग) और एसवीआर 4 से निपटने के लिए अतिरेक के रूप में [दूसरा] कांटा () कॉल के बाद ...


-1

इस तरह समझना आसान हो सकता है:

  • पहला कांटा और सेटिंग एक नया सत्र (लेकिन प्रक्रिया आईडी == सत्र आईडी) बनाएगा।
  • दूसरा कांटा प्रक्रिया आईडी सुनिश्चित करता है! = सत्र आईडी।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.