पाइपलाइनें मेमोरी के उपयोग को कैसे सीमित करती हैं?


36

ब्रायन कर्निघन इस वीडियो में बताते हैं कि स्मॉल लिमिटेशंस के आधार पर छोटी भाषाओं / कार्यक्रमों के लिए शुरुआती बेल लैब्स आकर्षण हैं

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

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

से विकिपीडिया :

अधिकांश यूनिक्स जैसी प्रणालियों में, एक पाइपलाइन की सभी प्रक्रियाएं एक ही समय में शुरू होती हैं [जोर मेरा], उनकी धाराओं के साथ उचित रूप से जुड़ा हुआ है, और मशीन पर चलने वाली अन्य सभी प्रक्रियाओं के साथ अनुसूचक द्वारा प्रबंधित किया जाता है। इसका एक महत्वपूर्ण पहलू, यूनिक्स पाइप को अन्य पाइप कार्यान्वयन के अलावा स्थापित करना, बफ़रिंग की अवधारणा है: उदाहरण के लिए एक भेजने का कार्यक्रम 5000 बाइट्स प्रति सेकंड का उत्पादन कर सकता है, और एक प्राप्त कार्यक्रम केवल 100 बाइट्स प्रति सेकंड स्वीकार करने में सक्षम हो सकता है, लेकिन नहीं डेटा खो गया है। इसके बजाय, भेजने वाले प्रोग्राम का आउटपुट बफर में आयोजित किया जाता है। जब प्राप्त कार्यक्रम डेटा पढ़ने के लिए तैयार है, तो पाइप लाइन में अगला कार्यक्रम बफर से पढ़ता है। लिनक्स में, बफर का आकार 65536 बाइट्स (64KB) है। यदि आवश्यक हो तो बड़े बफ़र्स प्रदान करने के लिए एक खुला स्रोत थर्ड-पार्टी फ़िल्टर बीएफआर कहा जाता है।

यह मुझे और भी भ्रमित करता है, क्योंकि यह छोटे कार्यक्रमों के उद्देश्य को पूरी तरह से हरा देता है (हालांकि वे एक निश्चित पैमाने तक मॉड्यूलर होंगे)।

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

यह सब बहुत समझ में आता है अगर अस्थायी फ़ाइलों का उपयोग किया गया था, लेकिन यह मेरी समझ है कि पाइप डिस्क पर नहीं लिखते हैं (जब तक कि स्वैप का उपयोग नहीं किया जाता है)।

उदाहरण:

sed 'simplesubstitution' file | sort | uniq > file2

यह मेरे लिए स्पष्ट है कि sedफाइल में पढ़ा जा रहा है और लाइन के आधार पर इसे बाहर थूक रहा है। लेकिन sort, जैसा कि बीके ने लिंक किए गए वीडियो में बताया है, एक पूर्ण विराम है, इसलिए सभी डेटा को मेमोरी में पढ़ना पड़ता है (या करता है?), फिर इसे पास कर दिया जाता है uniq, जो (मेरे दिमाग में) एक होगा? -लाइन पर एक समय कार्यक्रम। लेकिन पहले और दूसरे पाइप के बीच सारा डेटा मेमोरी में होना चाहिए, नहीं?


1
unless swap is usedस्वैप का उपयोग हमेशा किया जाता है जब पर्याप्त रैम नहीं होता है
edc65

जवाबों:


44

डेटा को रैम में संग्रहीत करने की आवश्यकता नहीं है। यदि पाठक नहीं हैं या नहीं रख सकते हैं तो पाइप उनके लेखकों को अवरुद्ध करते हैं; लिनक्स के तहत (और अधिकांश अन्य कार्यान्वयन, मुझे लगता है) कुछ बफ़रिंग है, लेकिन इसकी आवश्यकता नहीं है। जैसा कि mtraceur और JdeBP द्वारा उल्लेख किया गया है ( उत्तरार्द्ध का उत्तर देखें)), यूनिक्स के शुरुआती संस्करणों ने डिस्क में पाइपों को बफर कर दिया, और इस तरह से उन्होंने मेमोरी उपयोग को सीमित करने में मदद की: एक प्रोसेसिंग पाइपलाइन को छोटे कार्यक्रमों में विभाजित किया जा सकता था, जिनमें से प्रत्येक डिस्क बफ़र्स की सीमा के भीतर कुछ डेटा को संसाधित करेगा। छोटे कार्यक्रम कम मेमोरी लेते हैं, और पाइप के उपयोग का मतलब है कि प्रसंस्करण को क्रमबद्ध किया जा सकता है: पहला कार्यक्रम चलेगा, इसके आउटपुट बफर को भरें, निलंबित किया जाएगा, फिर दूसरे कार्यक्रम को शेड्यूल किया जाएगा, बफर को प्रोसेस करें, आदि। आधुनिक सिस्टम ऑर्डर हैं प्रारंभिक यूनिक्स प्रणालियों की तुलना में बड़ा परिमाण, और समानांतर में कई पाइप चला सकते हैं; लेकिन भारी मात्रा में डेटा के लिए आप अभी भी एक समान प्रभाव देखेंगे (और इस तरह की तकनीक के वेरिएंट "बड़े डेटा" प्रसंस्करण के लिए उपयोग किए जाते हैं)।

आपके उदाहरण में,

sed 'simplesubstitution' file | sort | uniq > file2

sedडेटा fileको आवश्यकतानुसार पढ़ता है, फिर उसे तब तक लिखता है जब तक sortवह उसे पढ़ने के लिए तैयार है; अगर sortतैयार नहीं है, तो ब्लॉक करें। डेटा वास्तव में स्मृति में रहता है, लेकिन यह विशिष्ट है sort, और sortकिसी भी मुद्दे से निपटने के लिए तैयार है (यह अस्थायी फ़ाइलों का उपयोग करेगा यह सॉर्ट करने के लिए डेटा की मात्रा बहुत बड़ी है)।

आप ब्लॉकिंग व्यवहार को चलाकर देख सकते हैं

strace seq 1000000 -1 1 | (sleep 120; sort -n)

यह उचित मात्रा में डेटा का उत्पादन करता है और इसे एक ऐसी प्रक्रिया में ले जाता है जो पहले दो मिनट तक कुछ भी पढ़ने के लिए तैयार नहीं है । आप कई writeऑपरेशन देखेंगे , लेकिन बहुत जल्दी seqरुक जाएंगे और कर्नेल द्वारा अवरुद्ध ( writeसिस्टम कॉल प्रतीक्षा करता है) दो मिनट रुकने का इंतजार करेंगे ।


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

6
@ पालन: कई प्रक्रियाएं शुरू की जा सकती हैं और यह एक ही समय में एक रननीय अवस्था में हो सकती हैं। लेकिन हर एक प्रक्रिया में किसी भी समय प्रत्येक भौतिक सीपीयू पर क्रियान्वित किया जा सकता है, और यह कर्नेल की प्रक्रिया अनुसूचक का काम है कि प्रत्येक चलने योग्य प्रक्रिया के लिए सीपीयू समय के "स्लाइस" आवंटित करें। आधुनिक प्रणालियों में, एक प्रक्रिया जो चलाने योग्य होती है, लेकिन वर्तमान में एक CPU टाइमस्लाइड शेड्यूल नहीं की जाती है, आमतौर पर इसकी अगली स्लाइस का इंतजार करते समय मेमोरी में रह जाती है, लेकिन कर्नेल को किसी भी प्रोसेस की मेमोरी को डिस्क में और फिर से मेमोरी में वापस करने की अनुमति है। यह सुविधाजनक लगता है। (यहां कुछ विवरणों को बताएं।)
डैनियल प्राइडेन

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

6
@malan कार्यक्रम "एक ही समय में" सभी यूनिक्स प्रणालियों पर वैचारिक रूप से शुरू किए गए हैं , बस आधुनिक मल्टीप्रोसेसर सिस्टम पर पर्याप्त रैम के साथ उन्हें धारण करने का मतलब है कि वे शाब्दिक रूप से एक ही समय में रैम में आयोजित किए जाते हैं, जबकि एक सिस्टम पर एक ही समय में रैम में उन सभी को पकड़ नहीं है, कुछ डिस्क से बाहर स्वैप हो जाते हैं। यह भी ध्यान दें कि बहुत सारे संदर्भों में "मेमोरी" का अर्थ है आभासी मेमोरी जो कि डिस्क पर रैम स्पेस और स्वैप स्पेस दोनों का योग है। विकिपीडिया कार्यान्वयन विवरणों के बजाय अवधारणा पर ध्यान केंद्रित कर रहा है, विशेष रूप से क्योंकि वास्तव में पुराने यूनिक्स ने जो चीजें की थीं, वे अब कम प्रासंगिक हैं।
मृत्युंजय

2
@मलन इसके अलावा, जो विरोधाभास आप देख रहे हैं, वह "मेमोरी" (RAM बनाम RAM + स्वैप) के दो अलग-अलग अर्थों से आता है। मैं केवल हार्डवेयर रैम के बारे में बात कर रहा था, और उस संदर्भ में केवल वर्तमान में सीपीयू द्वारा कोड किए जा रहे कोड को रैम में फिट करने की आवश्यकता थी (जो कि कार्निगन के फैसले की बात कर रहा था) को प्रभावित कर रहा था, जबकि सभी कार्यक्रमों के संदर्भ में तार्किक रूप से निष्पादित किया जा रहा था एक निश्चित समय पर ओएस द्वारा (समय टुकड़ा करने की क्रिया के शीर्ष पर प्रदान किए गए सार स्तर पर) एक कार्यक्रम को बस ओएस को उपलब्ध संपूर्ण आभासी मेमोरी के भीतर फिट होने की आवश्यकता होती है, जिसमें डिस्क पर स्वैप स्थान शामिल है।
मृत्युंजय

34

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

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

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

इस तंत्र को 1980 के दशक के मध्य में दूसरों द्वारा प्रतिस्थापित किया गया था। SCO XENIX ने "हाई परफॉर्मेंस पाइप सिस्टम" प्राप्त किया, जिसने आई-नोड्स को इन-कोर बफ़र्स से बदल दिया। 4BSD ने बेनामी पाइप को सॉकेटपेयर में बनाया। AT & T ने STREAMS तंत्र का उपयोग करके पाइपों को फिर से कार्यान्वित किया।

और निश्चित रूप से sortकार्यक्रम ने सीमित आंतरिक प्रकार का 32KiB चंक्स इनपुट (या 32KiB उपलब्ध नहीं होने पर जो कुछ भी स्मृति को आवंटित किया जा सकता है) का प्रदर्शन किया, छंटे हुए परिणामों को मध्यवर्ती stmX??फ़ाइलों में लिखते हुए /usr/tmp/जिसमें यह तब अंतिम रूप प्रदान करने के लिए हल किया गया था उत्पादन।

आगे की पढाई

  • स्टीव डी। पाटे (1996)। "अंतःप्रक्रम संचार"। यूनिक्स इंटर्नल्स: एक व्यावहारिक दृष्टिकोण । एडिसन-वेस्ले। आईएसबीएन 9780201877212
  • मौरिस जे। बाख (1987)। "फाइल सिस्टम के लिए सिस्टम कॉल"। यूनिक्स ऑपरेटिंग सिस्टम का डिज़ाइन । शागिर्द कक्ष। आईएसबीएन 0132017571।
  • स्टीवन वी। इर्हार्ट (1986)। " config(1M)"। यूनिक्स प्रोग्रामर मैनुअल: 3. सिस्टम एडमिनिस्ट्रेशन सुविधाएं । होल्ट, राइनहार्ट और विंस्टन। आईएसबीएन 0030093139. पीपी। 23-28

1

आप आंशिक रूप से सही हैं, लेकिन केवल दुर्घटना से

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

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

आजकल, पाइप एक छोटा, परिमित आकार का कर्नेल बफर होता है, जिसमें डेटा कॉपी किया जाता है, कम से कम वैचारिक रूप से ऐसा होता है। यदि कर्नेल इसमें मदद कर सकता है, तो वीएम ट्रिक खेलकर प्रतियों को खत्म कर दिया जाता है (उदाहरण के लिए, एक फ़ाइल से पाइपिंग आमतौर पर एक ही पृष्ठ को पढ़ने के लिए अन्य प्रक्रिया के लिए उपलब्ध होता है, इसलिए यह अंततः केवल एक पढ़ने का संचालन है, दो प्रतियां नहीं, और नहीं अतिरिक्त मेमोरी का उपयोग पहले से ही बफर कैश द्वारा किसी भी तरह से किया जाता है। कुछ स्थितियों में आपको 100% शून्य-कॉपी भी मिल सकती है। या, कुछ बहुत ही करीब।

यदि पाइप छोटे और परिमित आकार के हैं, तो यह किसी भी अज्ञात (संभवतः बड़ी) डेटा की मात्रा के लिए कैसे काम कर सकता है? यह सरल है: जब अधिक कुछ नहीं फिट बैठता है, तब तक लिखो जब तक कि फिर से कमरा न हो।

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


जब आप कहते हैं कि 'पाइप का नाम था' (JdeBP ऐसा लगता है कि एक 'पाइप डिवाइस' था), क्या इसका मतलब यह है कि पाइप की संख्या की सीमा थी जो किसी निश्चित समय पर इस्तेमाल की जा सकती थी (यानी, एक सीमा थी) कितनी बार आप |एक कमांड में) का उपयोग कर सकते हैं ?
मलय

2
मैंने ऐसी सीमा कभी नहीं देखी थी, और मुझे नहीं लगता कि सिद्धांत में कभी एक था। व्यवहार में, फ़ाइल नाम के लिए किसी भी चीज की जरूरत होती है, और इनोड की संख्या निश्चित रूप से, परिमित होती है। जैसा कि सिस्टम पर भौतिक पृष्ठों की संख्या है, यदि कुछ और नहीं। आधुनिक प्रणालियाँ 4k परमाणु लिखने की गारंटी देती हैं, इसलिए प्रत्येक पाइप में कम से कम एक पूर्ण 4k पृष्ठ होना चाहिए , जो आपके पास पाइप की संख्या पर एक कठिन सीमा रखता है। लेकिन राम की एक जोड़ी गीगाबाइट होने पर विचार करें ... व्यावहारिक रूप से, यह एक सीमा है जिसका आप कभी सामना नहीं करेंगे। कोशिश करें और एक टर्मिनल पर कुछ मिलियन पाइप टाइप करें ... :)
डेमन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.