2012-09-27 40 views
16

बनने के लिए कैसे चुना गया है (मुझे नहीं पता कि मुझे यह क्यों मुश्किल समय मिल रहा है, लेकिन मैं कुछ लिनक्स कोड देख रहा हूं जहां हम कुछ लिनक्स कोड देख रहे हैं select() का उपयोग कर फाइल डिस्क्रिप्टर पर इसके तैयार होने की प्रतीक्षा कर रहा है। चयन का आदमी पृष्ठ से:क्या कोई मुझे उदाहरण दे सकता है कि एक एफडी को "तैयार"

select() and pselect() allow a program to monitor multiple file descriptors, 
waiting until one or more of the file descriptors become "ready" for some 
class of I/O operation 

तो, कि बहुत अच्छा है ... मैं कुछ वर्णनकर्ता पर चुनिंदा फोन, यह कुछ समय बाहर मूल्य देने के लिए और संकेत जाने के लिए प्रतीक्षा करने के लिए शुरू करते हैं। फाइल डिस्क्रिप्टर (या वर्णनकर्ता का मालिक) रिपोर्ट करता है कि यह "तैयार" है कि select() कथन रिटर्न देता है?

+1

http://beej.us/guide/bgnet/output/html/multipage/selectman.html –

+1

@ निकोलई एनफेटिसोव - आपके लिंक से, 'चयन() रिटर्न के बाद, सेट में मान दिखाए जाएंगे पढ़ने या लिखने के लिए तैयार हैं, और जिनके पास अपवाद हैं। 'तो' चयन() 'की वापसी के कारण हमें बताया गया कि सॉकेट पढ़ने के लिए तैयार है? यही कारण है कि मुझे – Mike

+0

समझ में नहीं आता है जब इन-कर्नेल नेटवर्क स्टैक का पता लगाता है कि किसी भी सॉकेट डिस्क्रिप्टर पर लंबित कोई ईवेंट है, तो आपकी प्रक्रिया प्रतीक्षा से और 'चयन' रिटर्न से जागृत हो जाती है। एफडी सेट इन-आउट पैरामीटर हैं - आप कर्नेल को बताते हैं कि आप किसमें रुचि रखते हैं, यह आपको बताता है कि क्या हुआ। –

उत्तर

23

यह रिपोर्ट करता है कि यह लौटने के लिए तैयार है।

select आमतौर पर आपके प्रोग्राम के नियंत्रण से बाहर होने वाली घटनाओं की प्रतीक्षा करता है। संक्षेप में, select पर कॉल करके, आपका प्रोग्राम कहता है "मेरे पास तब तक कुछ नहीं है ..., कृपया मेरी प्रक्रिया को निलंबित करें"।

आपके द्वारा निर्दिष्ट शर्त घटनाओं का एक सेट है, जिसमें से कोई भी आपको जगाएगा।

उदाहरण के लिए, यदि आप कुछ डाउनलोड कर रहे हैं, तो आपके लूप को आने के लिए नए डेटा पर इंतजार करना होगा, स्थानांतरण होने पर एक टाइमआउट होना होगा, या उपयोगकर्ता को बाधा डालना होगा, जो ठीक है select करता है।

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

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

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

बारीकियों से एक है कि select मूल रूप से आप एक वादा है कि read या write में से एक मंगलाचरण ब्लॉक नहीं, कॉल खुद के बारे में कोई गारंटी किए बिना देता है।उदाहरण के लिए, यदि बफर स्पेस का एक बाइट उपलब्ध है, तो आप 10 बाइट्स लिखने का प्रयास कर सकते हैं, और कर्नेल वापस आ जाएगा और कहेंगे "मैंने 1 बाइट लिखा है", इसलिए आपको इस मामले को संभालने के लिए भी तैयार रहना चाहिए। एक सामान्य दृष्टिकोण एक बफर "डेटा को इस एफडी में लिखा जाना है", और जब तक यह खाली नहीं है, एफडी को लिखने के सेट में जोड़ा जाता है, और "लिखने योग्य" घटना को सभी लिखने का प्रयास करके संभाला जाता है वर्तमान में बफर में डेटा। यदि बफर बाद में खाली है, तो ठीक है, अगर नहीं, तो बस "लिखने योग्य" पर प्रतीक्षा करें।

"असाधारण" सेट का शायद ही कभी उपयोग किया जाता है - इसका उपयोग उन प्रोटोकॉल के लिए किया जाता है, जिनमें डेटा के हस्तांतरण के लिए संभव है, जबकि डेटा को स्थानांतरित करने के लिए संभव है, जबकि अन्य डेटा को पार करने की आवश्यकता है। यदि आपका प्रोग्राम वर्तमान में "पठनीय" फ़ाइल डिस्क्रिप्टर से डेटा स्वीकार नहीं कर सकता है (उदाहरण के लिए, आप डाउनलोड कर रहे हैं, और डिस्क भर चुकी है), तो आप "पठनीय" सेट में वर्णक शामिल नहीं करना चाहते हैं, क्योंकि आप ईवेंट को संभाल नहीं सकते और select तत्काल वापस आने पर वापस आ जाएगा। यदि रिसीवर में "असाधारण" सेट में एफडी शामिल है, और प्रेषक अपने आईपी स्टैक को "तत्काल" डेटा के साथ एक पैकेट भेजने के लिए कहता है, तो रिसीवर तब जागृत हो जाता है, और अनचाहे डेटा को त्यागने और प्रेषक के साथ पुन: सिंक्रनाइज़ करने का निर्णय ले सकता है । telnet प्रोटोकॉल इसका उपयोग करता है, उदाहरण के लिए, Ctrl-C हैंडलिंग के लिए। जब तक आप ऐसे प्रोटोकॉल को डिज़ाइन नहीं कर रहे हैं जिसके लिए ऐसी सुविधा की आवश्यकता होती है, तो आप आसानी से इसे बिना किसी नुकसान के बाहर छोड़ सकते हैं।

अनिवार्य कोड उदाहरण:

#include <sys/types.h> 
#include <sys/select.h> 

#include <unistd.h> 

#include <stdbool.h> 

static inline int max(int lhs, int rhs) { 
    if(lhs > rhs) 
     return lhs; 
    else 
     return rhs; 
} 

void copy(int from, int to) { 
    char buffer[10]; 
    int readp = 0; 
    int writep = 0; 
    bool eof = false; 
    for(;;) { 
     fd_set readfds, writefds; 
     FD_ZERO(&readfds); 
     FD_ZERO(&writefds); 

     int ravail, wavail; 
     if(readp < writep) { 
      ravail = writep - readp - 1; 
      wavail = sizeof buffer - writep; 
     } 
     else { 
      ravail = sizeof buffer - readp; 
      wavail = readp - writep; 
     } 

     if(!eof && ravail) 
      FD_SET(from, &readfds); 
     if(wavail) 
      FD_SET(to, &writefds); 
     else if(eof) 
      break; 
     int rc = select(max(from,to)+1, &readfds, &writefds, NULL, NULL); 
     if(rc == -1) 
      break; 
     if(FD_ISSET(from, &readfds)) 
     { 
      ssize_t nread = read(from, &buffer[readp], ravail); 
      if(nread < 1) 
       eof = true; 
      readp = readp + nread; 
     } 
     if(FD_ISSET(to, &writefds)) 
     { 
      ssize_t nwritten = write(to, &buffer[writep], wavail); 
      if(nwritten < 1) 
       break; 
      writep = writep + nwritten; 
     } 
     if(readp == sizeof buffer && writep != 0) 
      readp = 0; 
     if(writep == sizeof buffer) 
      writep = 0; 
    } 
} 

हम हम बफर स्थान उपलब्ध है और कोई अंत फ़ाइल या पढ़ा तरफ त्रुटि थी, और हम अगर हम डेटा है लिखने के लिए प्रयास करता है, तो पढ़ने के लिए प्रयास बफर में; अगर अंत-फ़ाइल तक पहुंचा है और बफर खाली है, तो हम कर रहे हैं।

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

+0

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

+0

अगर मैं केवल इस फ़ाइल डिस्क्रिप्टर तक पहुंचने वाला हूं, तो मेरे पास कम से कम सॉकेट और पाइप के लिए यह गारंटी है। और अगर कोई और मेरे एफडीएस तक पहुंचता है, तो मुझे वैसे भी अजीब इंटरलीव डेटा मिलेगा। –

+0

हां, यदि आपके पास केवल एक प्रक्रिया में एफडी खुला है, और इसका नाम अज्ञात पाइप या सॉकेट है। लेकिन अगर यह एक नामित पाइप नहीं है। (तकनीकी रूप से, अगर आप किसी भी तरह से जान सकते हैं कि नामित पाइप पढ़ने/लिखने की एकमात्र प्रक्रिया थी, तो यह सच है, लेकिन आप कैसे जानते हैं?) मैं केवल बिंदु बढ़ाता हूं क्योंकि यह एक क्लासिक समस्या उर्फ ​​है "मेरा सर्वर फ्रीज क्यों करता है यादृच्छिक अंतराल? " – rici

7

एक ही आदमी पृष्ठ से:

बाहर निकलने पर, सेट जगह से संकेत मिलता है जो फ़ाइल वर्णनकर्ता वास्तव में स्थिति बदल में संशोधित कर रहे हैं।

तो निर्धारित करने के लिए निर्धारित किए गए सेट पर FD_ISSET() का उपयोग करें कि कौन से एफडी तैयार हो गए हैं।

+1

मुझे यहां बिंदु याद आना चाहिए, मैं पूछ रहा हूं "फाइल डिस्क्रिप्टर को वापस करने के लिए चुनिंदा चयन() क्या है और मैं सुन रहा हूं," जब वे तैयार हों तो रिटर्न का चयन करें, क्योंकि वे हैं तैयार"। "तैयार" सॉकेट की परिभाषा क्या है? – Mike

+2

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