2016-01-05 8 views
9

मैं जंग में एक छोटा ncurses आवेदन कर रहा हूँ जिसे एक बच्चे की प्रक्रिया के साथ संवाद करने की जरूरत है। मेरे पास पहले से ही आम लिस्प में लिखा प्रोटोटाइप है; gif here उम्मीदपूर्वक दिखाएगा कि मैं क्या करना चाहता हूं। मैं इसे फिर से लिखने की कोशिश कर रहा हूं क्योंकि सीएल ऐसे छोटे उपकरण के लिए बड़ी मात्रा में स्मृति का उपयोग करता है।जंग में अवरुद्ध किए बिना मैं बाल प्रक्रिया के आउटपुट को कैसे पढ़ूं?

मैंने जंग (या अन्य निम्न स्तर की भाषाओं) से पहले जंग का उपयोग नहीं किया है, और मुझे उप-प्रक्रिया के साथ बातचीत करने के तरीके को समझने में कुछ परेशानी हो रही है।

let mut program = match Command::new(command) 
    .args(arguments) 
    .stdin(Stdio::piped()) 
    .stdout(Stdio::piped()) 
    .stderr(Stdio::piped()) 
    .spawn() { 
     Ok(child) => child, 
     Err(_) => { 
      println!("Cannot run program '{}'.", command); 
      return; 
     }, 
    }; 
  • एक अनंत (उपयोगकर्ता बाहर निकलता है जब तक) पाश के पास यह है, जो पढ़ता है और इनपुट के प्रबंधन:

    1. प्रक्रिया बनाएं:

      क्या मैं वर्तमान में कर रहा हूँ मोटे तौर पर यह है और इस तरह के आउटपुट के लिए सुनता है (और इसे स्क्रीन पर लिखता है):

      fn listen_for_output(program: &mut Child, 
              output_viewer: &TextViewer) { 
          match program.stdout { 
           Some(ref mut out) => { 
            let mut buf_string = String::new(); 
            match out.read_to_string(&mut buf_string) { 
             Ok(_) => output_viewer.append_string(buf_string), 
             Err(_) => return, 
            }; 
           }, 
           None => return, 
          }; 
      } 
      

    read_to_string पर कॉल हालांकि प्रक्रिया समाप्त होने तक प्रोग्राम को अवरुद्ध करता है। मैं देख सकता हूं कि read_to_end और read भी ब्लॉक करना प्रतीत होता है। अगर मैं ls जैसे कुछ चलाने की कोशिश करता हूं जो तुरंत बाहर निकलता है, तो यह काम करता है, लेकिन कुछ ऐसा जो python या sbcl से बाहर नहीं निकलता है, यह केवल तब ही जारी रहता है जब मैं सबप्रोसेस मैन्युअल रूप से मारता हूं।

    संपादित करें:

    fn listen_for_output(program: &mut Child, 
            output_viewer: &TextViewer) { 
        match program.stdout.as_mut() { 
         Some(out) => { 
          let buf_reader = BufReader::new(out); 
          for line in buf_reader.lines() { 
           match line { 
            Ok(l) => { 
             output_viewer.append_string(l); 
            }, 
            Err(_) => return, 
           }; 
          } 
         }, 
         None => return, 
        } 
    } 
    

    हालांकि समस्या अभी भी एक ही रहता है:

    पर this answer आधार पर, मैं BufReader उपयोग करने के लिए कोड बदल दिया है। यह उपलब्ध सभी लाइनें पढ़ेगा, और फिर ब्लॉक करें। चूंकि टूल को किसी भी प्रोग्राम के साथ काम करना है, इसलिए पढ़ने का प्रयास करने से पहले उत्पादन समाप्त होने पर अनुमान लगाने का कोई तरीका नहीं है। BufReader के लिए टाइमआउट सेट करने का कोई तरीका प्रतीत नहीं होता है।

  • उत्तर

    11

    स्ट्रीम डिफ़ॉल्ट रूप से अवरुद्ध कर रहे हैं। टीसीपी/आईपी धाराएं, फाइल सिस्टम स्ट्रीम, पाइप स्ट्रीम, वे सभी अवरुद्ध कर रहे हैं। जब आप बाइट्स का एक हिस्सा देने के लिए एक स्ट्रीम बताते हैं तो यह रुक जाएगा और जब तक कि बाइट्स के दिए गए एमओटी तक नहीं रहेगा तब तक प्रतीक्षा करेंगी (कुछ interrupt, स्ट्रीम का अंत, एक त्रुटि)।

    ऑपरेटिंग सिस्टम डेटा को पढ़ने की प्रक्रिया में वापस करने के लिए उत्सुक हैं, इसलिए यदि आप चाहते हैं कि अगली पंक्ति के लिए प्रतीक्षा करें और जैसे ही यह आता है, तो Unable to pipe to or from spawned child process more than once में शेम्प्स्टर द्वारा सुझाई गई विधि को संभालें। (सिद्धांत रूप में ऐसा नहीं है, क्योंकि एक ऑपरेटिंग सिस्टम को BufReaderread में अधिक डेटा के लिए प्रतीक्षा करने की अनुमति है, लेकिन व्यवहार में ऑपरेटिंग सिस्टम प्रतीक्षा करने के लिए प्रारंभिक "संक्षिप्त पढ़ता" पसंद करते हैं)।

    यह सरल BufReader-आधारित दृष्टिकोण कई धाराओं को संभालने की आवश्यकता होने पर काम करना बंद कर देता है (जैसे stdout और stderr बच्चे प्रक्रिया के) या एकाधिक प्रक्रियाएं। उदाहरण के लिए, BufReader-आधारित दृष्टिकोण डेडलॉक हो सकता है जब कोई बच्चा आपके stderr पाइप को निकालने के लिए प्रतीक्षा करता है, जबकि आपकी प्रक्रिया को खाली होने पर stdout पर अवरुद्ध कर दिया जाता है।

    इसी तरह, आप BufReader का उपयोग नहीं कर सकते हैं जब आप नहीं चाहते कि आपका प्रोग्राम अनिश्चित काल तक बाल प्रक्रिया पर प्रतीक्षा करे। शायद आप एक प्रगति पट्टी या टाइमर प्रदर्शित करना चाहते हैं जबकि बच्चा अभी भी काम कर रहा है और आपको कोई आउटपुट नहीं देता है।

    आप BufReader-आधारित दृष्टिकोण का उपयोग नहीं कर सकते हैं यदि आपका ऑपरेटिंग सिस्टम प्रक्रिया में डेटा लौटने में उत्सुक नहीं होता है ("पूर्ण पढ़ने" को "पूर्ण पढ़ता है" पसंद करता है) क्योंकि उस मामले में कुछ अंतिम पंक्तियां मुद्रित होती हैं बाल प्रक्रिया द्वारा भूरे रंग के क्षेत्र में समाप्त हो सकता है: ऑपरेटिंग सिस्टम उन्हें मिला, लेकिन वे BufReader के बफर को भरने के लिए पर्याप्त नहीं हैं।

    BufReaderRead इंटरफ़ेस को स्ट्रीम के साथ करने की अनुमति देता है, यह अंतर्निहित धारा से कम अवरोध नहीं है। कुशल होने के लिए यह read भाग में इनपुट होगा, ऑपरेटिंग सिस्टम को जितना अधिक बफर भरने के लिए कह रहा है उतना भरने के लिए कह रहा है।

    आप सोच रहे होंगे कि क्यों भाग में डेटा पढ़ना इतना महत्वपूर्ण है, BufReader क्यों बाइट द्वारा डेटा बाइट नहीं पढ़ सकता। समस्या यह है कि स्ट्रीम से डेटा को पढ़ने के लिए हमें ऑपरेटिंग सिस्टम की मदद की ज़रूरत है। दूसरी तरफ, हम ऑपरेटिंग सिस्टम नहीं हैं, हम इससे अलग काम करते हैं, इसलिए अगर हमारी प्रक्रिया में कुछ गड़बड़ हो तो इसके साथ गड़बड़ न करें। इसलिए ऑपरेटिंग सिस्टम को कॉल करने के लिए "कर्नेल मोड" में एक संक्रमण होना आवश्यक है, जिसमें "संदर्भ स्विच" भी हो सकता है। यही कारण है कि प्रत्येक बाइट को पढ़ने के लिए ऑपरेटिंग सिस्टम को कॉल करना महंगा है। हम जितना संभव हो उतने ओएस कॉल चाहते हैं और इसलिए हमें बैच में स्ट्रीम डेटा मिलता है।

    अवरुद्ध किए बिना स्ट्रीम पर प्रतीक्षा करने के लिए आपको गैर-अवरुद्ध धारा की आवश्यकता होगी। एमआईओ promises to have the required non-blocking stream support for pipes, शायद PipeReader के साथ, लेकिन मैंने अभी तक इसकी जांच नहीं की है।

    किसी स्ट्रीम की गैर-अवरुद्ध प्रकृति को खंडों में डेटा को पढ़ना संभव हो सकता है चाहे ऑपरेटिंग सिस्टम "संक्षिप्त पढ़ता है" या नहीं। क्योंकि गैर-अवरुद्ध धारा कभी भी अवरुद्ध नहीं होती है। यदि स्ट्रीम में कोई डेटा नहीं है तो यह आपको बस इतना बताता है।

    गैर-अवरुद्ध धारा की अनुपस्थिति में आपको स्पॉन्गिंग थ्रेड का सहारा लेना होगा ताकि अवरोधन पढ़ने को अलग थ्रेड में किया जा सके और इस प्रकार आपके प्राथमिक धागे को अवरुद्ध नहीं किया जाएगा। यदि ऑपरेटिंग सिस्टम "छोटे पढ़ता" पसंद नहीं करता है तो आप लाइन सेपरेटर पर प्रतिक्रिया करने के लिए बाइट द्वारा स्ट्रीम बाइट भी पढ़ना चाहेंगे। यहां एक कामकाजी उदाहरण है: https://gist.github.com/ArtemGr/db40ae04b431a95f2b78

    +0

    उपयोगी स्पष्टीकरण के लिए धन्यवाद। मैं एमआईओ में देखूंगा, और यदि यह काम नहीं करता है, तो मैं अलग थ्रेड का उपयोग करूंगा। – jkiiski

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