गोले को कांटा () क्यों कहते हैं?


32

जब एक प्रक्रिया को शेल से शुरू किया जाता है, तो प्रक्रिया को निष्पादित करने से पहले शेल खुद को कांटा क्यों करता है?

उदाहरण के लिए, जब उपयोगकर्ता इनपुट करता है grep blabla foo, तो शेल केवल exec()चाइल्ड शेल के बिना grep पर कॉल क्यों नहीं कर सकता है ?

इसके अलावा, जब एक जीयूआई टर्मिनल एमुलेटर के भीतर एक शेल खुद को फोर्क करता है, तो क्या यह दूसरा टर्मिनल एमुलेटर शुरू करता है? (जैसे pts/13शुरू pts/14)

जवाबों:


34

जब आप किसी execपारिवारिक विधि को कॉल करते हैं तो यह एक नई प्रक्रिया नहीं बनाता है, इसके बजाय execवर्तमान प्रक्रिया मेमोरी और इंस्ट्रक्शन सेट आदि को उस प्रक्रिया से बदल देता है जिसे आप चलाना चाहते हैं।

एक उदाहरण के रूप में, आप grepनिष्पादन का उपयोग करके चलाना चाहते हैं । bashएक प्रक्रिया है (जिसमें अलग मेमोरी, एड्रेस स्पेस है)। अब जब आप कॉल करते हैं exec(grep), तो निष्पादन grep'sडेटा के साथ वर्तमान प्रक्रिया की मेमोरी, एड्रेस स्पेस, इंस्ट्रक्शन सेट आदि को बदल देगा । इसका मतलब है कि bashप्रक्रिया अब मौजूद नहीं होगी। परिणामस्वरूप आप grepकमांड को पूरा करने के बाद टर्मिनल पर वापस नहीं आ सकते । यही कारण है कि परिवार के तरीके कभी नहीं लौटते हैं। आप निष्पादन के बाद किसी भी कोड को निष्पादित नहीं कर सकते हैं; यह पहुंच से बाहर है।


लगभग ठीक --- मैंने बैश के साथ टर्मिनल को प्रतिस्थापित किया। ;-)
रैनमो

2
BTW, आप कमांड का उपयोग करके, पहले बिना कांटे के grep निष्पादित करने के लिए बैश बता सकते हैं exec grep blabla foo। बेशक, इस विशेष मामले में, यह बहुत उपयोगी नहीं होगा (क्योंकि आपकी टर्मिनल विंडो बस grep के खत्म होते ही बंद हो जाएगी), लेकिन यह कभी-कभार काम में हो सकती है (जैसे यदि आप एक और शेल शुरू कर रहे हैं, तो शायद ssh के माध्यम से। / sudo / स्क्रीन, और मूल एक पर लौटने का इरादा नहीं है, या यदि आप जिस शेल प्रक्रिया को इस पर चला रहे हैं, वह एक उप-शेल है जो कभी भी एक से अधिक कमांड निष्पादित करने के लिए नहीं है)।
इल्मरी करोनें

7
निर्देश सेट का बहुत विशिष्ट अर्थ है। और इसका अर्थ यह नहीं है कि आप इसका उपयोग कर रहे हैं।
एंड्रयू सविनाख

@ इल्मारियारोनन यह रैपर स्क्रिप्ट में उपयोगी होगा, जहाँ आप एक कमांड के लिए तर्क और वातावरण तैयार करना चाहते हैं। और जिस मामले का आपने उल्लेख किया है, जहां बैश का मतलब कभी भी एक से अधिक कमांड चलाने का नहीं होता है, वास्तव में bash -c 'grep foo bar'कॉलिंग एग्जाम होता है और आपके लिए ऑप्टिमाइजेशन बैश का रूप अपने आप हो जाता है
सेर्गी कोलोडियाज़नी

3

के अनुसार pts, इसे स्वयं जांचें: एक शेल में, चलाएं

echo $$ 

आपकी प्रक्रिया-आईडी (पीआईडी) जानने के लिए, मेरे पास उदाहरण के लिए है

echo $$
29296

फिर उदाहरण के लिए sleep 60और फिर दूसरे टर्मिनल में चलाएं

(0)samsung-romano:~% ps -edao pid,ppid,tty,command | grep 29296 | grep -v grep
29296  2343 pts/11   zsh
29499 29296 pts/11   sleep 60

तो नहीं, सामान्य तौर पर आपके पास प्रक्रिया से संबंधित समान ट्टी होती है। (ध्यान दें कि यह आपका है sleepक्योंकि इसमें मूल के रूप में आपका शेल है)।


2

टीएल; डीआर : क्योंकि यह नई प्रक्रियाओं को बनाने और इंटरैक्टिव शेल में नियंत्रण रखने के लिए इष्टतम तरीका है

कांटा () प्रक्रियाओं और पाइपों के लिए आवश्यक है

इस प्रश्न के विशिष्ट भाग का उत्तर देने के लिए, यदि माता-पिता में सीधे के 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()इसके stdoutaka 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() यदि संभव हो तो ।

यह भी देखें


1

प्रत्येक कमांड के लिए (उदाहरण: grep) जो आप बैश प्रॉम्प्ट पर जारी करते हैं, आप वास्तव में एक नई प्रक्रिया शुरू करने का इरादा रखते हैं और फिर निष्पादन के बाद बैश प्रॉम्प्ट पर लौटते हैं।

यदि शेल प्रक्रिया (बैश) grep चलाने के लिए निष्पादन () कॉल करती है, तो शेल प्रक्रिया को grep से बदल दिया जाएगा। ग्रीप ठीक काम करेगा लेकिन निष्पादन के बाद, नियंत्रण शेल में वापस नहीं आ सकता है क्योंकि बैश प्रक्रिया पहले से ही बदल दी गई है।

इस कारण से, बैश कॉल कांटा (), जो वर्तमान प्रक्रिया को प्रतिस्थापित नहीं करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.