2012-03-19 10 views
13

अपने आवेदन में, वहाँ एक कब-धागा है, किलिनक्स का चयन करें() बनाम ppoll() pselect बनाम()

  1. रैपिंग डेटा के लिए समर्पित है एक कस्टम प्रोटोकॉल
  2. में आवेदन से प्राप्त भेजा जा रहा है टीसीपी/आईपी
  3. पर डेटा + कस्टम प्रोटोकॉल पैकेट टीसीपी/आईपी
  4. पर कस्टम + प्रोटोकॉल पैकेट प्राप्त करना कस्टम प्रोटोकॉल को अनवरोधित करना और एप्लिकेशन को डेटा सौंपना।

एप्लिकेशन डेटा को एक अलग थ्रेड पर संसाधित करता है। इसके अतिरिक्त, आवश्यकताओं को निर्देश दिया गया है कि अनजान खिड़की का आकार 1 होना चाहिए, यानी किसी भी समय अनधिकृत संदेश केवल एक लंबित होना चाहिए। इसका तात्पर्य यह है कि अगर आईओ-थ्रेड ने सॉकेट पर एक संदेश भेजा है, तो यह रिसीवर से एक एके सुनता है, जब तक यह कोई और संदेश नहीं भेजेगा। एप्लिकेशन का प्रोसेसिंग थ्रेड पाइप के माध्यम से आईओ-थ्रेड को संचारित करता है। यदि लिनक्स सीएलआई के किसी व्यक्ति ctrl + c टाइप करता है तो एप्लिकेशन को शानदार ढंग से बंद करने की आवश्यकता होती है। इस प्रकार, इन आवश्यकताओं को देखते हुए, मैं निम्नलिखित है विकल्प

  1. उपयोग ppoll() सॉकेट और पाइप वर्णनकर्ता
  2. उपयोग करें()
  3. उपयोग PSelect()

मैं निम्नलिखित है सवाल पर

  1. चयन() और मतदान() के बीच निर्णय। मेरा आवेदन केवल 50 से कम फ़ाइल डिस्क्रिप्टर से संबंधित है। क्या यह मानना ​​ठीक है कि क्या कोई चयन नहीं होगा कि मैं चयन या चुनाव चुनूं?

    1. चयन() और छेड़छाड़() के बीच निर्णय। मैंने लिनक्स दस्तावेज़ीकरण पढ़ा है और यह संकेतों के बीच दौड़ की स्थिति के बारे में बताता है और चयन()। मुझे सिग्नल के साथ अनुभव नहीं है, तो क्या कोई दौड़ की स्थिति के बारे में अधिक स्पष्ट रूप से समझा सकता है और() चुन सकता है? क्या सीएलआई पर ctrl + C दबाकर किसी के साथ कुछ करना है और एप्लिकेशन रोक नहीं रहा है?

    2. छद्म और ppoll() के बीच निर्णय? एक पर कोई विचार बनाम अन्य

उत्तर

20

मैं select() बनाम poll() के साथ तुलना शुरू करके सुझाव दूंगा। लिनक्स pselect() और ppoll() दोनों प्रदान करता है; और अतिरिक्त const sigset_t *pselect() और ppoll() (बनाम select() और poll()) के लिए तर्क प्रत्येक "पी-संस्करण" पर समान प्रभाव डालता है, जैसा कि यह था। यदि आप सिग्नल का उपयोग नहीं कर रहे हैं, तो आपके पास सुरक्षा के लिए कोई दौड़ नहीं है, इसलिए मूल प्रश्न वास्तव में दक्षता और प्रोग्रामिंग की आसानी के बारे में है।

इस बीच पहले से ही एक stackoverflow.com उत्तर है: what are the differences between poll and select

दौड़ के लिए: एक बार जब आप सिग्नल (किसी भी कारण से) का उपयोग शुरू करते हैं, तो आप सीखेंगे कि सामान्य रूप से सिग्नल हैंडलर को volatile sig_atomic_t का एक चर सेट करना चाहिए ताकि सिग्नल का पता चला हो। इसके लिए मूल कारण यह है कि कई लाइब्रेरी कॉल re-entrant नहीं हैं, और एक सिग्नल डिलीवर किया जा सकता है, जबकि आप इस तरह के दिनचर्या में "मध्य में" होते हैं। उदाहरण के लिए, बस stdout (सी) या cout (सी ++) जैसे स्ट्रीम-शैली डेटा संरचना में एक संदेश प्रिंट करना पुन: प्रवेश समस्याएं पैदा कर सकता है।

मान लीजिए आप कोड एक volatile sig_atomic_t flag चर का उपयोग करता है, शायद SIGINT, कुछ इस तरह पकड़ने के लिए है (यह भी http://pubs.opengroup.org/onlinepubs/007904975/functions/sigaction.html देखें):

volatile sig_atomic_t got_interrupted = 0; 
void caught_signal(int unused) { 
    got_interrupted = 1; 
} 
... 
    struct sigaction sa; 
    sa.sa_handler = caught_signal; 
    sigemptyset(&sa.sa_mask); 
    sa.sa_flags = SA_RESTART; 
    if (sigaction(SIGINT, &sa, NULL) == -1) ... handle error ... 
    ... 

अब, अपने कोड के मुख्य शरीर में, आप चाहते हो सकता है करने के लिए "रन बाधित जब तक":

while (!got_interrupted) { 
     ... do some work ... 
    } 

यह ठीक जब तक आप इस तरह के select या 0 के रूप में कॉल है कि कुछ इनपुट/आउटपुट के लिए इंतजार, बनाने के लिए की आवश्यकता होगी, शुरू है। "प्रतीक्षा" कार्रवाई को उस I/O के लिए प्रतीक्षा करने की आवश्यकता है, लेकिन यह भी को SIGINT बाधा के लिए प्रतीक्षा करने की आवश्यकता है। अगर तुम सिर्फ लिखने:

while (!got_interrupted) { 
     ... do some work ... 
     result = select(...); /* or result = poll(...) */ 
    } 

तो यह है कि बाधा नहीं होगा सिर्फ इससे पहले कि आप select() या poll() फोन, बल्कि बाद में की तुलना में संभव है। इस मामले में, आपको बाधा मिली - और परिवर्तनीय got_interrupted सेट हो गया- लेकिन उसके बाद, आप प्रतीक्षा करना शुरू कर देते हैं। प्रतीक्षा करने से पहले आपको got_interrupted वैरिएबल की जांच करनी चाहिए, इसके बाद नहीं।

आप लेखन की कोशिश कर सकते हैं:,

while (!got_interrupted) { 
     ... do some work ... 
     if (!got_interrupted) 
      result = select(...); /* or result = poll(...) */ 
    } 

यह "रेस खिड़की" सिकुड़ता है क्योंकि अब आप बाधा का पता लगा लेंगे अगर ऐसा होता है, जबकि आप "कुछ काम करना" कोड में हैं; लेकिन अभी भी एक दौड़ है, क्योंकि के बाद बाधा हो सकती है, आप चर का परीक्षण करते हैं, लेकिन चयन-या-पोल से ठीक पहले।

समाधान "परीक्षण, तो इंतजार" बनाने के लिए अनुक्रम "परमाणु", sigprocmask का संकेत-अवरुद्ध गुणों का उपयोग करते है (या, POSIX पिरोया में कोड, pthread_sigmask):

sigset_t mask, omask; 
... 
while (!got_interrupted) { 
    ... do some work ... 
    /* begin critical section, test got_interrupted atomically */ 
    sigemptyset(&mask); 
    sigaddset(&mask, SIGINT); 
    if (sigprocmask(SIG_BLOCK, &mask, &omask)) 
     ... handle error ... 
    if (got_interrupted) { 
     sigprocmask(SIG_SETMASK, &omask, NULL); /* restore old signal mask */ 
     break; 
    } 
    result = pselect(..., &omask); /* or ppoll() etc */ 
    sigprocmask(SIG_SETMASK, &omask, NULL); 
    /* end critical section */ 
} 

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

जब तक आप वास्तव में, हालांकि, आप केवल select() और poll() तुलना की जरूरत है (और यदि आप वर्णनकर्ता की बड़ी संख्या की आवश्यकता होगी, शुरू करते हैं, epoll() की तरह घटना के आधार पर सामान में से कुछ या तो एक से अधिक कुशल है) SIGINT को पकड़ने के लिए की आवश्यकता होगी,, शुरू करते हैं।

+0

"इसके लिए मूल कारण यह है कि कई लाइब्रेरी कॉल फिर से प्रवेश नहीं कर रहे हैं" तो क्या होता है जब सिग्नल वितरित किया जाता है जबकि हम "पढ़ने" के बीच में होते हैं। क्या इसका मतलब यह है कि हम कभी भी फिर से पढ़ने का उपयोग नहीं कर सकते? – kptlronyttcna

+0

@ kptlronyttcna: नहीं, और फिर भी थोड़ा हां: आप सुरक्षित रूप से कॉल नहीं कर सकते हैं, उदाहरण के लिए, सिग्नल हैंडलर में stdin पर 'fread'। इसका मतलब यह नहीं है कि आप * कभी * कॉल 'फ्रेड' नहीं कर सकते हैं, बस सिग्नल हैंडलर में साझा चर पर ऐसा नहीं कर सकते हैं। लाइब्रेरी, सिस्टम और अन्य विवरणों के मुताबिक, आप सुरक्षित रूप से क्या कर सकते हैं, और आप क्या नहीं कर सकते हैं इसका सटीक विवरण। (एक साइड पॉइंट के रूप में, जब 'रीड' एक सिस्टम कॉल होता है, क्योंकि यह लिनक्स और बीएसडी और मैक पर है, तो इसका सटीक व्यवहार अंतर्निहित फ़ाइल-सिस्टम ऑब्जेक्ट पर निर्भर करता है। "धीमे" डिवाइस EINTR त्रुटियों या छोटे पढ़ने को वापस कर सकते हैं।) – torek

4
बीच

(पी) का चयन करें और (पी) चुनाव एक नहीं बल्कि सूक्ष्म अंतर है:

चयन के लिए, आप प्रारंभ और बदसूरत fd_set बिटमैप्स को भरने के लिए है हर बार चयन करने से पहले चयन करें क्योंकि चयन उन्हें "विनाशकारी" फैशन में जगह में संशोधित करता है। (में .events और .revents सदस्यों के बीच मतदान भिन्न है)।

चुनने के बाद, पूरे बिटमैप को अक्सर घटनाओं के लिए स्कैन किया जाता है (लोगों/कोड द्वारा) यदि अधिकांश एफडीएस भी नहीं देखे जाते हैं।

तीसरा, बिटमैप केवल उन एफडीएस से निपट सकता है जिनकी संख्या एक निश्चित सीमा से कम है (समकालीन कार्यान्वयन: कहीं 1024..40 9 6 के बीच), जो उन कार्यक्रमों में नियम बनाता है जहां उच्च एफडीएस आसानी से प्राप्त किए जा सकते हैं (इसके बावजूद कार्यक्रमों के बजाय पहले से ही एपोल का उपयोग करने की संभावना है)।

+2

मेरी भावना यह है कि नए लिखित कार्यक्रमों में 'चयन' का उपयोग नहीं किया जाना चाहिए, बल्कि केवल' मतदान '[या' ppoll'] (कम से कम सिस्टम पर नहीं बहुत पुराने लिनक्स जहां दोनों 'चयन' और 'मतदान' मूल प्रणाली कॉल हैं)। मेरा मानना ​​है कि 'चयन' केवल विरासत कार्यक्रमों के लिए है ... –

+1

@ बेसिलस्टारनकेविच उद्धरण की आवश्यकता है ... – paradigmatic

+0

http://www.kegel.com/c10k.html या बस तथ्य यह है कि' fd_set' का आकार अधिकतम सीमा को सीमित कर रहा है 'जनसंपर्क' परमिट से अधिक कसकर फाइल डिस्क्रिप्टर फ़ाइल करें। –

0

स्वीकृत उत्तर सही और चयन के बीच एक अंतर अंतर के साथ सही नहीं है। यह अच्छी तरह से वर्णन करता है कि सिग-हैंडलर और चयन के बीच दौड़ की स्थिति कैसे उत्पन्न हो सकती है, लेकिन समस्या को हल करने के लिए यह गलत तरीके से उपयोग करने में गलत है। यह छेड़छाड़ के बारे में मुख्य बिंदु को याद करता है जो कि यह फ़ाइल-वर्णनकर्ता या सिग्नल तैयार होने के लिए इंतजार कर रहा है। जब इनमें से कोई भी तैयार हो तो पिक्सेल रिटर्न। केवल फ़ाइल-डिस्क्रिप्टर पर प्रतीक्षा करें। संकेतों को अनदेखा करें का चयन करें। एक अच्छा काम करने के उदाहरण के लिए इस ब्लॉग पोस्ट को देखें: https://www.linuxprogrammingblog.com/code-examples/using-pselect-to-avoid-a-signal-race

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