लिनक्स में "लीक" पाइप


12

मान लें कि आपके पास निम्नलिखित की तरह एक पाइपलाइन है:

$ a | b

यदि bस्टड का प्रसंस्करण बंद हो जाता है, तो थोड़ी देर बाद पाइप भर जाता है, और लिखता है, aअपने स्टडआउट से, ब्लॉक हो जाएगा (जब तक कि या तो bफिर से प्रसंस्करण शुरू नहीं होता है या यह मर जाता है)।

अगर मैं इससे बचना चाहता था, तो मुझे एक बड़ा पाइप (या, अधिक बस, buffer(1)) का उपयोग करने के लिए लुभाया जा सकता है :

$ a | buffer | b

यह बस मुझे और अधिक समय खरीदेगा, लेकिन अंत में aअंत में बंद हो जाएगा।

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

यह योग करने के लिए मैं एक बंधी हुई, टपका हुआ बफर की तरह कुछ करना पसंद करूंगा:

$ a | leakybuffer | b

मैं शायद इसे किसी भी भाषा में काफी आसानी से लागू कर सकता हूं, मैं बस सोच रहा था कि क्या कोई "उपयोग करने के लिए तैयार" (या एक बैश वन-लाइनर की तरह) है जो मुझे याद आ रही है।

नोट: उदाहरणों में मैं नियमित पाइप का उपयोग कर रहा हूं, लेकिन नाम पाइप पर समान रूप से प्रश्न लागू होता है


जब मैंने नीचे दिए गए जवाब से सम्मानित किया, तो मैंने भी लीचीबफ़र कमांड को लागू करने का फैसला किया क्योंकि नीचे दिए गए सरल समाधान की कुछ सीमाएँ थीं: https://github.com/CAFxX/leakybuffer


क्या नामित पाइप वास्तव में भरते हैं? मैंने सोचा होगा कि नामित पाइप इसका समाधान हैं , लेकिन मैं निश्चित रूप से नहीं कह सकता।
वाइल्डकार्ड

3
नामित पाइपों में अनाम पाइपों के समान क्षमता है (AFAIK
CAFxX

जवाबों:


14

सबसे आसान तरीका कुछ प्रोग्राम के माध्यम से पाइप करना होगा जो नॉनब्लॉकिंग आउटपुट सेट करता है। यहाँ सरल पर्ल ऑन्लाइनर है (जिसे आप लीककफ़र के रूप में बचा सकते हैं ) जो ऐसा करता है:

तो आपका a | bबन जाता है:

a | perl -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { print }' | b

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

आप उदाहरण के लिए परीक्षण कर सकते हैं:

seq 1 500000 | perl -w -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { print }' | \
    while read a; do echo $a; done > output

आपको outputखोई हुई लाइनों के साथ फाइल मिलेगी (सटीक आउटपुट आपके शेल आदि की गति पर निर्भर करता है) इस तरह:

12768
12769
12770
12771
12772
12773
127775610
75611
75612
75613

आप देखते हैं कि शेल के बाद लाइनें कहां खो गईं 12773, लेकिन यह भी एक विसंगति है - पर्ल के पास पर्याप्त बफर नहीं था, 12774\nलेकिन इसके लिए 1277यह सिर्फ इतना लिखा था - और इसलिए अगला नंबर 75610लाइन की शुरुआत में शुरू नहीं होता है, जिससे यह थोड़ा कम हो जाता है बदसूरत।

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

अद्यतन (बाइनरी फ़ाइलों के लिए): यदि आप नईलाइन समाप्त लाइनों (जैसे लॉग फाइलें या समान) को संसाधित नहीं कर रहे हैं, तो आपको कमांड को थोड़ा बदलना होगा, या पर्ल बड़ी मात्रा में मेमोरी का उपभोग करेगा (आपके इनपुट में कितनी बार नईलाइन वर्ण दिखाई देते हैं) के आधार पर:

perl -w -MFcntl -e 'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (read STDIN, $_, 4096) { print }' 

यह बाइनरी फ़ाइलों के लिए भी सही ढंग से काम करेगा (अतिरिक्त मेमोरी का उपभोग किए बिना)।

Update2 - अच्छे टेक्स्ट फ़ाइल आउटपुट: आउटपुट बफ़र्स से बचना ( syswriteइसके बजाय print):

seq 1 500000 | perl -w -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { syswrite STDOUT,$_ }' | \
    while read a; do echo $a; done > output

मेरे लिए "मर्ज लाइनों" के साथ समस्याओं को ठीक करने के लिए लगता है:

12766
12767
12768
16384
16385
16386

(ध्यान दें: कोई यह सत्यापित कर सकता है कि किन लाइनों के साथ आउटपुट कट गया था: perl -ne '$c++; next if $c==$_; print "$c $_"; $c=$_' outputoneliner)


मुझे ऑन्लाइनर से प्यार है: मैं कोई पर्ल विशेषज्ञ नहीं हूं, अगर कोई भी इसके ऊपर के सुधारों का सुझाव दे सकता है, तो यह भयानक होगा
CAFxX

1
यह कुछ हद तक काम करने लगता है । लेकिन जैसा कि मैंने अपने आदेश को देखा है perl -w -MFcntl -e 'fcntl STDOUT,F_SETFL,O_WRONLY|O_NONBLOCK; while (<STDIN>) { print }' | aplay -t raw -f dat --buffer-size=16000, जो ओएलएम प्रबंधक द्वारा मारे जाने तक पर्ल को लगातार अधिक मेमोरी आवंटित करना प्रतीत होता है।
फॉक्स में

@Allacoloo यह इंगित करने के लिए धन्यवाद कि, मेरा मामला लॉग फ़ाइलों को स्ट्रीम कर रहा था ... बाइनरी फ़ाइलों का समर्थन करने के लिए आवश्यक थोड़े बदलाव के लिए अद्यतन उत्तर देखें।
मतिजा नलिस

जीएनयू ddका भी देखें dd oflag=nonblock status=none
स्टीफन चेज़लस

1
क्षमा करें, मेरा बुरा फिर से, वास्तव में PIPE_BUF बाइट्स से कम (4096 लिनक्स पर, POSIX द्वारा कम से कम 512 होने की आवश्यकता है) परमाणु के होने की गारंटी देता है, इसलिए $| = 1और आपका syswrite()दृष्टिकोण छोटे लेखन को वास्तव में रोकता है जब तक कि लाइनें यथोचित रूप से छोटी न हों।
स्टीफन चेज़लस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.