2010-10-06 15 views
5

मैं एक प्रोग्राम लिख रहा हूं जो कई बाल प्रक्रियाओं को फोर्क करता है और मैं इन सभी बच्चों की प्रक्रियाओं को STDERR और STDOUT पर लाइनों को लिखने में सक्षम होना चाहता हूं आउटपुट गड़बड़ हो रहा है। मैं कुछ भी फैंसी नहीं कर रहा हूं, बस एक नई लाइन के साथ समाप्त होने वाली लाइनों को उत्सर्जित करना (कि, कम से कम मेरी समझ में लिनक्स के लिए एक परमाणु ऑपरेशन होगा)। Perlfaq से यह कहता है:फोर्क() और एसटीडीओयूटी/एसटीडीईआरआर बाल प्रक्रियाओं से कंसोल पर

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

यह कहता है कि मुझे बच्चे के लिए इन फाइलों को "बंद या फिर से खोलना" चाहिए। समापन सरल है, लेकिन इसका मतलब क्या है "फिर से खोलना"? मैं अपने बच्चे की प्रक्रिया के भीतर से कुछ इस तरह की कोशिश की है और यह काम नहीं करता (उत्पादन अभी भी ठीक से प्रदर्शित हो जाता है):

open(SAVED_STDERR, '>&', \*STDERR) or die "Could not create copy of STDERR: $!"; 
close(STDERR); 

# re-open STDERR 
open(STDERR, '>&SAVED_STDERR') or die "Could not re-open STDERR: $!"; 

तो, क्या मैं इस के साथ कुछ गलत कर रहा हूँ? पाइप उदाहरण क्या दिखता है? क्या एकाधिक प्रक्रियाओं से आउटपुट को कंसोल में समन्वयित करने का एक बेहतर तरीका है?

उत्तर

9

एक फ़ाइल हैंडल के लिए लिखते हैं एसटीडीओटीटी और एसटीडीआईएन के लिए परमाणु नहीं हैं। फीफो जैसे चीजों के लिए विशेष मामले हैं लेकिन यह आपकी वर्तमान स्थिति नहीं है।

जब यह कहता है कि STDOUT फिर से खोलें इसका अर्थ है "एक नया STDOUT उदाहरण बनाएं" यह नया उदाहरण माता-पिता से एक जैसा नहीं है। इस तरह आप अपने सिस्टम पर कई टर्मिनल खोल सकते हैं और सभी STDOUT एक ही स्थान पर नहीं जा सकते हैं।

पाइप समाधान बच्चे को पाइप (जैसे | खोल में) के माध्यम से माता-पिता से जोड़ देगा और आपको माता-पिता को पाइप से बाहर पढ़ने और आउटपुट को मल्टीप्लेक्स करने की आवश्यकता होगी। माता-पिता पाइप से पढ़ने के लिए जिम्मेदार होंगे और यह सुनिश्चित करेंगे कि यह पाइप से आउटपुट नहीं करेगा और एक ही समय में माता-पिता के STDOUT के लिए निर्धारित आउटपुट होगा। पाइप के एक उदाहरण और लेखन here है।

एक SNIPPIT:

use IO::Handle; 

pipe(PARENTREAD, PARENTWRITE); 
pipe(CHILDREAD, CHILDWRITE); 

PARENTWRITE->autoflush(1); 
CHILDWRITE->autoflush(1); 

if ($child = fork) { # Parent code 
    chomp($result = <PARENTREAD>); 
    print "Got a value of $result from child\n"; 
    waitpid($child,0); 
} else { 
    print PARENTWRITE "FROM CHILD\n"; 
    exit; 
} 

देखें कि कैसे बच्चे को stdout में लिखना नहीं है, बल्कि माता पिता, जो अपने stdout के साथ लेखन करता है के लिए एक संदेश भेजने के लिए पाइप का उपयोग करता है। एक नजर डालना सुनिश्चित करें क्योंकि मैंने अनियंत्रित फ़ाइल हैंडल बंद करने जैसी चीजों को छोड़ दिया है।

+0

मैं पाइप और आईओ चयन के लिए आईओ :: पाइप और AnyEvent के साथ जा रहा था, लेकिन ऐसा लगता है कि यह काम करता है। धन्यवाद – mpeters

1

हालांकि यह आपकी गरमागरिता में मदद नहीं करता है, लेकिन मुझे बाल प्रक्रिया को लॉन्च करने का एक तरीका खोजने में काफी समय लगा, जिसे मूल प्रक्रिया द्वारा लिखा जा सकता है और बच्चे की प्रक्रिया की सीढ़ी और stdout सीधे भेजा गया है स्क्रीन पर (यह चुनने जैसी कुछ फैंसी का उपयोग किए बिना दो अलग-अलग एफडी से पढ़ने की कोशिश करते समय आपके पास खराब ब्लॉकिंग समस्याएं हल हो सकती हैं)।

एक बार मैं यह पता लगा, समाधान तुच्छ

my $pid = open3(*CHLD_IN, ">&STDERR", ">&STDOUT", 'some child program'); 
# write to child 
print CHLD_IN "some message"; 
close(CHLD_IN); 
waitpid($pid, 0); 

सब कुछ से "कुछ बच्चे कार्यक्रम" stdout/stderr को उत्सर्जित हो जाएगा था, और आप बस CHLD_IN को लिख कर डेटा पंप और पर भरोसा कर सकते हैं कि यह बच्चे के बफर भरने पर ब्लॉक होगा। अभिभावक कार्यक्रम के कॉल करने वालों के लिए, यह सब सिर्फ stderr/stdout जैसा दिखता है।

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