टीएल; डीआर : क्योंकि यह नई प्रक्रियाओं को बनाने और इंटरैक्टिव शेल में नियंत्रण रखने के लिए इष्टतम तरीका है
कांटा () प्रक्रियाओं और पाइपों के लिए आवश्यक है
इस प्रश्न के विशिष्ट भाग का उत्तर देने के लिए, यदि माता-पिता में सीधे के grep blabla foo
माध्यम से कॉल किया जाना था , तो exec()
माता-पिता मौजूद रहेंगे, और सभी संसाधनों के साथ इसकी पीआईडी पर कब्जा कर लिया जाएगा।grep blabla foo
।
हालाँकि, सामान्य रूप से exec()
और के बारे में बात करते हैं fork()
। इस तरह के व्यवहार का मुख्य कारण यह fork()/exec()
है कि यूनिक्स / लिनक्स पर एक नई प्रक्रिया बनाने का मानक तरीका है, और यह एक विशिष्ट बात नहीं है; यह पद्धति शुरुआत से ही लागू है और इस पद्धति से उस समय के पहले से मौजूद ऑपरेटिंग सिस्टम से प्रभावित है। संबंधित प्रश्न पर कुछ हद तक गोल्डफ्लॉक्स के उत्तर केfork()
लिए , नई प्रक्रिया बनाने के लिए आसान है क्योंकि कर्नेल के पास कम से कम काम है जहाँ तक संसाधनों का आवंटन होता है, और बहुत सारे गुण (जैसे फ़ाइल विवरणक, पर्यावरण, आदि) - सभी कर सकते हैं मूल प्रक्रिया से विरासत में मिला (इस मामले में bash
)।
दूसरे, जहाँ तक इंटरैक्टिव गोले चलते हैं, आप बिना फोर्क किए बाहरी कमांड नहीं चला सकते। एक निष्पादन योग्य लॉन्च करने के लिए जो डिस्क पर रहता है (उदाहरण के लिए /bin/df -h
), आपको exec()
परिवार के कार्यों में से एक को कॉल करना होगा, जैसे कि execve()
, जो नई प्रक्रिया के साथ माता-पिता की जगह लेगा, इसके पीआईडी और मौजूदा फाइल डिस्क्रिप्टर, आदि को संभाल लेगा। इंटरेक्टिव शेल के लिए, आप चाहते हैं कि कंट्रोल उपयोगकर्ता के पास लौटे और पैरेंट इंटरेक्टिव शेल को ले जाने दें। इस प्रकार, सबसे अच्छा तरीका है के माध्यम से एक उपप्रकार बनाने के लिए fork()
, और उस प्रक्रिया को इसके माध्यम से लिया जाए execve()
। इसलिए इंटरएक्टिव शेल पीआईडी 1156 fork()
पीआईडी 1157 के माध्यम से एक बच्चे को जन्म देगा , फिर कॉल करें execve("/bin/df",["df","-h"],&environment)
, जो पीआईडी 1157 के /bin/df -h
साथ चलता है। अब शेल को केवल प्रक्रिया से बाहर निकलने और उस पर नियंत्रण वापस करने के लिए इंतजार करना होगा।
ऐसे मामले में जहां आपको दो या दो से अधिक कमांड के बीच एक पाइप बनाना होता है, कहते हैं df | grep
, आपको दो फ़ाइल डिस्क्रिप्टर बनाने का एक तरीका चाहिए (यह पाइप के अंत में पढ़ने और लिखने का काम है जो कि pipe()
syscall से आते हैं ), फिर किसी तरह दो नई प्रक्रियाओं को इनहेरिट करने दें। यह नई प्रक्रिया के लिए किया जाता है और फिर dup2()
इसके stdout
aka fd 1 पर कॉल के माध्यम से पाइप के राइट एंड को कॉपी करके (इसलिए यदि राइट एंड fd 4 है, तो हम करते हैं dup2(4,1)
)। जब exec()
स्पॉन df
होता है, तो बाल प्रक्रिया इसके बारे में कुछ नहीं सोचेगी stdout
और बिना जागरूक हुए इसे लिखेगी (जब तक कि यह सक्रिय रूप से जांच नहीं करती) कि इसका आउटपुट वास्तव में एक पाइप है। एक ही प्रक्रिया के लिए होता grep
है, हम को छोड़कर fork()
, fd 3 के साथ और पाइप के पढ़ने के अंत लेने dup(3,0)
को उत्पन्न करने से पहले grep
साथexec()
। यह सब समय माता-पिता की प्रक्रिया अभी भी है, पाइपलाइन पूरी होने के बाद नियंत्रण वापस पाने की प्रतीक्षा में।
अंतर्निहित कमांड के मामले में, आमतौर पर शेल fork()
अपवाद के साथ नहीं होता है source
। सदस्यता की आवश्यकता है fork()
।
संक्षेप में, यह एक आवश्यक और उपयोगी तंत्र है।
फोर्किंग और अनुकूलन के नुकसान
अब, यह गैर-इंटरैक्टिव शेल के लिए अलग है , जैसे कि bash -c '<simple command>'
। fork()/exec()
इष्टतम विधि होने के बावजूद जहां आपको कई कमांडों को संसाधित करना पड़ता है, यह संसाधनों की बर्बादी है जब आपके पास केवल एक ही आदेश होता है। इस पोस्ट से स्टीफन चेज़लस को उद्धृत करने के लिए :
सीपीयू समय, मेमोरी, आवंटित फ़ाइल डिस्क्रिप्टर में फोर्किंग महंगा है ... एक शेल प्रक्रिया के बारे में झूठ बोलने से पहले बाहर निकलने से पहले किसी अन्य प्रक्रिया की प्रतीक्षा करना सिर्फ संसाधनों की बर्बादी है। इसके अलावा, कमांड को निष्पादित करने वाली अलग प्रक्रिया की निकास स्थिति को सही ढंग से रिपोर्ट करना मुश्किल बनाता है (उदाहरण के लिए, जब प्रक्रिया को मार दिया जाता है)।
इसलिए, कई गोले (सिर्फ नहीं bash
) का उपयोग exec()
करने के लिए अनुमति देने के bash -c ''
लिए कि एक ही सरल आदेश द्वारा पर ले लिया। और ठीक ऊपर दिए गए कारणों के लिए, शेल स्क्रिप्ट में पाइपलाइनों को कम करना बेहतर है। अक्सर आप देख सकते हैं कि शुरुआती लोग कुछ इस तरह करते हैं:
cat /etc/passwd | cut -d ':' -f 6 | grep '/home'
बेशक, यह fork()
3 प्रक्रियाएं होंगी । यह एक सरल उदाहरण है, लेकिन गीगाबाइट की सीमा में एक बड़ी फ़ाइल पर विचार करें। यह एक प्रक्रिया के साथ कहीं अधिक कुशल होगा:
awk -F':' '$6~"/home"{print $6}' /etc/passwd
संसाधनों की बर्बादी वास्तव में डेनियल ऑफ सर्विस हमले का एक रूप हो सकती है, और विशेष रूप से फोर्क बम शेल कार्यों के माध्यम से बनाए जाते हैं जो खुद को पाइपलाइन में कॉल करते हैं, जो खुद की कई प्रतियों की तलाश करते हैं। आजकल, सिस्टमग पर cgroups में अधिकतम प्रक्रियाओं को सीमित करने के माध्यम से इसे कम किया जाता है , जो उबंटू भी संस्करण 15.04 के बाद से उपयोग करता है।
बेशक इसका मतलब यह नहीं है कि फोर्किंग सिर्फ खराब है। यह अभी भी एक उपयोगी तंत्र है जैसा कि पहले चर्चा की गई है, लेकिन अगर आप कम प्रक्रियाओं, और लगातार कम संसाधनों और इस तरह बेहतर प्रदर्शन से दूर हो सकते हैं, तो आपको बचना चाहिएfork()
यदि संभव हो तो ।
यह भी देखें