लिनक्स में पाइप कैसे काम करते हैं


25

मैं इस बारे में पढ़ रहा हूं कि कैसे लिनक्स कर्नेल में पाइप को लागू किया जाता है और मेरी समझ को मान्य करना चाहता है। यदि मैं गलत हूं, तो सही स्पष्टीकरण के साथ उत्तर का चयन किया जाएगा।

  • लिनक्स में एक VFS होता है जिसे पिपिफ कहते हैं जो कर्नेल में रखा जाता है (उपयोगकर्ता स्थान में नहीं)
  • pipefs में एक ही सुपर ब्लॉक होता है और इसे अपने मूल ( pipe:) के साथ लगाया जाता है/
  • अधिकतर फाइल सिस्टमों के विपरीत पिपिफ्स को सीधे नहीं देखा जा सकता है
  • Pipefs के लिए प्रवेश pipe(2)syscall के माध्यम से होता है
  • ऑपरेटर (या किसी अन्य प्रक्रिया से मैन्युअल रूप से) के pipe(2)साथ पाइपिंग के लिए गोले द्वारा उपयोग किया जाने वाला syscall |, pipefs में एक नई फ़ाइल बनाता है जो एक सामान्य फ़ाइल की तरह बहुत अधिक व्यवहार करता है
  • पाइप ऑपरेटर की बाईं ओर की stdoutफ़ाइल पाइपिफ्स में बनाई गई अस्थायी फ़ाइल पर पुनर्निर्देशित हो गई है
  • पाइप ऑपरेटर के दाहिने हाथ की तरफ की stdinफाइल पाइपिफ्स पर फाइल पर सेट है
  • पिपेफ्स को मेमोरी में संग्रहीत किया जाता है और कुछ कर्नेल जादू के माध्यम से, पृष्ठांकित नहीं किया जाना चाहिए

क्या यह स्पष्टीकरण है कि कैसे पाइप (जैसे ls -la | less) बहुत अधिक सही कार्य करते हैं?

एक बात मुझे समझ में नहीं आती है कि कैसे बैश जैसी कोई चीज एक प्रक्रिया तय करती है ' stdinया stdoutफाइल डिस्क्रिप्टर द्वारा लौटा दी जाती है pipe(2)। मैं अभी तक उस बारे में कुछ भी नहीं जान पाया हूं।


ध्यान दें कि आप एक ही नाम के साथ चीजों की दो अलग-अलग परतों के बारे में बात कर रहे हैं। pipe()मशीनरी है कि समर्थन करता है यह (के साथ साथ कर्नेल कॉल pipefs, आदि) से काफी कम स्तर है |ऑपरेटर अपने खोल में की पेशकश की। उत्तरार्द्ध आमतौर पर पूर्व का उपयोग करके कार्यान्वित किया जाता है, लेकिन यह होना जरूरी नहीं है।
ग्रेग हेविगिल

हां, मैं विशेष रूप से निचले स्तर के संचालन का उल्लेख कर रहा हूं, इस धारणा के साथ कि |ऑपरेटर बस pipe(2)एक प्रक्रिया के रूप में कॉल कर रहा है जैसे बैश करता है।
ब्रैंडन वंबोल्ड

जवाबों:


19

अब तक का आपका विश्लेषण आम तौर पर सही है। जिस तरह से एक शेल एक प्रक्रिया के स्टड को पाइप डिस्क्रिप्टर पर सेट कर सकता है वह (स्यूडोकोड) हो सकता है:

pipe(p) // create a new pipe with two handles p[0] and p[1]
fork() // spawn a child process
    close(p[0]) // close the write end of the pipe in the child
    dup2(p[1], 0) // duplicate the pipe descriptor on top of fd 0 (stdin)
    close(p[1]) // close the other pipe descriptor
    exec() // run a new process with the new descriptors in place

धन्यवाद! बस जिज्ञासु क्यों dup2कॉल की आवश्यकता है, और आप सीधे स्टेपल के लिए पाइप डिस्क्रिप्टर को असाइन नहीं कर सकते हैं?
ब्रैंडन वम्बोल्ड

3
कॉलर को यह चुनने के लिए नहीं मिलता है कि फ़ाइल डिस्क्रिप्टर का संख्यात्मक मान क्या है जब इसे बनाया जाता है pipe()dup2()कॉल फोन करने वाले एक विशिष्ट संख्यात्मक मान (जरूरत है क्योंकि 0, 1, 2 stdin कर रहे हैं, stdout, stderr) के लिए फ़ाइल वर्णनकर्ता कॉपी करने के लिए अनुमति देता है। यह "स्टड को सीधे असाइन करने" के बराबर कर्नेल है। ध्यान दें कि C रनटाइम लाइब्रेरी ग्लोबल वैरिएबल stdinएक है FILE *, जो कर्नेल से संबंधित नहीं है (हालांकि इसे वर्णनात्मक 0 से जुड़ा होने के लिए आरंभीकृत किया गया है)।
ग्रेग हेविगिल

बहुत बढ़िया जवाब! मैं विवरण में थोड़ा खो गया हूं। बस यह सोचकर कि आप निष्पादन () चलाने से पहले पास (पी [1]) क्यों करते हैं? एक बार जब d2 लौटता है, तो p [1] fd 0 को इंगित नहीं करेगा? फिर पास (पी [1]) फ़ाइल डिस्क्रिप्टर 0. को बंद कर देता है। फिर हम बच्चे की प्रक्रिया की गति को कैसे पढ़ सकते हैं?
14:15 बजे user1559897

@ user1559897: dup2कॉल नहीं बदलता है p[1]। इसके बजाय, यह दो हैंडल बनाता है p[1]और 0एक ही कर्नेल ऑब्जेक्ट (पाइप) को इंगित करता है। चूँकि बच्चे की प्रक्रिया को दो स्टडिन हैंडल्स की आवश्यकता नहीं होती है (और न जाने क्या-क्या हैंडल किया जाता p[1]है जो वैसे भी होता है), p[1]पहले बंद हो जाता है exec
ग्रेग हेविल

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