2008-12-12 15 views
9

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

  • यह प्राप्त अंत इनपुट के लिए इंतज़ार कर अवरुद्ध कर देगा, जो मैं उम्मीद करेंगे की तरह लगता है, लेकिन किसी को धारा से पढ़ने के लिए भेजने के अंत ब्लॉक कभी कभी इंतजार कर देगा?
  • यदि मैं धारा में एक ईओएफ लिखता हूं, तो क्या मैं इसे बंद करने तक उस स्ट्रीम को लिखना जारी रख सकता हूं?
  • क्या नाम और अज्ञात पाइप के व्यवहार में अंतर है?
  • क्या इससे कोई फर्क पड़ता है कि मैंने पाइप के किस छोर को पहले नामित पाइप के साथ खोल दिया था?
  • क्या विभिन्न लिनक्स सिस्टम के बीच पाइप का व्यवहार संगत है?
  • क्या पाइप का व्यवहार उस शैल पर निर्भर करता है जिसका मैं उपयोग कर रहा हूं या जिस तरह से मैंने इसे कॉन्फ़िगर किया है?
  • क्या कोई अन्य प्रश्न पूछना चाहिए या मुझे समस्याएं होनी चाहिए, मुझे इस बारे में पता होना चाहिए कि क्या मैं इस तरह से पाइप का उपयोग करना चाहता हूं?

उत्तर

4

वाह, यह बहुत सारे प्रश्न हैं। चलो अगर मैं सब कुछ कवर कर सकते हैं देखते हैं ...

यह ब्लॉक इनपुट के लिए इंतजार कर रहे होंगे, जो मैं उम्मीद करेंगे

आप उम्मीद सही ढंग से एक वास्तविक 'पढ़ा' कॉल करेंगे प्राप्त अंत की तरह लगता है तब तक ब्लॉक करें जब तक कुछ न हो। हालांकि, मेरा मानना ​​है कि कुछ सी फ़ंक्शंस हैं जो आपको पाइप में (और कितना) इंतजार कर रहे हैं पर 'चोटी' करने की अनुमति देंगे। दुर्भाग्यवश, मुझे याद नहीं है कि यह ब्लॉक भी है या नहीं।

भेजने अंत ब्लॉक कभी कभी किसी धारा

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

अगर मैं धारा के लिए एक EOF बारे में मैं जब तक मैं इसे बंद यह

मैं इस आप किस भाषा का प्रयोग कर रहे हैं पर निर्भर करता है और इसके कार्यान्वयन की सोच सकता है कि धारा को लिखना जारी रखने के रख कर सकते हैं पाइप। सी में, मैं नहीं कहूंगा। एक लिनक्स खोल में, मैं हाँ कहूंगा। अधिक अनुभव वाले किसी और को इसका उत्तर देना होगा।

वहाँ व्यवहार नामित और बेनाम पाइप में अंतर है? जहाँ तक मुझे पता है, हाँ। हालांकि, मुझे नाम बनाम अज्ञात नाम के साथ ज्यादा अनुभव नहीं है।मेरा मानना ​​है कि अंतर है:

  • एकल दिशा द्विदिश संचार
  • पढ़ना बनाम और "में" और "बाहर" एक धागा

बात यह है की धाराओं के लिए लिख जो पाइप I नामित पाइप के साथ पहले खुला?

आम तौर पर नहीं, लेकिन आप एक दूसरे के साथ धागे बनाने और जोड़ने की कोशिश करने के प्रारंभ में समस्याओं में भाग ले सकते हैं। आपको एक मुख्य धागा होना चाहिए जो सभी उप-धागे बनाता है और एक दूसरे के साथ अपने संबंधित पाइप सिंक करता है।

क्या विभिन्न लिनक्स सिस्टम के बीच पाइप का व्यवहार लगातार है?

फिर, यह किस भाषा पर निर्भर करता है, लेकिन आम तौर पर हां। कभी POSIX के बारे में सुना है? यह मानक है (कम से कम लिनक्स के लिए, विंडोज़ अपनी खुद की चीज करता है)।

पाइप के व्यवहार खोल मैं उपयोग कर रहा हूँ या जिस तरह से मैं गया है इसे कॉन्फिगर पर निर्भर करता है?

यह एक भूरे रंग के क्षेत्र में थोड़ा और अधिक हो रहा है। उत्तर होना चाहिए क्योंकि शेल अनिवार्य रूप से सिस्टम कॉल कर रहा है। हालांकि, उस बिंदु तक सब कुछ पकड़ने के लिए ऊपर है।

आपका कोई और प्रश्न मैं

प्रश्न से पता चलता है आप इस प्रणाली के एक सभ्य समझ है पूछा है पूछ किया जाना चाहिए। शोध रखें और ध्यान दें कि आप किस स्तर पर काम कर रहे हैं (खोल, सी, इतने पर)। हालांकि आप इसे आजमाकर बहुत कुछ सीखेंगे।

+0

stat() के साथ एक पाइप की सामग्री पर peeking सभी प्लेटफार्मों में भरोसेमंद नहीं है। –

+1

पाइप बफर भरने पर लेखन अंत ब्लॉक कर सकता है - यह बहुत बड़ा नहीं है। –

+1

क्रॉस-मशीन पाइप हैं ... मौजूद नहीं हैं? निकटतम दृष्टिकोण शायद एक सॉकेट है, लेकिन यह एक पाइप के समान नहीं है। –

4

यह सब यूनिक्स जैसी प्रणाली पर आधारित है; मैं विंडोज के हाल के संस्करणों के विशिष्ट व्यवहार से परिचित नहीं हूं।

ऐसा लगता है कि प्राप्त करने वाला अंत इनपुट के लिए इंतजार कर रहा है, जो मैं उम्मीद करता हूं, लेकिन कभी-कभी प्रेषण अंत ब्लॉक किसी को धारा से पढ़ने के लिए इंतजार कर रहा है?

हां, हालांकि आधुनिक मशीन पर यह अक्सर नहीं हो सकता है। पाइप में एक इंटरमीडिएट बफर है जो संभावित रूप से भर सकता है। यदि ऐसा होता है, तो पाइप का लेखन पक्ष वास्तव में अवरुद्ध होगा। लेकिन अगर आप इसके बारे में सोचते हैं, तो ऐसी कई फाइलें नहीं हैं जो इसे जोखिम देने के लिए काफी बड़ी हैं।

यदि मैं धारा में एक ईओएफ लिखता हूं तो क्या मैं इसे बंद करने तक उस स्ट्रीम को लिखना जारी रख सकता हूं?

उम, आपका मतलब CTRL-D, 0x04 जैसा है? निश्चित रूप से, जब तक स्ट्रीम इस तरह से स्थापित किया जाता है। अर्थात।

506 # cat | od -c 
abc 
^D 
efg 
0000000 a b c \n 004 \n e f g \n       
0000012 

वहाँ व्यवहार नामित और बेनाम पाइप में अंतर है?

हां, लेकिन वे सूक्ष्म और कार्यान्वयन निर्भर हैं। सबसे बड़ा यह है कि दूसरे नाम चलने से पहले आप एक नामित पाइप को लिख सकते हैं; अज्ञात पाइप के साथ, फाइल डिस्क्रिप्टर कांटा/निष्पादन प्रक्रिया के दौरान साझा किया जाता है, इसलिए प्रक्रियाओं के बिना क्षणिक बफर तक पहुंचने का कोई तरीका नहीं है।

क्या इससे कोई फर्क पड़ता है कि मैंने पाइप के किस छोर को पहले नामित पाइप के साथ खोल दिया था?

नहीं।

क्या विभिन्न लिनक्स सिस्टम के बीच पाइप का व्यवहार संगत है?

कारण के भीतर, हाँ। बफर आकार आदि भिन्न हो सकते हैं।

क्या पाइप का व्यवहार उस खोल पर निर्भर करता है जिसका मैं उपयोग कर रहा हूं या जिस तरह से मैंने इसे कॉन्फ़िगर किया है?

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

जनक:

create pipe, returning two file descriptors, call them fd[0] and fd[1] 
fork write-side process 
fork read-side process 

लिखें साइड:

close fd[0] 
connect fd[1] to stdout 
exec writer program 

पढ़ें साइड:

close fd[1] 
connect fd[0] to stdin 
exec reader program 

आपका कोई और प्रश्न मैं पूछ किया जाना चाहिए या मुद्दों मैं अगर मैं इस तरह से पाइप का उपयोग करना चाहते के बारे में पता होना चाहिए रहे हैं?

क्या आप वास्तव में ऐसा कुछ करना चाहते हैं जो इस तरह की रेखा में बाहर निकलना चाहते हैं? यदि नहीं, तो आप अधिक सामान्य वास्तुकला के बारे में सोचना चाहेंगे। लेकिन अंतर्दृष्टि जिसमें पाइप के "संकीर्ण" इंटरफेस के माध्यम से कई अलग-अलग प्रक्रियाएं चल रही हैं वांछनीय है एक अच्छा है।

[अपडेट किया गया: मेरे पास फ़ाइल डिस्क्रिप्टर इंडेक्स पहले उल्टा था। वे अभी सही हैं, man 2 pipe देखें।]

+0

समस्या 1: आप किसी फ़ाइल या पाइप पर ईओएफ नहीं लिख सकते हैं; आप EOF को इंगित करने के लिए फ़ाइल या पाइप को बंद करते हैं (या, फ़ाइल के साथ, आप इसे छोटा कर सकते हैं)। टर्मिनल के संदर्भ में नियंत्रण-डी ईओएफ नहीं है। –

+0

समस्या 2: आप रिसीवर होने से पहले नामित पाइप पर नहीं लिख सकते हैं, भले ही आप O_NONBLOCK के साथ खुलते हैं। –

+0

समस्या 3: अधिकांश प्रणालियों पर, पाइप बफर आकार 4096 या 5120 बाइट्स की तरह काफी छोटा है। इसे भरने के लिए यह बहुत अधिक आउटपुट नहीं लेता है। –

4

जैसा कि दशूनून और चार्ली मार्टिन ने नोट किया, यह एक बड़ा सवाल है। उनके उत्तरों के कुछ हिस्से गलत हैं, इसलिए मैं भी जवाब देने जा रहा हूं।

मुझे अलग-अलग प्रोग्राम मॉड्यूल लिखने में दिलचस्पी है जो स्वतंत्र धागे के रूप में चलते हैं जिन्हें मैं पाइप के साथ जोड़ सकता हूं।

एक भी प्रक्रिया के धागे के बीच एक संचार तंत्र के रूप में पाइप का उपयोग करने की कोशिश कर रहा से सावधान रहें। क्योंकि आप दोनों एक ही प्रक्रिया में खुले पाइप के सिरों को पढ़ और लिखते हैं, तो आपको कभी भी ईओएफ (शून्य बाइट्स) संकेत नहीं मिलेगा।

तुम सच में प्रक्रियाओं की चर्चा करते हुए किया गया है, तो इस निर्माण उपकरण के लिए क्लासिक यूनिक्स दृष्टिकोण का आधार है। मानक यूनिक्स प्रोग्राम में से कई फ़िल्टर मानक हैं जो मानक इनपुट से पढ़ते हैं, इसे किसी भी तरह बदलते हैं, और मानक आउटपुट को परिणाम लिखते हैं। उदाहरण के लिए, tr, sort, grep, और cat सभी फिल्टर, नाम लेकिन कुछ कर रहे हैं। जब आप मैनिपुलेटिंग कर रहे हैं तो यह अनुपालन करने के लिए यह एक उत्कृष्ट प्रतिमान है। सभी डेटा मैनिप्ल्यूशन इस दृष्टिकोण के अनुकूल नहीं हैं, लेकिन ऐसे कई हैं।

प्रेरणा हो सकता है कि मैं लिख सकता है और पूरी तरह से स्वतंत्र रूप से प्रत्येक मॉड्यूल का परीक्षण, शायद यह भी उन्हें अलग अलग भाषाओं में लिखते हैं, या विभिन्न मशीनों पर विभिन्न मॉड्यूल चलाते हैं।

अच्छा अंक। , जानते हैं कि वास्तव में मशीनों के बीच एक पाइप कार्यप्रणाली नहीं है हो सकता है कि आप इस तरह के rsh या (बेहतर) ssh के रूप में कार्यक्रमों के साथ इसे करने के लिए करीब प्राप्त कर सकते हैं। हालांकि, आंतरिक रूप से, ऐसे कार्यक्रम पाइप से स्थानीय डेटा पढ़ सकते हैं और उस डेटा को दूरस्थ मशीनों पर भेज सकते हैं, लेकिन वे पाइप का उपयोग न करने वाले सॉकेट पर मशीनों के बीच संवाद करते हैं।

यहां संभावनाओं की एक विस्तृत विविधता हैं। मैंने थोड़ी देर के लिए पाइपिंग का उपयोग किया है, लेकिन मैं इसके व्यवहार की बारीकियों से अपरिचित हूं।

ठीक; प्रश्न पूछना सीखने के लिए एक (अच्छा) तरीका है। प्रयोग निश्चित रूप से एक और है।

ऐसा लगता है कि प्राप्त करने वाला अंत इनपुट के लिए इंतजार कर रहा है, जो मैं उम्मीद करता हूं, लेकिन कभी-कभी भेजना अंत ब्लॉक किसी को धारा से पढ़ने के लिए इंतजार कर रहा है?

हां। एक पाइप बफर के आकार की एक सीमा है। क्लासिकल, यह काफी छोटा था - 40 9 6 या 5120 आम मूल्य थे। आप पाते हैं कि आधुनिक लिनक्स एक बड़ा मूल्य का उपयोग करता है। आप पाइप बफर के आकार का पता लगाने के लिए fpathconf() और _PC_PIPE_BUF का उपयोग कर सकते हैं। POSIX केवल बफर को 512 होने की आवश्यकता है (यानी, _POSIX_PIPE_BUF 512 है)।

यदि मैं धारा में एक ईओएफ लिखता हूं तो क्या मैं इसे बंद करने तक उस स्ट्रीम को लिखना जारी रख सकता हूं?

तकनीकी रूप से, स्ट्रीम में ईओएफ लिखने का कोई तरीका नहीं है; आप ईओएफ इंगित करने के लिए पाइप डिस्क्रिप्टर बंद करते हैं। यदि आप नियंत्रण-डी या नियंत्रण-जेड को ईओएफ चरित्र के रूप में सोच रहे हैं, तो वे केवल नियमित वर्ण हैं जहां तक ​​पाइप का संबंध है - वे केवल ईओएफ की तरह प्रभाव डालते हैं जब टर्मिनल पर टाइप किया जाता है जो कैनोलिक मोड में चल रहा है (पकाया जाता है , या सामान्य)।

क्या नामित नाम और नामित पाइप में अंतर है?

हाँ, और नहीं। सबसे बड़ा अंतर यह है कि अज्ञात पाइप एक प्रक्रिया द्वारा स्थापित किए जाने चाहिए और केवल उस प्रक्रिया और बच्चों द्वारा उपयोग किया जा सकता है जो उस प्रक्रिया को एक सामान्य पूर्वजों के रूप में साझा करते हैं। इसके विपरीत, नामित पाइप का उपयोग पूर्व असंबद्ध प्रक्रियाओं द्वारा किया जा सकता है। अगला बड़ा अंतर पहले का परिणाम है; एक अनाम पाइप के साथ, आप वापस एक भी समारोह (प्रणाली) pipe() करने के लिए कॉल से दो फ़ाइल वर्णनकर्ता मिलता है, लेकिन आप एक फीफो या नामित पाइप नियमित open() समारोह का उपयोग कर खोलें।(किसी को इसे खोलने से पहले mkfifo() कॉल के साथ एक फीफो बनाना होगा; अज्ञात पाइपों को ऐसे पूर्व सेटअप की आवश्यकता नहीं है।) हालांकि, एक बार जब आपके पास फ़ाइल डिस्क्रिप्टर खुलता है, तो नामित पाइप और एक अज्ञात पाइप के बीच मूल्यवान अंतर होता है ।

क्या इससे कोई फर्क पड़ता है कि मैंने पाइप के किस छोर को पहले नामित पाइप के साथ खोल दिया था?

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

विभिन्न लिनक्स सिस्टम के बीच लगातार पाइप के व्यवहार है?

हां। मैंने उन प्रणालियों पर पाइप के साथ किसी भी समस्या के बारे में सुना या अनुभव नहीं किया है जहां मैंने उनका उपयोग किया है।

क्या पाइप का व्यवहार उस खोल पर निर्भर करता है जिसका मैं उपयोग कर रहा हूं या जिस तरह से मैंने इसे कॉन्फ़िगर किया है?

नहीं: पाइप और FIFOs खोल आप उपयोग से स्वतंत्र हैं।

क्या कोई अन्य प्रश्न पूछना चाहिए या मुझे समस्याएं होनी चाहिए, मुझे इस बारे में पता होना चाहिए कि क्या मैं इस तरह से पाइप का उपयोग करना चाहता हूं?

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


मैंने ऊपर और टिप्पणियों में दोनों उत्तरों को देखा कि पाइप बफर शास्त्रीय रूप से काफी छोटे आकार तक सीमित हैं। @ चार्ली मार्टिन ने टिप्पणी की कि यूनिक्स के कुछ संस्करणों में गतिशील पाइप बफर हैं और ये काफी बड़े हो सकते हैं।

मुझे यकीन नहीं है कि उनके मन में कौन सा है।

#include <unistd.h> 
#include <signal.h> 
#include <stdio.h> 
#include <fcntl.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <string.h> 

static const char *arg0; 

static void err_syserr(char *str) 
{ 
    int errnum = errno; 
    fprintf(stderr, "%s: %s - (%d) %s\n", arg0, str, errnum, strerror(errnum)); 
    exit(1); 
} 

int main(int argc, char **argv) 
{ 
    int pd[2]; 
    pid_t kid; 
    size_t i = 0; 
    char buffer[2] = "a"; 
    int flags; 

    arg0 = argv[0]; 

    if (pipe(pd) != 0) 
     err_syserr("pipe() failed"); 
    if ((kid = fork()) < 0) 
     err_syserr("fork() failed"); 
    else if (kid == 0) 
    { 
     close(pd[1]); 
     pause(); 
    } 
    /* else */ 
    close(pd[0]); 
    if (fcntl(pd[1], F_GETFL, &flags) == -1) 
     err_syserr("fcntl(F_GETFL) failed"); 
    flags |= O_NONBLOCK; 
    if (fcntl(pd[1], F_SETFL, &flags) == -1) 
     err_syserr("fcntl(F_SETFL) failed"); 
    while (write(pd[1], buffer, sizeof(buffer)-1) == sizeof(buffer)-1) 
    { 
     putchar('.'); 
     if (++i % 50 == 0) 
      printf("%u\n", (unsigned)i); 
    } 
    if (i % 50 != 0) 
     printf("%u\n", (unsigned)i); 
    kill(kid, SIGINT); 
    return 0; 
} 

मैं अन्य प्लेटफार्मों से अतिरिक्त परिणाम पाने के लिए उत्सुक हो जाएगा: मैं परीक्षण कार्यक्रम कि सोलारिस, AIX, HP-UX, MacOS X, लिनक्स और Cygwin/Windows XP (नीचे दिए गए परिणामों) पर इस प्रकार का इस्तेमाल किया। मुझे मिले आकार यहां दिए गए हैं। मेरे परिणाम अपेक्षा से बड़े हैं, मुझे कबूल करना होगा, लेकिन जब बफर आकार की बात आती है तो चार्ली और मैं 'काफी बड़े' के अर्थ पर बहस कर रहे हैं।

  •   8196 - IA-64 के लिए HP-UX 11.23 (fcntl (F_SETFL) विफल)
  • 16384 - Solaris 10
  • 16384 - MacOS X 10.5 (O_NONBLOCK काम नहीं किया है, हालांकि fcntl (F_SETFL) असफल नहीं हुआ)
  • 32768 - AIX 5।3
  • 65536 - Cygwin/Windows XP (O_NONBLOCK, काम नहीं किया है, हालांकि fcntl (F_SETFL) असफल नहीं किया था)
  • 65536 - SuSE Linux 10 (और CentOS) (fcntl (F_SETFL) में विफल रहा है)

इन बिंदुओं से स्पष्ट एक बिंदु यह है कि O_NONBLOCK कुछ प्लेटफ़ॉर्म पर पाइप के साथ काम करता है, न कि दूसरों पर।

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

+0

यह एक उत्कृष्ट उत्तर है। – jfs

+0

CentOS - fcntl (F_SETFL) विफल रहा; उबंटू - बस के बाद ब्लॉक ... 65500। – jfs

+0

धन्यवाद, जे एफ सेबेस्टियन। मैं एसएसई के साथ भी यही देखता हूं; 65536 की सीमा अनुमानित है। –

संबंधित मुद्दे