2013-01-03 10 views
11

जैसा कि ज्ञात है, read और write जैसे कुछ अवरुद्ध कॉल -1 वापस आ जाएंगे और errnoEINTR पर सेट करेंगे, और हमें इसे संभालने की आवश्यकता है।ईआईएनटीआर और गैर-अवरुद्ध कॉल

मेरा प्रश्न है: क्या यह गैर-अवरुद्ध कॉल के लिए लागू होता है, उदाहरण के लिए O_NONBLOCK पर सॉकेट सेट करें?

चूंकि मैंने कुछ लेख और स्रोतों को पढ़ा है, इसलिए कहा गया है कि गैर-अवरुद्ध कॉलों को इसके साथ परेशान करने की आवश्यकता नहीं है, लेकिन मुझे इसके बारे में कोई आधिकारिक संदर्भ नहीं मिला है। यदि हां, तो क्या यह अलग-अलग कार्यान्वयन को लागू करता है?

उत्तर

20

मैं आपको इस प्रश्न का एक निश्चित उत्तर नहीं दे सकता, और उत्तर सिस्टम से सिस्टम में और भिन्न हो सकता है, लेकिन मुझे उम्मीद है कि गैर-अवरुद्ध सॉकेट EINTR के साथ कभी विफल नहीं होगा। यदि आप निम्न सॉकेट फ़ंक्शंस bind(), connect(),, और , या उन्हें पॉज़िक्स मानक में देखने के लिए विभिन्न प्रणालियों के मैन पेजों पर नज़र डालें, तो आप कुछ दिलचस्प देखेंगे: इन सभी कार्यों को छोड़कर -1 वापस आ सकता है और errno से EINTR पर सेट करें। एक फ़ंक्शन जिसे EINTR के साथ कभी भी असफल होने के लिए प्रलेखित नहीं किया गया है bind() है। और bind() भी उस सूची का एकमात्र कार्य है जो डिफ़ॉल्ट रूप से कभी भी अवरुद्ध नहीं होगा। तो ऐसा लगता है कि EINTR, read() और write() समेत केवल अवरुद्ध फ़ंक्शन विफल हो सकते हैं, फिर भी यदि ये फ़ंक्शंस कभी भी अवरुद्ध नहीं होते हैं, तो वे EINTR के साथ कभी भी असफल नहीं होंगे और यदि आप O_NONBLOCK का उपयोग करते हैं, तो वे कार्य कभी भी अवरुद्ध नहीं होंगे।

यह एक तार्किक परिप्रेक्ष्य से भी कोई समझ नहीं लेगा। जैसे मान लें कि आप ब्लॉकिंग I/O का उपयोग कर रहे हैं और आप read() पर कॉल करते हैं और इस कॉल को अवरुद्ध करना है, लेकिन जब यह अवरुद्ध हो रहा है, तो आपकी प्रक्रिया में एक सिग्नल भेजा जाता है और इस प्रकार पढ़ने का अनुरोध ublocked है। सिस्टम को इस स्थिति को कैसे संभालना चाहिए? दावा करते हुए कि read() सफल रहा? यह एक झूठ होगा, यह सफल नहीं हुआ क्योंकि कोई डेटा नहीं पढ़ा गया था। दावा करना सफल रहा, लेकिन शून्य बाइट डेटा पढ़ा गया था? यह सही नहीं होगा, क्योंकि "शून्य पढ़ने का परिणाम" का उपयोग एंड-ऑफ-स्ट्रीम (या एंड-ऑफ-स्ट्रीम) को इंगित करने के लिए किया जाता है, इसलिए आपकी प्रक्रिया यह मान लेगी कि कोई डेटा नहीं पढ़ा गया था, क्योंकि एक के अंत फ़ाइल तक पहुंच गई है (या दूसरी तरफ एक सॉकेट/पाइप बंद कर दिया गया है), जो कि मामला नहीं है। यदि आप read() को फिर से कॉल करते हैं, तो यह फ़ाइल का अंत (या अंत-ऑफ-स्ट्रीम) तक नहीं पहुंचा है, यह अधिक डेटा वापस करने में सक्षम होगा। तो यह भी झूठ होगा। आप उम्मीद करते हैं कि यह पढ़ा गया कॉल या तो सफल हो जाता है और डेटा पढ़ता है या किसी त्रुटि के साथ विफल रहता है। इस प्रकार रीड कॉल को असफल होना चाहिए और उस मामले में -1 वापस करना होगा, लेकिन errno मूल्य सिस्टम सेट करेगा? अन्य सभी त्रुटि मान फ़ाइल डिस्क्रिप्टर के साथ एक गंभीर त्रुटि इंगित करते हैं, फिर भी कोई गंभीर त्रुटि नहीं थी और इस तरह की त्रुटि का संकेत भी झूठ होगा। यही कारण है कि errnoEINTR पर सेट किया गया है, जिसका अर्थ है: "स्ट्रीम में कुछ भी गलत नहीं था। आपका पठन कॉल अभी विफल रहा है, क्योंकि यह सिग्नल द्वारा बाधित था। अगर इसे बाधित नहीं किया गया था, तो यह अभी भी सफल हो सकता है, इसलिए यदि आप अभी भी डेटा की देखभाल करते हैं, कृपया पुनः प्रयास करें। "

यदि अब आप गैर-अवरुद्ध I/O पर स्विच करते हैं, तो ऊपर की स्थिति कभी नहीं उभरती है। रीड कॉल कभी भी ब्लॉक नहीं करेगा और यदि यह तुरंत डेटा नहीं पढ़ सकता है, तो यह EAGAIN (POSIX) या EWOULDBLOCK (अनौपचारिक, लिनक्स दोनों एक ही त्रुटि है, इसके लिए वैकल्पिक नाम हैं) के साथ असफल हो जाएगा, जिसका अर्थ है: " अभी कोई डेटा उपलब्ध नहीं है और इस प्रकार आपके पढ़ने के कॉल को अवरुद्ध करना होगा और डेटा आने की प्रतीक्षा करनी होगी, लेकिन अवरोधन की अनुमति नहीं है, इसलिए यह इसके बजाय विफल रहा। " तो उत्पन्न होने वाली हर स्थिति के लिए एक त्रुटि है।

बेशक, यहां तक ​​कि गैर-अवरुद्ध I/O के साथ भी, पढ़ने की कॉल अस्थायी रूप से सिग्नल द्वारा बाधित हो सकती है लेकिन सिस्टम को इसका संकेत क्यों देना होगा? प्रत्येक फ़ंक्शन कॉल, चाहे यह एक सिस्टम फ़ंक्शन है या उपयोगकर्ता द्वारा लिखित एक है, अस्थायी रूप से सिग्नल द्वारा बाधित हो सकता है, वास्तव में हर एक, कोई अपवाद नहीं। अगर सिस्टम को जब भी ऐसा होता है तो उपयोगकर्ता को सूचित करना होगा, EINTR के कारण सभी सिस्टम फ़ंक्शन संभवतः विफल हो सकते हैं। हालांकि, यहां तक ​​कि अगर सिग्नल बाधा होती है, तो फ़ंक्शन आमतौर पर अंत तक अपना कार्य पूरा करते हैं, यही कारण है कि यह बाधा अप्रासंगिक है। त्रुटि EINTR का उपयोग कॉलर को बताने के लिए किया जाता है कि उसने जिस क्रिया का अनुरोध किया है वह सिग्नल बाधा के कारण नहीं किया गया था, लेकिन गैर-अवरुद्ध I/O के मामले में, फ़ंक्शन को पढ़ने या लिखने का कोई कारण नहीं है अनुरोध करें, जब तक कि इसे अभी नहीं किया जा सकता है, लेकिन फिर इसे उचित त्रुटि से इंगित किया जा सकता है।

मेरे सिद्धांत की पुष्टि करने के लिए, मैंने मैकोज़ (10.8) के कर्नेल पर एक नज़र डाली, जो अभी भी काफी हद तक फ्रीबीएसडी कर्नेल पर आधारित है और ऐसा लगता है कि यह संदेह की पुष्टि करता है। यदि कोई रीड कॉल वर्तमान में संभव नहीं है, क्योंकि कोई डेटा उपलब्ध नहीं है, तो कर्नेल फ़ाइल डिस्क्रिप्टर झंडे में O_NONBLOCK ध्वज के लिए जांच करता है। यदि यह ध्वज सेट किया गया है, तो यह EAGAIN के साथ तुरंत विफल हो जाता है। यदि यह सेट नहीं है, तो यह वर्तमान थ्रेड को msleep() नामक फ़ंक्शन को कॉल करके सो जाता है। समारोह documented here है (जैसा कि मैंने कहा था, ओएस एक्स अपने कर्नेल में फ्रीबीएसडी कोड का उपयोग करता है)। यह फ़ंक्शन वर्तमान थ्रेड को तब तक सोने का कारण बनता है जब तक यह स्पष्ट रूप से जागृत नहीं होता है (जो डेटा पढ़ने के लिए तैयार हो जाता है) या एक टाइमआउट मारा गया है (उदाहरण के लिए आप सॉकेट पर टाइमआउट प्राप्त कर सकते हैं)। फिर भी धागा भी जागृत हो जाता है, अगर एक संकेत दिया जाता है, तो msleep() खुद EINTR देता है और अगली उच्च परत बस इस त्रुटि को पास करती है। तो यह msleep() है जो EINTR त्रुटि उत्पन्न करता है, लेकिन यदि O_NONBLOCK ध्वज सेट किया गया है, तो msleep() को पहले स्थान पर कभी नहीं कहा जाता है, इसलिए यह त्रुटि वापस नहीं की जा सकती है।

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

+0

आपके विस्तृत उत्तर के लिए धन्यवाद। – haohaolee

+0

अच्छा स्पष्टीकरण। धन्यवाद। –

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