मैं एक प्रक्रिया को कैसे मार सकता हूं और सुनिश्चित करें कि पीआईडी ​​का पुन: उपयोग नहीं किया गया है


40

उदाहरण के लिए मान लीजिए, आपके पास एक शेल स्क्रिप्ट है:

longrunningthing &
p=$!
echo Killing longrunningthing on PID $p in 24 hours
sleep 86400
echo Time up!
kill $p

चाल करना चाहिए, है ना? सिवाय इसके कि प्रक्रिया को जल्दी समाप्त कर दिया गया हो और इसके पीआईडी ​​को पुनर्नवीनीकरण किया गया हो, जिसका अर्थ है कि कुछ निर्दोष नौकरी को इसके संकेत कतार में एक बम मिल सकता है। व्यवहार में यह संभवतः मायने रखता है, लेकिन इसकी चिंता मुझे फिर भी है। अपने आप से मृत को छोड़ने के लिए लंबे समय तक चलने वाली हैकिंग, या एफएस पर अपने पीआईडी ​​को रखना / हटाना, लेकिन मैं यहां सामान्य स्थिति के बारे में सोच रहा हूं।


3
आप यह सुनिश्चित करते हैं कि यदि आपकी लक्षित प्रक्रिया मर जाती है, तो यह प्रक्रिया में उसके हत्यारे को मार देता है।
मिकसेर्व

2
killallनाम पर कौन से मैच का उपयोग करें , इसलिए कम से कम आप केवल उसी नाम के साथ एक प्रक्रिया को मार रहे हैं longrunningthing। मान लें कि आपके पास केवल एक समय में इनमें से एक होगा।
लॉरेंस सी

8
आप मूल प्रक्रिया के प्रारंभ समय को बचा सकते हैं और, मारने से पहले, जाँच लें कि प्रक्रिया का प्रारंभ समय उस pid से मेल खाता है जिसे आपने बचाया था। जोड़ी पीआईडी, शुरू समय है लिनक्स में प्रक्रियाओं के लिए एक अद्वितीय पहचानकर्ता।
बाकुरू

1
मैं पूछ सकता हूं कि आपको इसकी आवश्यकता क्यों है ? वह कौन सी मूलभूत चीज है जिसे आप प्राप्त करने की कोशिश कर रहे हैं? (कुछ ऐसा जो लगातार चलता है लेकिन हर 24 घंटों में रीसेट हो जाता है?)
ओलिवियर दुलैक

2
@mikeserv एक प्रक्रिया यह गारंटी नहीं दे सकती है कि कुछ अपनी मृत्यु की स्थिति में होगा।
कास्परड

जवाबों:


29

timeoutयदि आपके पास इसका उपयोग करने के लिए सबसे अच्छा है तो कमांड का उपयोग करें :

timeout 86400 cmd

वर्तमान (8.23) GNU कार्यान्वयन alarm()बच्चे की प्रक्रिया की प्रतीक्षा करते समय कम से कम उपयोग या समकक्ष काम करता है । ऐसा लगता है कि लौटने और बाहर निकलने (प्रभावी रूप से उस अलार्म को रद्द करने ) के SIGALRMबीच वितरित होने के खिलाफ सुरक्षा नहीं है । उस छोटी खिड़की के दौरान, stderr पर संदेश भी लिख सकते हैं (उदाहरण के लिए यदि बच्चे ने एक कोर डंप किया है) जो उस दौड़ खिड़की को और अधिक बड़ा कर देगा (अनिश्चित काल के लिए यदि stderr उदाहरण के लिए एक पूर्ण पाइप है)।waitpid()timeouttimeout

मैं व्यक्तिगत रूप से उस सीमा के साथ रह सकता हूं (जो शायद भविष्य के संस्करण में तय किया जाएगा)। timeoutसही निकास स्थिति की रिपोर्ट करने के लिए अतिरिक्त देखभाल भी करेगा, अन्य कोने के मामलों को संभालना (जैसे कि SIGALRM अवरुद्ध / स्टार्टअप पर नजरअंदाज करना, अन्य संकेतों को संभालना ...) बेहतर है कि आप शायद हाथ से करने का प्रबंधन करें।

एक सन्निकटन के रूप में, आप इसे इस perlतरह लिख सकते हैं:

perl -MPOSIX -e '
  $p = fork();
  die "fork: $!\n" unless defined($p);
  if ($p) {
    $SIG{ALRM} = sub {
      kill "TERM", $p;
      exit 124;
    };
    alarm(86400);
    wait;
    exit (WIFSIGNALED($?) ? WTERMSIG($?)+128 : WEXITSTATUS($?))
  } else {exec @ARGV}' cmd

Http://devel.ringlet.net/sysutils/timelimit/timelimit पर एक आदेश है ( कुछ महीनों से GNU पूर्वसूचक करता है )।timeout

 timelimit -t 86400 cmd

यह एक समान alarm()तंत्र का उपयोग करता है, लेकिन SIGCHLDमरने वाले बच्चे का पता लगाने के लिए एक हैंडलर स्थापित करता है । यह चलने से पहले अलार्म को भी रद्द कर देता है waitpid()( SIGALRMयदि यह लंबित होने पर डिलीवरी रद्द नहीं करता है , लेकिन जिस तरह से यह लिखा गया है, मैं इसे समस्या नहीं देख सकता) और कॉल करने से पहले मारता है waitpid()(इसलिए पुन: उपयोग नहीं किया गया पीआईडी ​​को नहीं मार सकता )।

netpipes में एक timelimitकमांड भी है । वह दशकों से अन्य सभी को पहले से ही बताता है, फिर भी एक और दृष्टिकोण लेता है, लेकिन रोकी गई कमांड के लिए ठीक से काम नहीं करता है और 1समय समाप्त होने पर बाहर निकलने की स्थिति देता है ।

आपके प्रश्न के अधिक प्रत्यक्ष उत्तर के रूप में, आप कुछ ऐसा कर सकते हैं:

if [ "$(ps -o ppid= -p "$p")" -eq "$$" ]; then
  kill "$p"
fi

यही है, जांच लें कि प्रक्रिया अभी भी हमारा बच्चा है। फिर, एक छोटी दौड़ खिड़की है ( psउस प्रक्रिया की स्थिति को पुनः प्राप्त करने और killइसे मारने के बीच में ) जिसके दौरान यह प्रक्रिया मर सकती है और किसी अन्य प्रक्रिया द्वारा इसका उपयोग पुन: उपयोग किया जा सकता है।

कुछ गोले के साथ ( zsh, bash, mksh), आप पीआईडी की बजाय काम चश्मा पारित कर सकते हैं।

cmd &
sleep 86400
kill %
wait "$!" # to retrieve the exit status

यह केवल तभी काम करता है जब आप केवल एक बैकग्राउंड जॉब करते हैं (अन्यथा सही जॉबस्पेक प्राप्त करना हमेशा मज़बूती से संभव नहीं होता है)।

यदि यह समस्या है, तो एक नया शेल उदाहरण शुरू करें:

bash -c '"$@" & sleep 86400; kill %; wait "$!"' sh cmd

वह काम करता है क्योंकि शेल बच्चे को मरने पर नौकरी की मेज से हटा देता है। यहां, शेल कॉल के बाद से कोई रेस विंडो नहीं होनी चाहिए kill(), या तो SIGCHLD सिग्नल को संभाला नहीं गया है और पिड का पुन: उपयोग नहीं किया जा सकता है (क्योंकि इसका इंतजार नहीं किया गया है), या इसे हैंडल किया गया है और आगे नौकरी को प्रक्रिया तालिका से हटा दिया गया है (और killएक त्रुटि की रिपोर्ट करेगा)। bash's killकम से कम ब्लॉक SIGCHLD पर इससे पहले कि यह विस्तार करने के लिए अपना काम तालिका तक पहुँचता है %के बाद और यह अनब्लॉक करता kill()

एक अन्य विकल्प यह है कि मरने के sleepबाद भी इस प्रक्रिया को इधर-उधर लटकने से बचाया cmdजा सकता है, bashया इसके बजाय ksh93एक पाइप का उपयोग read -tकरना है sleep:

{
  {
    cmd 4>&1 >&3 3>&- &
    printf '%d\n.' "$!"
  } | {
    read p
    read -t 86400 || kill "$p"
  }
} 3>&1

वह अभी भी दौड़ की स्थिति है, और आप कमांड की निकास स्थिति खो देते हैं। यह भी मानता cmdहै कि अपने fd 4 को बंद नहीं करता है।

आप दौड़-मुक्त समाधान लागू करने की कोशिश कर सकते हैं perlजैसे:

perl -MPOSIX -e '
   $p = fork();
   die "fork: $!\n" unless defined($p);
   if ($p) {
     $SIG{CHLD} = sub {
       $ss = POSIX::SigSet->new(SIGALRM); $oss = POSIX::SigSet->new;
       sigprocmask(SIG_BLOCK, $ss, $oss);
       waitpid($p,WNOHANG);
       exit (WIFSIGNALED($?) ? WTERMSIG($?)+128 : WEXITSTATUS($?))
           unless $? == -1;
       sigprocmask(SIG_UNBLOCK, $oss);
     };
     $SIG{ALRM} = sub {
       kill "TERM", $p;
       exit 124;
     };
     alarm(86400);
     pause while 1;
   } else {exec @ARGV}' cmd args...

(हालांकि अन्य प्रकार के कोने के मामलों को संभालने के लिए इसे सुधारने की आवश्यकता होगी)।

एक और दौड़-मुक्त विधि प्रक्रिया समूहों का उपयोग कर सकती है:

set -m
((sleep 86400; kill 0) & exec cmd)

हालाँकि, ध्यान दें कि यदि किसी टर्मिनल डिवाइस में I / O शामिल है, तो प्रक्रिया समूहों का उपयोग करने के दुष्प्रभाव हो सकते हैं। हालांकि इसका अतिरिक्त लाभ है कि सभी अन्य अतिरिक्त प्रक्रियाओं को मार डाला गया है cmd


4
सबसे अच्छी विधि का उल्लेख पहले क्यों नहीं किया गया?
डेल्टाब

2
@ डैल्टैब: timeoutपोर्टेबल नहीं है, पहले एक पोर्टेबल समाधान का जवाब दिया गया था।
कोउंगलम

1
@ डैल्टैब: यह इस बात की अंतर्दृष्टि देता है कि चीजें कैसे काम करती हैं और विशेष रूप से "सामान्य ज्ञान" दृष्टिकोण कैसे विफल हो सकता है (स्टीफन पहले एक मछली को सिखाने के लिए पसंद करते हैं, जो मुझे पसंद है)। पूरे उत्तर को पढ़ने की उम्मीद है
ओलिवियर दुलैक

@ स्टेफ़ेन: "सही जॉबस्पेक्ट प्राप्त करना हमेशा मज़बूती से संभव नहीं होता है" के लिए: क्या आप पहले इसका आउटपुट नहीं गिन सकते हैं jobsऔर फिर जानते हैं कि (जैसा कि यह आपका अपना शेल है, जिसमें आप पर नियंत्रण होता है कि आगे क्या होता है) नौकरी N + 1 होगी? [तब आप एन को बचा सकते हैं, और बाद में% N + 1 को मार सकते हैं]
ओलिवियर दुलक

1
@OlivierDulac, यह मान लेगा कि जब तक आप कोई नया काम शुरू नहीं करेंगे तब तक कोई भी पिछली नौकरी समाप्त नहीं होगी।
स्टीफन चेज़लस

28

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

प्रक्रियाओं को मारने का एक वैकल्पिक सुरक्षित तरीका उन्हें एक छद्म टर्मिनल के लिए एक नियंत्रित ट्टी सेट के साथ शुरू करना है, जिसके लिए आप मास्टर साइड के मालिक हैं। फिर आप टर्मिनल के माध्यम से संकेत भेज सकते हैं, जैसे कि पीटीआई के लिए SIGTERMया उससे SIGQUITअधिक वर्ण लिखना ।

फिर भी स्क्रिप्टिंग के साथ अधिक सुविधाजनक तरीका नामांकित screenसत्र का उपयोग करना है और इसे समाप्त करने के लिए स्क्रीन सत्र को कमांड भेजना है। यह प्रक्रिया स्क्रीन सत्र के अनुसार नामित एक पाइप या यूनिक्स सॉकेट पर होती है, जो यदि आप एक सुरक्षित अद्वितीय नाम चुनते हैं तो स्वचालित रूप से पुन: उपयोग नहीं किया जाएगा।


4
मैं यह नहीं देख सकता कि गोले में क्यों नहीं किया जा सकता है। मैंने कई समाधान दिए हैं।
स्टीफन चेजलस

3
क्या आप कृपया एक स्पष्टीकरण और दौड़ की खिड़कियों और अन्य कमियों की मात्रात्मक चर्चा कर सकते हैं? इसके बिना, "अब तक दिए गए सभी जवाब बगिया के आंकड़े हैं" बिना किसी लाभ के सिर्फ अनावश्यक टकराव है।
पेट्रफ

3
@peterph: सामान्य तौर पर, पीआईडी ​​का कोई भी उपयोग एक TOCTOU दौड़ है - कोई फर्क नहीं पड़ता कि आप कैसे जांचते हैं कि क्या यह अभी भी उसी प्रक्रिया को संदर्भित करता है जिससे आप इसे संदर्भित करने की उम्मीद करते हैं, यह उस प्रक्रिया को संदर्भित करने और कुछ नए को संदर्भित करने के लिए बंद हो सकता है अंतराल में प्रक्रिया इससे पहले कि आप इसका उपयोग करें (संकेत भेजना)। इसे रोकने का एकमात्र तरीका पिड के मुक्त / पुन: उपयोग को अवरुद्ध करने में सक्षम होना है, और एकमात्र प्रक्रिया जो ऐसा कर सकती है वह है प्रत्यक्ष अभिभावक।
आर ..

2
@ स्टीफनचेलजैस: आप शेल को पृष्ठभूमि की प्रक्रिया से बचने के लिए इंतजार करने से कैसे रोक सकते हैं? यदि आप ऐसा कर सकते हैं, तो ओपी की जरूरत के मामले में समस्या आसानी से हल हो सकती है।
आर ..

5
@peterph: "रेस विंडो छोटा है" एक समाधान नहीं है। और दौड़ की दुर्लभता अनुक्रमिक पिड असाइनमेंट पर निर्भर है। कीड़े जो वर्ष में एक बार कुछ बहुत बुरा होने का कारण बनते हैं, वे कीड़े से बहुत खराब होते हैं जो हर समय होते हैं क्योंकि वे निदान और ठीक करने के लिए लगभग असंभव हैं।
आर ..

10
  1. प्रक्रिया शुरू करते समय अपना आरंभ समय बचाएं:

    longrunningthing &
    p=$!
    stime=$(TZ=UTC0 ps -p "$p" -o lstart=)
    
    echo "Killing longrunningthing on PID $p in 24 hours"
    sleep 86400
    echo Time up!
    
  2. प्रक्रिया को मारने से पहले इसे रोकने की कोशिश करें (यह सही मायने में आवश्यक नहीं है, लेकिन यह दौड़ की स्थिति से बचने का एक तरीका है: यदि आप प्रक्रिया को रोकते हैं, तो यह फिर से उपयोग नहीं किया जा सकता है)

    kill -s STOP "$p"
    
  3. जाँच लें कि उस PID के साथ प्रक्रिया का प्रारंभ समय समान है और यदि हाँ, तो उसे मार दें, अन्यथा प्रक्रिया जारी रखें:

    cur=$(TZ=UTC0 ps -p "$p" -o lstart=)
    
    if [ "$cur" = "$stime" ]
    then
        # Okay, we can kill that process
        kill "$p"
    else
        # PID was reused. Better unblock the process!
        echo "long running task already completed!"
        kill -s CONT "$p"
    fi
    

यह काम करता है क्योंकि एक ही PID के साथ केवल एक ही प्रक्रिया हो सकती है और किसी दिए गए OS पर समय शुरू कर सकती है।

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


व्यक्तिगत रूप से मैं बस अजगर का उपयोग करूँगा और psutilजो PID का पुन: उपयोग स्वचालित रूप से करता है:

import time

import psutil

# note: it would be better if you were able to avoid using
#       shell=True here.
proc = psutil.Process('longrunningtask', shell=True)
time.sleep(86400)

# PID reuse handled by the library, no need to worry.
proc.terminate()   # or: proc.kill()

UNIX में पायथन के नियम ... मुझे यकीन नहीं है कि क्यों अधिक उत्तर वहां से शुरू नहीं होते हैं क्योंकि मुझे यकीन है कि अधिकांश सिस्टम इसके उपयोग को प्रतिबंधित नहीं करते हैं।
श्री मसाकारो

मैंने पहले भी इसी तरह की स्कीम (स्टार्ट टाइम का इस्तेमाल करते हुए) का इस्तेमाल किया है, लेकिन आपकी श स्क्रिप्टिंग स्किल्स मेरे मुकाबले बहुत कम हैं! धन्यवाद।
FJL

इसका मतलब है कि आप संभावित रूप से गलत प्रक्रिया को रोक रहे हैं। ध्यान दें कि ps -o start=प्रारूप थोड़ी देर के बाद 18:12 से जनवरी 26 तक बदल जाता है। DST परिवर्तन से भी सावधान रहें। यदि लिनक्स पर है, तो आप शायद पसंद करेंगे TZ=UTC0 ps -o lstart=
स्टीफन चेजलस

@ स्टीफनचैलेजेलस हाँ, लेकिन आपने इसे बाद में जारी रखने दिया। मैंने स्पष्ट रूप से कहा: उस कार्य के प्रकार पर निर्भर करता है जो उस प्रक्रिया को कर रहा है जिससे आपको कुछ मिलीसेकंड को रोकने के लिए कुछ परेशानी हो सकती है। टिप के लिए धन्यवाद lstart, मैं इसे संपादित करूँगा।
बकुरीउ

ध्यान दें कि (जब तक आपका सिस्टम प्रति उपयोगकर्ता प्रक्रियाओं की संख्या को सीमित नहीं करता है), किसी के लिए भी प्रक्रिया तालिका को लाश के साथ भरना आसान है। एक बार जब केवल 3 उपलब्ध ग्रिड बचे होते हैं, तो किसी के लिए एक ही सेकंड के भीतर सैकड़ों विभिन्न प्रक्रियाओं को शुरू करना आसान होता है। इसलिए, कड़ाई से बोलते हुए, आपका "एक ही पीआईडी ​​के साथ केवल एक प्रक्रिया हो सकती है और किसी दिए गए ओएस पर समय शुरू करना" जरूरी नहीं है।
स्टीफन चेजलस

7

एक लिनक्स सिस्टम पर आप आश्वस्त कर सकते हैं कि एक पिड अपने पिड नेमस्पेस को जीवित रखने से फिर से उपयोग नहीं किया जाएगा। यह /proc/$pid/ns/pidफ़ाइल के माध्यम से किया जा सकता है ।

  • man namespaces -

    बाइंड माउंटिंग (देखें mount(2)) इस डायरेक्टरी में मौजूद फाइलों में से एक को फाइलसिस्टम में कहीं और रखने की प्रक्रिया के संगत नेमस्पेस को जीवित रखता है, भले ही सभी प्रक्रियाएं वर्तमान में नेमस्पेस में समाप्त हो जाएं।

    इस निर्देशिका में फ़ाइलों में से एक को खोलना (या एक फ़ाइल जो इन फ़ाइलों में से एक के लिए मुहिम की जाती है) पीआईडी ​​द्वारा निर्दिष्ट प्रक्रिया के संबंधित नामस्थान के लिए एक फ़ाइल हैंडल देता है। जब तक यह फ़ाइल डिस्क्रिप्टर खुला रहता है, तब तक नेमस्पेस जीवित रहेगा, भले ही नेमस्पेस की सभी प्रक्रियाएँ समाप्त हो जाएँ। फ़ाइल डिस्क्रिप्टर को पास किया जा सकता है setns(2)

आप प्रक्रियाओं के एक समूह को अलग कर सकते हैं - मूल रूप से प्रक्रियाओं की संख्या - उनके नाम स्थान द्वारा init

  • man pid_namespaces -

    एक नया नाम स्थान में बनाया गया पहला प्रक्रिया (यानी, प्रक्रिया का उपयोग कर बनाया clone(2) के साथ CLONE_NEWPID झंडा, या एक कॉल के बाद एक प्रक्रिया के द्वारा बनाई गई पहली संतान के लिए unshare(2)का उपयोग कर CLONE_NEWPID झंडा) है पीआईडी 1 , और है initनाम स्थान के लिए प्रक्रिया ( देखें init(1)) । एक बच्चे की प्रक्रिया जो नामस्थान के भीतर अनाथ है, इस प्रक्रिया के बजाय init(1) (जब तक कि एक ही PID नामस्थान में बच्चे के पूर्वजों में से एक को prctl(2) PR_SET_CHILD_SUBBAPER आदेश दिया गया हो, अनाथ वंशज प्रक्रियाओं के रीपर के रूप में चिह्नित करने के लिए नियोजित किया जाएगा )

    यदि PID नाम स्थान की initप्रक्रिया समाप्त हो जाती है, तो कर्नेल SIGKILL सिग्नल के माध्यम से नाम स्थान की सभी प्रक्रियाओं को समाप्त कर देता है। यह व्यवहार इस तथ्य को दर्शाता है कि प्रक्रिया एक पीआईडी नाम स्थान के सही संचालन के लिए आवश्यक है।init

util-linuxपैकेज नामस्थान जोड़ तोड़ के लिए कई उपयोगी उपकरण प्रदान करता है। उदाहरण के लिए, unshareहालांकि, यदि आपने पहले से ही उपयोगकर्ता नामस्थान में इसके अधिकारों की व्यवस्था नहीं की है, तो इसके लिए सुपरसाइडर्स की आवश्यकता होगी:

unshare -fp sh -c 'n=
    echo "PID = $$"
    until   [ "$((n+=1))" -gt 5 ]
    do      while   sleep 1
            do      date
            done    >>log 2>/dev/null   &
    done;   sleep 5' >log
cat log; sleep 2
echo 2 secs later...
tail -n1 log

यदि आपने एक उपयोगकर्ता नामस्थान की व्यवस्था नहीं की है, तो आप अभी भी विशेषाधिकारों को तुरंत छोड़ने के द्वारा सुरक्षित रूप से मनमाना आदेशों को निष्पादित कर सकते हैं। runuserआदेश एक और है (गैर setuid) द्वारा प्रदान की बाइनरी util-linuxपैकेज और यह कैसा लग सकता है शामिल:

sudo unshare -fp runuser -u "$USER" -- sh -c '...'

...और इसी तरह।

उपर्युक्त उदाहरण में, दो स्विच ध्वज को पारित किए जाते हैं unshare(1), --forkजो sh -cपहले से बनाए गए बच्चे की प्रक्रिया को बनाता है और उसकी initस्थिति सुनिश्चित करता है , और वह --pidध्वज जो unshare(1)एक पिड नेमस्पेस बनाने का निर्देश देता है।

sh -cप्रक्रिया spawns पाँच backgrounded बच्चे के गोले - प्रत्येक एक inifinite whileपाश है कि के उत्पादन में संलग्न करने के लिए जारी रहेगा dateके अंत में logलंबे समय के रूप के रूप में के लिए sleep 1सही रिटर्न। स्पॉनिंग के बाद ये प्रक्रियाएं 5 सेकंड के लिए shकॉल करती हैं sleepऔर फिर समाप्त हो जाती हैं।

शायद यह ध्यान देने योग्य है कि यदि -fध्वज का उपयोग नहीं किया गया था, तो कोई भी पृष्ठभूमि वाले whileछोरों को समाप्त नहीं करेगा, लेकिन इसके साथ ...

उत्पादन:

PID = 1
Mon Jan 26 19:17:45 PST 2015
Mon Jan 26 19:17:45 PST 2015
Mon Jan 26 19:17:45 PST 2015
Mon Jan 26 19:17:45 PST 2015
Mon Jan 26 19:17:45 PST 2015
Mon Jan 26 19:17:46 PST 2015
Mon Jan 26 19:17:46 PST 2015
Mon Jan 26 19:17:46 PST 2015
Mon Jan 26 19:17:46 PST 2015
Mon Jan 26 19:17:46 PST 2015
Mon Jan 26 19:17:47 PST 2015
Mon Jan 26 19:17:47 PST 2015
Mon Jan 26 19:17:47 PST 2015
Mon Jan 26 19:17:47 PST 2015
Mon Jan 26 19:17:47 PST 2015
Mon Jan 26 19:17:48 PST 2015
Mon Jan 26 19:17:48 PST 2015
Mon Jan 26 19:17:48 PST 2015
Mon Jan 26 19:17:48 PST 2015
Mon Jan 26 19:17:48 PST 2015
2 secs later...
Mon Jan 26 19:17:48 PST 2015

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

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

5

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

  1. एक साधारण आवरण स्क्रिप्ट जो पृष्ठभूमि में आवश्यक कार्य शुरू करेगी (वैकल्पिक आउटपुट पुनर्निर्देशन के साथ), इस प्रक्रिया के पीआईडी ​​को एक फ़ाइल में लिखें, फिर प्रक्रिया को समाप्त करने (उपयोग करने wait) और फ़ाइल को निकालने के लिए प्रतीक्षा करें । यदि प्रतीक्षा के दौरान प्रक्रिया कुछ इस तरह से मारी जाती है

    kill $(cat pidfile)
    

    रैपर सिर्फ यह सुनिश्चित करेगा कि पिडफाइल को हटा दिया गया है।

  2. एक मॉनीटर रैपर, जो अपना खुद का पीआईडी डाल देगा और इसे भेजे गए संकेतों को पकड़ (और जवाब देगा)। सरल उदाहरण:

    #!/bin/bash
    p=0
    trap killit USR1

    killit () {
        printf "USR1 caught, killing %s\n" "$p"
        kill -9 $p
    }

    printf "monitor $$ is waiting\n"
    therealstuff &
    p=%1
    wait $p
    printf "monitor exiting\n"

अब, जैसा कि @ आर .. और @ स्टीफनचेज़ेलस ने बताया, इन दृष्टिकोणों में अक्सर कहीं न कहीं एक दौड़ की स्थिति होती है या उन प्रक्रियाओं की संख्या पर प्रतिबंध लगा सकते हैं जिन्हें आप स्पॉन कर सकते हैं। इसके अलावा, यह उन मामलों को नहीं संभालता है, जहां longrunningthingकांटा हो सकता है और बच्चे अलग हो जाते हैं (जो मूल प्रश्न में समस्या नहीं थी)।

हाल ही में (कुछ साल पुराने पढ़ें) लिनक्स कर्नेल, इसे अच्छी तरह से cgroups , अर्थात् फ्रीज़र - जो, मुझे लगता है का उपयोग करके इलाज किया जा सकता है , जो कि कुछ आधुनिक लिनक्स इनिट सिस्टम का उपयोग करता है।


धन्यवाद, और सभी को। मैं अब हर पढ़ रहा हूँ। इसके बारे में longrunningthingबात यह है कि आपका इस पर कोई नियंत्रण नहीं है। मैंने एक शेल स्क्रिप्ट का उदाहरण भी दिया क्योंकि इसने समस्या को समझाया। मुझे आपका और अन्य सभी रचनात्मक समाधान यहां पसंद हैं, लेकिन यदि आप लिनक्स / बैश का उपयोग कर रहे हैं तो उसके लिए "टाइमआउट" बिल्टइन है। मुझे लगता है कि मुझे उस स्रोत को प्राप्त करना चाहिए और यह देखना चाहिए कि यह कैसे करता है!
एफजेएल

@FJL, timeoutहै एक खोल builtin। timeoutलिनक्स के लिए एक कमांड के विभिन्न कार्यान्वयन हुए हैं , एक हाल ही में (2008) जीएनयू कोर्यूटिल्स में जोड़ा गया था (इसलिए लिनक्स विशिष्ट नहीं), और यही आजकल अधिकांश लिनक्स वितरण का उपयोग करता है।
स्टीफन चेज़लस

@ स्टीफन - धन्यवाद - मुझे बाद में GNU कोरुटिल्स का संदर्भ मिला। वे पोर्टेबल हो सकते हैं, लेकिन जब तक आधार प्रणाली में इसका भरोसा नहीं किया जा सकता है। मुझे यह जानने में अधिक दिलचस्पी है कि यह कैसे काम करता है, हालांकि मैं आपकी टिप्पणी को कहीं और ध्यान देता हूं कि यह 100% विश्वसनीय नहीं है। जिस तरह से यह धागा चला गया है, उसे देखते हुए मुझे आश्चर्य नहीं हुआ!
FJL

1

यदि आप लिनक्स (और कुछ अन्य * निक्स) पर चल रहे हैं, तो आप जांच सकते हैं कि क्या जिस प्रक्रिया को आप मारने का इरादा रखते हैं वह अभी भी उपयोग की जाती है और कमांड लाइन आपकी लंबी प्रक्रिया से मेल खाती है। कुछ इस तरह :

echo Time up!
grep -q longrunningthing /proc/$p/cmdline 2>/dev/null
if [ $? -eq 0 ]
then
  kill $p
fi

एक विकल्प यह जांचने के लिए हो सकता है कि आप कितने समय तक मारने का इरादा रखते हैं, कुछ इस तरह से चल रहा है ps -p $p -o etime=। आप इसे इस जानकारी को निकालने के द्वारा स्वयं कर सकते हैं /proc/$p/stat, लेकिन यह मुश्किल होगा (समय jiffies में मापा जाता है, और आपको सिस्टम अपटाइम का /proc/statभी उपयोग करना होगा )।

वैसे भी, आप आमतौर पर यह सुनिश्चित नहीं कर सकते हैं कि प्रक्रिया आपके चेक के बाद और इससे पहले कि आप इसे मार न दें।


यह अभी भी सही नहीं है क्योंकि यह दौड़ की स्थिति से छुटकारा नहीं दिलाता है।
स्ट्रैट

@strcat वास्तव में, सफलता की कोई वारंटी नहीं है, लेकिन अधिकांश स्क्रिप्ट भी इस तरह की जांच करने की जहमत नहीं उठाते हैं और केवल एक cat pidfileपरिणाम को मारते हैं । मैं इसे केवल शेल में करने के लिए एक साफ तरीके से याद नहीं कर सकता। प्रस्तावित नाम स्थान का उत्तर हालांकि एक अंतरालीय लगता है ...
उरईल

-1

यह वास्तव में एक बहुत अच्छा सवाल है।

प्रक्रिया विशिष्टता को निर्धारित करने का तरीका यह है कि (ए) जहां यह स्मृति में है; और (बी) उस मेमोरी में क्या है। विशिष्ट होने के लिए, हम यह जानना चाहते हैं कि स्मृति में प्रारंभिक आह्वान के लिए प्रोग्राम टेक्स्ट कहां है, क्योंकि हम जानते हैं कि प्रत्येक थ्रेड का पाठ क्षेत्र मेमोरी में एक अलग स्थान पर कब्जा कर लेगा। यदि प्रक्रिया मर जाती है और एक ही पीड के साथ एक और लॉन्च किया जाता है, तो नई प्रक्रिया के लिए प्रोग्राम टेक्स्ट मेमोरी में एक ही स्थान पर कब्जा नहीं करेगा और इसमें समान जानकारी नहीं होगी।

इसलिए, अपनी प्रक्रिया शुरू करने के तुरंत बाद, md5sum /proc/[pid]/mapsपरिणाम को करें और सहेजें। बाद में जब आप प्रक्रिया को मारना चाहते हैं, तो एक और md5sum करें और उसकी तुलना करें। यदि यह मेल खाता है, तो पिड को मारें। यदि नहीं, तो नहीं।

अपने लिए इसे देखने के लिए, दो समान बैश गोले लॉन्च करें। /proc/[pid]/mapsउनके लिए जांच करें और आप पाएंगे कि वे अलग हैं। क्यूं कर? क्योंकि भले ही यह एक ही कार्यक्रम है, वे स्मृति में विभिन्न स्थानों पर कब्जा कर लेते हैं और उनके ढेर के पते अलग-अलग होते हैं। इसलिए, यदि आपकी प्रक्रिया समाप्त हो जाती है और इसका PID पुन: उपयोग किया जाता है, तो समान कमांड द्वारा समान तर्कों के साथ रिलॉन्च किए जाने पर भी , "मैप्स" फ़ाइल अलग हो जाएगी और आपको पता चल जाएगा कि आप मूल प्रक्रिया के साथ काम नहीं कर रहे हैं।

विवरण के लिए देखें: मैन पेज खरीदें

ध्यान दें कि फ़ाइल में /proc/[pid]/statपहले से ही वह सभी जानकारी है जो अन्य पोस्टर ने अपने उत्तर में उल्लिखित की है: प्रक्रिया की उम्र, माता-पिता की पिड, आदि। इस फ़ाइल में स्थैतिक जानकारी और गतिशील जानकारी दोनों शामिल हैं, इसलिए यदि आप इस फाइल को आधार के रूप में उपयोग करना चाहते हैं। तुलना करने के बाद, फिर अपने लॉन्च करने पर longrunningthing, आपको statफ़ाइल से निम्न स्थिर फ़ील्ड निकालने और बाद में तुलना करने के लिए उन्हें सहेजने की आवश्यकता है:

pid, फ़ाइल नाम, माता-पिता का pid, प्रक्रिया समूह id, टर्मिनल को नियंत्रित करना, सिस्टम बूट, निवासी सेट आकार, स्टैक की शुरुआत का पता, के बाद समय प्रक्रिया शुरू

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


1
यह आम तौर /proc/[pid]/mapsपर समय के साथ बदलाव के रूप में काम नहीं करेगा क्योंकि अतिरिक्त मेमोरी आवंटित की जाती है या स्टैक बढ़ता है या नई फ़ाइलों को मिमीैप किया जाता है ... और लॉन्च के तुरंत बाद इसका क्या मतलब है? आखिरकार पुस्तकालयों को मिमीप किया गया है? आपको वह कैसे तय करना है?
स्टीफन चेज़लस

मैं अपने सिस्टम पर अब दो प्रक्रियाओं के साथ एक परीक्षण कर रहा हूं, एक जावा ऐप और दूसरा एक cfengine सर्वर। हर 15 मिनट में मैं md5sumउनके नक्शे फाइलों पर करता हूं । मैं इसे एक या दो दिन तक चलने दूँगा और परिणामों के साथ वापस यहाँ रिपोर्ट करूँगा।
माइकल मार्टिनेज

@ स्टीफनचेज़लस: मैं अब 16 घंटे के लिए अपनी दो प्रक्रियाओं की जाँच कर रहा हूं, और md5sum में कोई बदलाव नहीं हुआ है
माइकल मार्टिनेज

-1

हत्या करने से पहले प्रक्रिया की उम्र की जांच करने का एक और तरीका होगा। इस तरह, आप यह सुनिश्चित कर सकते हैं कि आप ऐसी प्रक्रिया को नहीं मार रहे हैं जो 24 घंटे से कम समय में पैदा नहीं हुई है। आप ifप्रक्रिया को मारने से पहले उसके आधार पर एक शर्त जोड़ सकते हैं ।

if [[ $(ps -p $p -o etime=) =~ 1-. ]] ; then
    kill $p
fi

यह ifस्थिति यह जांच करेगी कि प्रक्रिया आईडी $p24 घंटे (86400 सेकंड) से कम है या नहीं।

PS: - कमांड ps -p $p -o etime=का प्रारूप होगा<no.of days>-HH:MM:SS


mtimeकी /proc/$pप्रक्रिया के शुरू होने के समय से कोई संबंध नहीं है।
स्टीफन चेजलस

धन्यवाद @ स्टीफनचेज़ेलस तुम सही हो। मैंने ifहालत बदलने का जवाब संपादित किया है । अगर इसकी छोटी गाड़ी है तो कृपया बेझिझक टिप्पणी करें।
श्री

-3

मैं क्या करता हूं, इस प्रक्रिया को मारने के बाद, इसे फिर से करें। हर बार जब मैं करता हूं कि जवाब वापस आता है, "ऐसी कोई प्रक्रिया नहीं"

allenb   12084  5473  0 08:12 pts/4    00:00:00 man man
allenb@allenb-P7812 ~ $ kill -9 12084
allenb@allenb-P7812 ~ $ kill -9 12084
bash: kill: (12084) - No such process
allenb@allenb-P7812 ~ $ 

सरल नहीं हो सकता है और मैं वर्षों से बिना किसी समस्या के यह कर रहा हूं।


यह "मैं इसे कैसे बदतर बना सकता हूं" सवाल का जवाब दे रहा है, न कि "मैं इसे कैसे ठीक कर सकता हूं"।
स्टीफन चेज़लस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.