2010-05-15 9 views
12

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

मेरा समाधान, जो काम करता है, ऐसा लगता है कि यह सही समाधान नहीं है। मुझे अभी तक विंडोज समाधान में दिलचस्पी नहीं है, लेकिन मुझे अंत में भी इसकी आवश्यकता होगी। मेरा यूनिक्स केवल यूनिक्स के लिए काम करता है, जो अभी के लिए ठीक है।

$ fork_bomb <parallel jobs> <number of forks> 

$ fork_bomb 8 500 

यह शायद कुछ मिनटों के भीतर प्रति-उपयोगकर्ता प्रक्रिया सीमा तक नहीं पहुंच जाएगा:

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

मैं प्रक्रिया प्रक्रिया को क्रॉल करता हूं, बच्चे की प्रक्रियाओं को ढूंढता हूं और SIGALRM हैंडलर में व्यक्तिगत रूप से लटका प्रक्रियाओं को बंद करता हूं, जिसे मरने की आवश्यकता होती है क्योंकि शेष वास्तविक कोड के बाद सफलता की कोई उम्मीद नहीं होती है। प्रक्रिया तालिका के माध्यम से kludgey क्रॉल मुझे एक प्रदर्शन के नजरिए से परेशान नहीं है, लेकिन मैं इसे नहीं कर रही कोई फ़र्क नहीं पड़ेगा:

use Parallel::ForkManager; 
use Proc::ProcessTable; 

my $pm = Parallel::ForkManager->new($ARGV[0]); 

my $alarm_sub = sub { 
     kill 9, 
      map { $_->{pid} } 
      grep { $_->{ppid} == $$ } 
      @{ Proc::ProcessTable->new->table }; 

     die "Alarm rang for $$!\n"; 
     }; 

foreach (0 .. $ARGV[1]) 
    { 
    print "."; 
    print "\n" unless $count++ % 50; 

    my $pid = $pm->start and next; 

    local $SIG{ALRM} = $alarm_sub; 

    eval { 
     alarm(2); 
     system "$^X -le '<STDIN>'"; # this will hang 
     alarm(0); 
     }; 

    $pm->finish; 
    } 

आप प्रक्रियाओं से बाहर चलाने के लिए चाहते हैं, kill बाहर ले।

मैंने सोचा था कि एक प्रक्रिया समूह की स्थापना तो काम करेगा मैं सब कुछ एक साथ मार सकता है, लेकिन यह है कि ब्लॉक:

my $alarm_sub = sub { 
     kill 9, -$$; # blocks here 
     die "Alarm rang for $$!\n"; 
     }; 

foreach (0 .. $ARGV[1]) 
    { 
    print "."; 
    print "\n" unless $count++ % 50; 

    my $pid = $pm->start and next; 
    setpgrp(0, 0); 

    local $SIG{ALRM} = $alarm_sub; 

    eval { 
     alarm(2); 
     system "$^X -le '<STDIN>'"; # this will hang 
     alarm(0); 
     }; 

    $pm->finish; 
    } 

POSIX के setsid साथ एक ही बात या तो काम नहीं किया, और मुझे लगता है कि वास्तव में लगता है चीजों को एक अलग तरीके से तोड़ दिया क्योंकि मैं वास्तव में इसे निंदा नहीं कर रहा हूं।

उत्सुकता से, Parallel::ForkManager की run_on_finish उसी क्लीन-अप कोड के लिए बहुत देर हो चुकी है: पोते को स्पष्ट रूप से उस समय बाल प्रक्रियाओं से अलग कर दिया गया है।

+0

वहाँ एक कारण है कि आप बच्चों में STDIN बंद नहीं करते है? – jrockway

+0

यह भी एक अच्छा विचार हो सकता है। मुझे इसके बारे में सोचना होगा, हालांकि मैं कुछ बुरे मामलों को संभालने पर विचार कर रहा था जैसे उन्हें ऐसा करने की उम्मीद है कि उन्हें ऐसा करने की ज़रूरत है। –

+0

नीचे लंबा जवाब देखें। लेकिन मूल रूप से, मुझे लगता है कि आपको समानांतर की तुलना में स्थिति पर अधिक नियंत्रण की आवश्यकता है :: फोर्कमैनेजर आपको देता है, जिसका अर्थ है कि आपको अपना खुद का रोल करना होगा। – jrockway

उत्तर

1

ब्रायन - यह थोड़ा कच्चे तेल और गैर मुहावरेदार है, लेकिन एक दृष्टिकोण मैंने देखा लिया है यह है: आप किसी भी समय कांटा, आप:

  1. बच्चे प्रक्रिया दें पहली बार एक "-id" डमी कार्यक्रम के लिए पैरामीटर, कुछ हद तक अद्वितीय (प्रति पीआईडी) मूल्य के साथ - एक अच्छा उम्मीदवार अप-टू-मिलीसेकंद टाइमस्टैम्प + पैरेंट के पीआईडी ​​हो सकता है।

  2. माता पिता रिकॉर्ड बच्चे पीआईडी ​​और वांछित समय समाप्ति के साथ एक (आदर्श, लगातार) रजिस्ट्री में एक -id मूल्य/समय मार डालते हैं।

फिर एक द्रष्टा प्रक्रिया (या तो परम दादा-दादी या एक ही यूआईडी के साथ एक अलग प्रक्रिया) बस चक्र रजिस्ट्री के माध्यम से समय-समय पर है, और जाँच जो मारे जाने के लिए की आवश्यकता होगी, प्रक्रियाओं (के अनुसार करने के लिए को मारने के समय) अभी भी आसपास लटक रहे हैं (पीआईडी ​​और "-id" पैरामीटर मान दोनों पीआईडी ​​के साथ रजिस्ट्री में और प्रक्रिया तालिका में कमांड लाइन से मिलान करके); और ऐसी प्रक्रिया में सिग्नल 9 भेजें (या अच्छा हो और सिग्नल 2 भेजने की कोशिश करके धीरे-धीरे पहले मारने का प्रयास करें)।

अद्वितीय "-id" पैरामीटर स्पष्ट रूप से कुछ निर्दोष प्रक्रिया को मारने से रोकने के लिए है जो कि संयोग से पहले प्रक्रिया की पीआईडी ​​का पुन: उपयोग करने के लिए हुआ था, जो शायद आपके द्वारा वर्णित पैमाने पर दिया जा सकता है।

रजिस्ट्री का विचार "पहले से अलग" ग्रैंड-बच्चों की समस्या से मदद करता है क्योंकि अब आप अपने लिए माता-पिता/बाल संबंध रखने के लिए सिस्टम पर निर्भर नहीं हैं।

यह एक प्रकार का क्रूर बल है, लेकिन चूंकि किसी ने अभी तक जवाब नहीं दिया है, इसलिए मुझे लगा कि मैं अपने 3 सेंट के विचार को अपने तरीके से मानूंगा।

+0

यह एक बैंड-सहायता है, समाधान नहीं। मैं क्रूर बल की चीजों के बारे में जानता हूं जो मैं कर सकता हूं, लेकिन मैं वास्तव में प्रोग्राम के भीतर अजीब युग्मन के सभी प्रकार के बिना वास्तविक समस्या को पाने की कोशिश कर रहा हूं। –

8

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

तो, मैंने इन दो तरीकों को लागू करने का प्रयास किया। सबसे पहले माता-पिता को एक नए यूनिक्स सत्र में बच्चे को जन्म देने के लिए बनाया गया था, कुछ सेकंड के लिए टाइमर सेट करें, और टाइमर बंद होने पर पूरे बच्चे सत्र को मार दें। इसने माता-पिता को बच्चे और पोते दोनों के लिए ज़िम्मेदार बनाया। यह भी सही काम नहीं किया।

अगली रणनीति माता-पिता को बच्चे को जन्म देने के लिए थी, और फिर बच्चे को पोते के प्रबंधन के लिए जिम्मेदार बनाते हैं। यह प्रत्येक पोते के लिए एक टाइमर सेट करेगा, और अगर प्रक्रिया समाप्ति समय से बाहर नहीं है तो इसे मार दें। यह बहुत अच्छा काम करता है, तो यहां कोड है।

हम बच्चों और टाइमर का प्रबंधन करने के लिए ईवी का उपयोग करेंगे, और एपीआई के लिए AnyEvent। (आप इवेंट या पीओई जैसे किसी भी एनीवेंट इवेंट लूप को आजमा सकते हैं। लेकिन मुझे पता है कि ईवी सही ढंग से उस स्थिति को संभालती है जहां एक बच्चे से बाहर निकलता है इससे पहले कि आप इसे मॉनिटर करने के लिए लूप बताएं, जो कष्टप्रद दौड़ शर्तों को समाप्त करता है जो अन्य लूप कमजोर हैं ।

# active child watchers 
my %children; 

फिर हम एक समारोह बच्चों शुरू करने के लिए लिखने के लिए की जरूरत है:)

#!/usr/bin/env perl 

use strict; 
use warnings; 
use feature ':5.10'; 

use AnyEvent; 
use EV; # you need EV for the best child-handling abilities 

हम बच्चे पर नजर रखने वालों का ट्रैक रखने की जरूरत है। चीजें माता-पिता के स्पंज बच्चों को बुलाते हैं, और बच्चों को स्पॉन को नौकरियां कहा जाता है।

sub start_child([email protected]) { 
    my ($on_success, $on_error, @jobs) = @_; 

तर्क coderef कार्यों की सूची एक कॉलबैक जब बच्चे पूरा करता सफलतापूर्वक (जिसका अर्थ है अपनी नौकरियों को भी एक सफलता थे) के नाम से जाना, एक कॉलबैक जब बच्चे सफलतापूर्वक पूर्ण नहीं हुआ है, और फिर रहे हैं चलाने के लिए।

इस फ़ंक्शन में, हमें कांटा जाना होगा। माता-पिता के लिए, हम सेटअप एक बच्चे द्रष्टा बच्चे की निगरानी के लिए:

if(my $pid = fork){ # parent 
     # monitor the child process, inform our callback of error or success 
     say "$$: Starting child process $pid"; 
     $children{$pid} = AnyEvent->child(pid => $pid, cb => sub { 
      my ($pid, $status) = @_; 
      delete $children{$pid}; 

      say "$$: Child $pid exited with status $status"; 
      if($status == 0){ 
       $on_success->($pid); 
      } 
      else { 
       $on_error->($pid); 
      } 
     }); 
    } 

बच्चे में, हम वास्तव में नौकरियों चलाते हैं। इसमें सेटअप का थोड़ा सा हिस्सा शामिल है, हालांकि।

सबसे पहले, हम माता-पिता के बच्चे निरीक्षक को भूल जाते हैं, क्योंकि यह बच्चे को अपने भाई बहनों से बाहर निकलने के लिए समझ में नहीं आता है। (कांटा मज़ा है, क्योंकि आप माता-पिता के राज्य के सभी वारिस, तब भी जब कि बिल्कुल कोई मतलब नहीं है है।)

else { # child 
     # kill the inherited child watchers 
     %children =(); 
     my %timers; 

हम यह भी पता करने के लिए जब सभी नौकरियों किया जाता है की जरूरत है, और चाहे या नहीं वे सभी एक सफलता थीं। हम गिनती सशर्त चर का उपयोग पर करते हैं जब सबकुछ निकलता है। हम स्टार्टअप पर वृद्धि करते हैं, और बाहर निकलने पर कमी, और जब गिनती 0 होती है, तो हम सबकुछ जानते हैं।

मैं त्रुटि स्थिति को इंगित करने के लिए चारों ओर एक बुलियन भी रखता हूं। एक प्रक्रिया एक गैर शून्य की स्थिति के साथ बाहर निकल जाता है, त्रुटि अन्यथा 1. करने के लिए चला जाता है, यह रहता है 0. आप अधिक राज्य रखने के लिए चाहते हो सकता है की तुलना में इस :)

 # then start the kids 
     my $done = AnyEvent->condvar; 
     my $error = 0; 

     $done->begin; 

(हम भी 1 पर गिनती शुरू ताकि अगर 0 नौकरियां हों, तो हमारी प्रक्रिया अभी भी बाहर निकलती है।)

अब हमें प्रत्येक नौकरी के लिए कांटा और नौकरी चलाने की आवश्यकता है। अभिभावक में, हम कुछ चीजें करते हैं। हम condvar में वृद्धि। हम बच्चे को बहुत धीमा होने पर मारने के लिए टाइमर सेट करते हैं। और हम एक बच्चे के दर्शक को स्थापित करते हैं, इसलिए हम नौकरी की निकास स्थिति के बारे में सूचित हो सकते हैं। क्योंकि यह इसके साथ राज्य वहन करती है

for my $job (@jobs) { 
      if(my $pid = fork){ 
       say "[c] $$: starting job $job in $pid"; 
       $done->begin; 

       # this is the timer that will kill the slow children 
       $timers{$pid} = AnyEvent->timer(after => 3, interval => 0, cb => sub { 
        delete $timers{$pid}; 

        say "[c] $$: Killing $pid: too slow"; 
        kill 9, $pid; 
       }); 

       # this monitors the children and cancels the timer if 
       # it exits soon enough 
       $children{$pid} = AnyEvent->child(pid => $pid, cb => sub { 
        my ($pid, $status) = @_; 
        delete $timers{$pid}; 
        delete $children{$pid}; 

        say "[c] [j] $$: job $pid exited with status $status"; 
        $error ||= ($status != 0); 
        $done->end; 
       }); 
      } 

टाइमर का उपयोग करना, एक छोटा सा अलार्म की तुलना में आसान है। प्रत्येक टाइमर जानता है कि किस प्रक्रिया को मारना है, और यह प्रक्रिया आसान होने पर टाइमर को रद्द करने के लिए आसान है - हम केवल हैश से इसे हटा दें।

यह माता-पिता (बच्चे का) है। (बच्चे की, या काम) बच्चे वास्तव में सरल है:

  else { 
       # run kid 
       $job->(); 
       exit 0; # just in case 
      } 

तुम भी पास stdin यहाँ, अगर आप चाहते थे सकता है।

अब, सभी प्रक्रियाओं के बाद उत्पन्न होने के बाद, हम उन्हें कन्वेयर पर प्रतीक्षा करके सभी बाहर निकलने का इंतजार करते हैं।घटना पाश बच्चों और टाइमर monior जाएगा, और हमारे लिए सही काम: की तरह,

 } # this is the end of the for @jobs loop 
     $done->end; 

     # block until all children have exited 
     $done->recv; 

तब, जब सभी बच्चों को बाहर आ गए हैं, हम जो कुछ भी सफाई काम हम चाहते हैं कर सकते हैं:

 if($error){ 
      say "[c] $$: One of your children died."; 
      exit 1; 
     } 
     else { 
      say "[c] $$: All jobs completed successfully."; 
      exit 0; 
     } 
    } # end of "else { # child" 
} # end of start_child 

ठीक है, तो यह बच्चा और पोती/नौकरी है। अब हमें माता-पिता को लिखना होगा, जो कि बहुत आसान है।

बच्चे की तरह, हम बच्चों के लिए प्रतीक्षा करने के लिए एक गिनती कोंवर का उपयोग करने जा रहे हैं।

# main program 
my $all_done = AnyEvent->condvar; 

हमें कुछ नौकरियों की आवश्यकता है। यहाँ एक हमेशा सफल नहीं है, और एक है कि सफल हो जाएगा अगर आप वापसी प्रेस है, लेकिन अगर आप बस इसे टाइमर द्वारा मारा जा जाने असफल हो जायेगी:

my $good_grandchild = sub { 
    exit 0; 
}; 

my $bad_grandchild = sub { 
    my $line = <STDIN>; 
    exit 0; 
}; 

तो फिर हम सिर्फ बच्चे शुरू करने के लिए की जरूरत है नौकरियों। यदि आपको start_child के शीर्ष पर वापस याद है, तो इसमें दो कॉलबैक, त्रुटि कॉलबैक और सफलता कॉलबैक शामिल है। हम उन्हें स्थापित करेंगे; त्रुटि कॉलबैक "ठीक नहीं" प्रिंट करेगा और कन्वेयर को कम करेगा, और सफलता कॉलबैक "ठीक" प्रिंट करेगा और वही करेगा। बहुत आसान।

my $ok = sub { $all_done->end; say "$$: $_[0] ok" }; 
my $nok = sub { $all_done->end; say "$$: $_[0] not ok" }; 

तो हम और भी अधिक पोते नौकरियों के साथ बच्चों का एक समूह शुरू कर सकते हैं:

say "starting..."; 

$all_done->begin for 1..4; 
start_child $ok, $nok, ($good_grandchild, $good_grandchild, $good_grandchild); 
start_child $ok, $nok, ($good_grandchild, $good_grandchild, $bad_grandchild); 
start_child $ok, $nok, ($bad_grandchild, $bad_grandchild, $bad_grandchild); 
start_child $ok, $nok, ($good_grandchild, $good_grandchild, $good_grandchild, $good_grandchild); 

उन लोगों में से दो का समय-समाप्त होगा, और दो सफल होगा। यदि आप चल रहे हैं, तो दर्ज करें, फिर भी, वे सभी सफल हो सकते हैं।

वैसे भी, एक बार उन शुरू कर दिया है, हम बस समाप्त करने के लिए उनके लिए प्रतीक्षा करने की आवश्यकता:

$all_done->recv; 

say "...done"; 

exit 0; 

और वह कार्यक्रम है।

एक बात है कि हम नहीं कर रहे हैं कि समानांतर :: ForkManager करता "दर सीमित" हमारे कांटे ताकि केवल n बच्चों को एक समय में चल रहे हैं। यह मैन्युअल रूप से लागू करने के लिए बहुत आसान है, हालांकि यह है:

use Coro; 
use AnyEvent::Subprocess; # better abstraction than manually 
          # forking and making watchers 
use Coro::Semaphore; 

my $job = AnyEvent::Subprocess->new(
    on_completion => sub {}, # replace later 
    code   => sub { the child process }; 
) 

my $rate_limit = Coro::Semaphore->new(3); # 3 procs at a time 

my @coros = map { async { 
    my $guard = $rate_limit->guard; 
    $job->clone(on_completion => Coro::rouse_cb)->run($_); 
    Coro::rouse_wait; 
}} ({ args => 'for first job' }, { args => 'for second job' }, ...); 

# this waits for all jobs to complete 
my @results = map { $_->join } @coros; 

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

वैसे भी, उम्मीद है कि इससे मदद मिलती है।

+0

इसके अलावा, आप कोड को एक स्क्रिप्ट में कट-पेस्ट कर सकते हैं और इसे चला सकते हैं। बस पाठ हटाएं। पुस्तक की तरह उत्तर के लिए – jrockway

+1

+1! – Kyle

0

मुझे मॉड्यूल I've been working on में एक ही समस्या को हल करना है।मैं या तो पूरी तरह से मेरी समाधान (रों) के सभी के साथ संतुष्ट नहीं हूँ, लेकिन क्या आम तौर पर यूनिक्स पर काम करता है

  1. परिवर्तन एक बच्चे की प्रक्रिया समूह
  2. अंडे पोते के रूप में आवश्यक
  3. परिवर्तन बच्चे की प्रक्रिया समूह के लिए है फिर से (जैसे कि, वापस अपने मूल मूल्य के लिए)
  4. संकेत पोते की प्रक्रिया समूह पोते

कुछ की तरह मारने के लिए:

use Time::HiRes qw(sleep); 

sub be_sleepy { sleep 2 ** (5 * rand()) } 
$SIGINT = 2; 

for (0 .. $ARGV[1]) { 
    print "."; 
    print "\n" unless ++$count % 50; 
    if (fork() == 0) { 
     # a child process 
     # $ORIGINAL_PGRP and $NEW_PGRP should be global or package or object level vars 
     $ORIGINAL_PGRP = getpgrp(0); 
     setpgrp(0, $$); 
     $NEW_PGRP = getpgrp(0); 

     local $SIG{ALRM} = sub { 
      kill_grandchildren(); 
      die "$$ timed out\n"; 
     }; 

     eval { 
      alarm 2; 
      while (rand() < 0.5) { 
       if (fork() == 0) { 
        be_sleepy(); 
       } 
      } 
      be_sleepy(); 
      alarm 0; 
      kill_grandchildren(); 
     }; 

     exit 0; 
    } 
} 

sub kill_grandchildren { 
    setpgrp(0, $ORIGINAL_PGRP); 
    kill -$SIGINT, $NEW_PGRP; # or kill $SIGINT, -$NEW_PGRP 
} 

यह पूरी तरह से सबूत मूर्ख नहीं है। पोते अपने प्रक्रिया समूहों या जाल संकेतों को बदल सकते हैं। इस के

कोई भी निश्चित रूप से विंडोज पर काम करेंगे,, लेकिन सिर्फ इतना कहना है कि TASKKILL /F /T अपने दोस्त है चलो।


अद्यतन: यह समाधान (मेरे लिए, वैसे भी) मामले को संभाल नहीं है जब बच्चे प्रक्रिया का आह्वान system "perl -le '<STDIN>'"। मेरे लिए, यह तुरंत प्रक्रिया को निलंबित कर देता है, और एसआईजीएएलआरएम को फायरिंग और एसआईजीएएलआरएम हैंडलर से चलने से रोकता है। STDIN एकमात्र कामकाज बंद कर रहा है?

+0

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