यूनिक्स डेमॉन वे प्रोग्राम होते हैं जो बैकग्राउंड में चलते हैं। Nginx, Postgres और OpenSSH कुछ उदाहरण हैं। वे अपने प्रोसेस को "डिटैच" करने के लिए कुछ विशेष तरकीबों का उपयोग करते हैं, और उन्हें किसी भी टर्मिनल से स्वतंत्र रूप से चलने देते हैं।
मैं हमेशा से डेमॉन के प्रति थोड़ा आकर्षित रहा हूँ - शायद नाम की वजह से - और मुझे लगा कि यह दिखाना मजेदार होगा कि वे कैसे काम करते हैं। और विशेष रूप से, आप उन्हें रूबी में कैसे बना सकते हैं।
...लेकिन पहले।
इसे घर पर आज़माने की कोशिश न करें!
आप शायद डेमॉन नहीं बनाना चाहते। काम पूरा करने के बहुत आसान तरीके हैं।
आप शायद ऐसा प्रोग्राम चाहते हैं जो बैकग्राउंड में चले। कोई बात नहीं। आपका OS सामान्य प्रोग्राम को बैकग्राउंड में चलाने के लिए एक सिस्टम प्रदान करता है।
उबंटू पर, यह Upstart या systemd के माध्यम से होता है। OSX पर यह launchd है। अन्य भी हैं। लेकिन वे सभी एक ही अवधारणा के अनुसार काम करते हैं। आप एक कॉन्फिगरेशन फाइल प्रदान करते हैं जो सिस्टम को बताती है कि लंबे समय तक चलने वाले प्रोग्राम को कैसे शुरू और बंद करना है। फिर... ठीक है, बस इतना ही। आप service my_app start जैसे सिस्टम कमांड का उपयोग करके प्रोग्राम शुरू कर सकते हैं और यह बैकग्राउंड में चलता है।
संक्षेप में, अपस्टार्ट सरल और विश्वसनीय है जबकि पुराने स्कूल के डेमॉन गूढ़ और सही करना बहुत कठिन हैं।
...लेकिन अगर ऐसा है, तो हमें डेमॉन के बारे में क्यों सीखना चाहिए? खैर, क्योंकि मज़ा! और हम रास्ते में यूनिक्स प्रोसेस के बारे में कुछ दिलचस्प तथ्य सीखेंगे।
सबसे सरल डेमॉन
अब जब आपको कभी डेमॉन न बनाने के लिए कहा गया है, तो आइए कुछ डेमॉन बनाते हैं! रूबी 1.9 के बाद से, यह अविश्वसनीय रूप से सरल है। आपको बस Process.daemon मेथड का उपयोग करना है।
# Optional: set the process name to something easy to type<br>$PROGRAM_NAME = "rubydaemon"<br> # Make the current process into a daemon Process.daemon() # Once per second, log the current time to a file loop do File.open("/tmp/rubydaemon.log", "a") { |f| f.puts(Time.now) } sleep(1) end
अब, जब मैं इस स्क्रिप्ट को चलाता हूँ, तो कंट्रोल कंसोल पर वापस आ जाता है। अगर मैं अपना लॉग टेल करता हूँ, तो मैं देख सकता हूँ कि टाइमस्टैम्प हर सेकंड जोड़ा जा रहा है, जैसा कि मुझे उम्मीद थी।

तो यह आसान था। लेकिन यह अभी भी समझाता नहीं है कि डेमॉन कैसे काम करते हैं। इसे वास्तव में समझने के लिए, हमें डेमोनाइजेशन को मैन्युअली करना होगा।
पेरेंट प्रोसेस बदलना
यदि आप सामान्य प्रोग्राम चलाने के लिए बैश का उपयोग करते हैं, तो उस प्रोग्राम का प्रोसेस बैश का चाइल्ड होता है। लेकिन डेमॉन के साथ, इससे कोई फर्क नहीं पड़ता कि आप उन्हें कैसे लॉन्च करते हैं। उनका पेरेंट प्रोसेस हमेशा OS द्वारा प्रदान किया गया "रूट" प्रोसेस होता है।
आप यह डेमॉन के पेरेंट आईडी को देखकर बता सकते हैं। एक डेमॉन का पेरेंट आईडी हमेशा 1 होता है। नीचे दिए गए उदाहरण में हम इसे दिखाने के लिए pstree का उपयोग करते हैं:
$ pstree -+= 00001 root /sbin/launchd |--- 72314 snhorne rubydaemon
दिलचस्प बात यह है कि "अनाथ प्रोसेस" भी ऐसे ही दिखते हैं। एक अनाथ प्रोसेस एक चाइल्ड प्रोसेस होता है जिसका पेरेंट समाप्त हो गया है।
तो, डेमॉन बनाने के लिए हमें जानबूझकर एक प्रोसेस को अनाथ बनाना होगा। नीचे दिया गया कोड ऐसा करता है।
# Optional: set the process name to something easy to type $PROGRAM_NAME = "rubydaemon" # Create a new child process and exit the parent. This "orphans" # our process and creates a daemon. exit if fork() # Once per second, log the current time to a file loop do File.open("/tmp/rubydaemon.log", "a") { |f| f.puts(Time.now) } sleep(1) end
fork की कॉल के परिणामस्वरूप दो प्रोसेस एक ही कोड चलाते हैं। मूल प्रोसेस नए प्रोसेस का पेरेंट होता है। Fork पेरेंट के लिए ट्रूथी मान और चाइल्ड के लिए फाल्सी मान लौटाता है। इसलिए exit if fork() केवल पेरेंट को बाहर निकालता है।
वर्तमान सत्र से डिटैच होना
हमारे "डेमोनाइजेशन" कोड में कुछ समस्याएं हैं। हालांकि यह सफलतापूर्वक प्रोसेस को अनाथ बनाता है, यह अभी भी टर्मिनल के सत्र का हिस्सा है। इसका मतलब है कि अगर आप टर्मिनल को मारते हैं, तो आप डेमॉन को मार देते हैं। इसे ठीक करने के लिए, हमें एक नया सत्र बनाना होगा और फिर से फोर्क करना होगा। यूनिक्स सत्र समूहों से परिचित नहीं हैं? यहां एक अच्छा StackOverflow पोस्ट है।
# Optional: set the process name to something easy to type $PROGRAM_NAME = "rubydaemon" # Create a new child process and exit the parent. This "orphans" # our process and creates a daemon. exit if fork # Create a new session, create a new child process in it and # exit the current process. Process.setsid exit if fork # Once per second, log the current time to a file loop do File.open("/tmp/rubydaemon.log", "a") { |f| f.puts(Time.now) } sleep(1) end
STDIN, STDOUT और STDERR को पुनर्निर्देशित करना
उपरोक्त कोड की एक और समस्या यह है कि यह मौजूदा STDOUT, आदि को छोड़ देता है। इसका मतलब है कि अगर आप डेमॉन को टर्मिनल से लॉन्च करते हैं, तो डेमॉन जो कुछ भी STDOUT पर लिखता है वह आपके टर्मिनल पर भेजा जाएगा। अच्छा नहीं।
लेकिन आप वास्तव में STDIN, STDOUT, और STDERR को किसी भी पथ पर पुनर्निर्देशित कर सकते हैं। यहां हम /dev/null पर पुनर्निर्देशित करते हैं।
# Optional: set the process name to something easy to type $PROGRAM_NAME = "rubydaemon" # Create a new child process and exit the parent. This "orphans" # our process and creates a daemon. exit if fork # Create a new session, create a new child process in it and # exit the current process. Process.setsid exit if fork STDIN.reopen "/dev/null" STDOUT.reopen "/dev/null", "a" STDERR.reopen '/dev/null', 'a' # Once per second, log the current time to a file loop do File.open("/tmp/rubydaemon.log", "a") { |f| f.puts(Time.now) } sleep(1) end
कार्य निर्देशिका बदलना
अंत में, डेमॉन की कार्य निर्देशिका वही होती है जिसमें हम इसे चलाते समय थे। यह शायद सबसे अच्छा विचार नहीं है, क्योंकि मैं बाद में उस निर्देशिका को हटाने का फैसला कर सकता हूँ। तो चलिए निर्देशिका को / पर बदलते हैं।
# Optional: set the process name to something easy to type $PROGRAM_NAME = "rubydaemon" # Create a new child process and exit the parent. This "orphans" # our process and creates a daemon. exit if fork # Create a new session, create a new child process in it and # exit the current process. Process.setsid exit if fork STDIN.reopen "/dev/null" STDOUT.reopen "/dev/null", "a" STDERR.reopen '/dev/null', 'a' Dir.chdir("/") # Once per second, log the current time to a file loop do File.open("/tmp/rubydaemon.log", "a") { |f| f.puts(Time.now) } sleep(1) end
क्या मैंने इसे पहले देखा है?
चरणों का यह क्रम मूल रूप से वही है जो हर रूबी डेमॉन को Process.daemon मेथड के रूबी कोर में जोड़े जाने से पहले करना पड़ता था। मैंने इसे ActiveSupport के Process मॉड्यूल के एक्सटेंशन से लगभग लाइन दर लाइन कॉपी किया है जिसे Rails 4.x में हटा दिया गया था। आप उस मेथड को यहां देख सकते हैं।