linux - प्रक्रिया समूह के सभी सदस्यों को सिग्नल भेजने का सबसे अच्छा तरीका क्या है?




bash shell (20)

मैं एक पूरी प्रक्रिया पेड़ को मारना चाहता हूँ। किसी भी सामान्य स्क्रिप्टिंग भाषाओं का उपयोग करके ऐसा करने का सबसे अच्छा तरीका क्या है? मैं एक साधारण समाधान की तलाश में हूं।


Sh में जॉब्स कमांड पृष्ठभूमि प्रक्रियाओं को सूचीबद्ध करेगा। कुछ मामलों में पहले सबसे नई प्रक्रिया को मारना बेहतर हो सकता है, उदाहरण के लिए पुराने ने एक साझा सॉकेट बनाया है। उन मामलों में पीआईडी ​​को रिवर्स ऑर्डर में सॉर्ट करें। कभी-कभी आप काम पर कुछ लिखने के लिए पल का इंतजार करना चाहते हैं या इससे पहले कि सामान बंद हो जाएं।

और अगर आपको नहीं करना है तो मत मारो!

for SIGNAL in TERM KILL; do
  for CHILD in $(jobs -s|sort -r); do
    kill -s $SIGNAL $CHILD
    sleep $MOMENT
  done
done

Zhigang के उत्तर का संशोधित संस्करण:

#!/usr/bin/env bash
set -eu

killtree() {
    local pid
    for pid; do
        kill -stop $pid
        local cpid
        for cpid in $(pgrep -P $pid); do
            killtree $cpid
        done
        kill $pid
        kill -cont $pid
        wait $pid 2>/dev/null || true
   done
}

cpids() {
    local pid=$1 options=${2:-} space=${3:-}
    local cpid
    for cpid in $(pgrep -P $pid); do
        echo "$space$cpid"
        if [[ "${options/a/}" != "$options" ]]; then
            cpids $cpid "$options" "$space  "
        fi
    done
}

while true; do sleep 1; done &
cpid=$!
for i in $(seq 1 2); do
    cpids $$ a
    sleep 1
done
killtree $cpid
echo ---
cpids $$ a

अगर आपके सिस्टम पर पस्ट्री और पर्ल है, तो आप इसे आजमा सकते हैं:

perl -e 'kill 9, (`pstree -p PID` =~ m/\((\d+)\)/sg)'

आप यह नहीं कहते कि क्या पेड़ आप मारना चाहते हैं वह एक एकल प्रक्रिया समूह है। (यह अक्सर होता है यदि पेड़ सर्वर प्रारंभ या शेल कमांड लाइन से फोर्किंग का परिणाम है।) आप निम्नानुसार जीएनयू पीएस का उपयोग कर प्रक्रिया समूहों को खोज सकते हैं:

 ps x -o  "%p %r %y %x %c "

यदि यह एक प्रक्रिया समूह है जिसे आप मारना चाहते हैं, तो बस kill(1) कमांड का उपयोग करें, लेकिन इसे एक प्रक्रिया संख्या देने के बजाय, इसे समूह संख्या की अस्वीकृति दें। उदाहरण के लिए समूह 5112 में प्रत्येक प्रक्रिया को मारने के लिए, kill -TERM -- -5112 5112 का उपयोग करें।


एक प्रक्रिया पेड़ को दोबारा मारने के लिए, killtree () का उपयोग करें:

#!/bin/bash

killtree() {
    local _pid=$1
    local _sig=${2:--TERM}
    kill -stop ${_pid} # needed to stop quickly forking parent from producing children between child killing and parent killing
    for _child in $(ps -o pid --no-headers --ppid ${_pid}); do
        killtree ${_child} ${_sig}
    done
    kill -${_sig} ${_pid}
}

if [ $# -eq 0 -o $# -gt 2 ]; then
    echo "Usage: $(basename $0) <pid> [signal]"
    exit 1
fi

killtree [email protected]

निम्नलिखित का परीक्षण फ्रीबीएसडी, लिनक्स और मैकोज़ एक्स पर किया गया है और केवल पीजीआरई पर निर्भर करता है और मारता है (पीएस -ओ संस्करण बीएसडी के तहत काम नहीं करते हैं)। पहला तर्क माता-पिता की पिड है जिसके बच्चों को समाप्त करना होगा। दूसरा तर्क यह निर्धारित करने के लिए एक बूलियन है कि माता-पिता को भी समाप्त किया जाना है या नहीं।

KillChilds() {
        local pid="${1}"
        local self="${2:-false}"

        if children="$(pgrep -P "$pid")"; then
                for child in $children; do
                        KillChilds "$child" true
                done
        fi

        if [ "$self" == true ]; then
                kill -s SIGTERM "$pid" || (sleep 10 && kill -9 "$pid" &)
        fi
}

KillChilds $$ > /dev/null 2>&1

यह किसी शेल स्क्रिप्ट के भीतर किसी भी बच्चे / पोते की प्रक्रिया में SIGTERM भेजेगा और यदि SIGTERM सफल नहीं होता है, तो यह 10 सेकंड तक प्रतीक्षा करेगा और फिर हत्या भेज देगा।

पहले जवाब:

निम्नलिखित भी काम करता है लेकिन बीएसडी पर खुद को खोल देगा।

KillSubTree() {
    local parent="${1}"
    for child in $(ps -o pid=$parent); do
            if [ $$ -ne $child ]; then (kill -s SIGTERM $child || (sleep 10 && kill -9 $child & )) > /dev/null 2>&1 ; fi
    done
}
# Example lanch from within script
KillSubTree $$ > /dev/null 2>&1

नोर्मन रैमसे के जवाब में जोड़ने के लिए, यदि आप एक प्रक्रिया समूह बनाना चाहते हैं तो सेटिड पर देखने लायक हो सकता है।
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html

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

जिसका अर्थ यह है कि आप प्रारंभिक प्रक्रिया से एक समूह बना सकते हैं। मैंने इसे शुरू करने के बाद पूरे प्रक्रिया पेड़ को मारने में सक्षम होने के लिए PHP में इसका उपयोग किया।

यह एक बुरा विचार हो सकता है। मुझे टिप्पणियों में दिलचस्पी होगी।


पुराना सवाल, मुझे पता है, लेकिन सभी प्रतिक्रियाएं पीएस को कॉल करना प्रतीत होती हैं, जिसे मैं पसंद नहीं करता था।

इस अजीब-आधारित समाधान को रिकर्सन की आवश्यकता नहीं होती है और केवल एक बार पीएस को कॉल किया जाता है।

awk 'BEGIN {
  p=1390
  while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2
  o=1
  while (o==1) {
    o=0
    split(p, q, " ")
    for (i in q) if (a[q[i]]!="") {
      p=p""a[q[i]]
      o=1
      a[q[i]]=""
    }
  }
  system("kill -TERM "p)
}'

या एक लाइन पर:

awk 'BEGIN {p=1390;while ("ps -o ppid,pid"|getline) a[$1]=a[$1]" "$2;o=1;while (o==1) {o=0;split(p, q, " ");for (i in q) {if (a[q[i]]!="") {p=p""a[q[i]];o=1;a[q[i]]=""}}}system("kill -TERM "p)}'

असल में यह विचार यह है कि हम माता-पिता के एक सरणी (ए) का निर्माण करते हैं: बाल प्रविष्टियां, फिर हमारे मिलान करने वाले माता-पिता के लिए बच्चों को ढूंढने वाले सरणी के चारों ओर लूप, उन्हें हमारे माता-पिता सूची (पी) में जोड़कर हम जाते हैं।

यदि आप शीर्ष-स्तरीय प्रक्रिया को मारना नहीं चाहते हैं, तो कर रहे हैं

sub(/[0-9]*/, "", p)

सिस्टम () लाइन से पहले इसे मारने के सेट से हटा दिया जाएगा।

ध्यान रखें कि यहां एक दौड़ की स्थिति है, लेकिन यह सभी समाधानों के सत्य (जहां तक ​​मैं देख सकता हूं) है। यह वही करता है जो मुझे चाहिए क्योंकि जिस स्क्रिप्ट की मुझे आवश्यकता थी, वह बहुत से अल्पकालिक बच्चों को नहीं बनाती है।

पाठक के लिए एक अभ्यास इसे 2-पास लूप बनाना होगा: पहले पास के बाद, पी सूची में सभी प्रक्रियाओं के लिए सिगस्टॉप भेजें, फिर पीओपी को फिर से चलाने के लिए लूप करें और दूसरे पास के बाद SIGTERM, फिर सिगकंट भेजें। यदि आपको अच्छे अंत की परवाह नहीं है तो दूसरा-पास सिग्कील हो सकता है, मुझे लगता है।


ब्रैड का जवाब वह है जो मैं भी अनुशंसा करता हूं, सिवाय इसके कि यदि आप ps लिए --ppid विकल्प का उपयोग करते हैं तो आप पूरी तरह से --ppid दूर कर सकते हैं।

for child in $(ps -o pid -ax --ppid $PPID) do ....... done

मुझे पता है कि पुराना है, लेकिन यह बेहतर समाधान है जो मैंने पाया:

killtree() { 
    for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
        echo Terminating: $p 
        kill $p
    done
}

मैं टिप्पणी नहीं कर सकता (पर्याप्त प्रतिष्ठा नहीं), इसलिए मुझे एक नया जवाब जोड़ने के लिए मजबूर किया गया है, भले ही यह वास्तव में एक उत्तर नहीं है।

28 फरवरी को @olibre द्वारा दिए गए अन्यथा बहुत अच्छे और गहन उत्तर के साथ थोड़ी सी समस्या है। ps opgid= $PID आउटपुट में पांच अंकों से कम पीआईडी ​​के लिए प्रमुख रिक्त स्थान होंगे क्योंकि ps कॉलम को उचित ठहरा रहा है (रिगथ संरेखण संख्या)। संपूर्ण कमांड लाइन के भीतर, इसका परिणाम नकारात्मक संकेत होता है, इसके बाद स्पेस (ओं) के बाद, समूह पीआईडी ​​के बाद। सरल समाधान रिक्त स्थान को हटाने के लिए tr ps को पाइप करना है:

kill -- -$( ps opgid= $PID | tr -d ' ' )

मैं यहां वर्णित विधि के थोड़ा संशोधित संस्करण का उपयोग करता हूं: https://.com/a/5311362/563175

तो ऐसा लगता है कि:

kill `pstree -p 24901 | sed 's/(/\n(/g' | grep '(' | sed 's/(\(.*\)).*/\1/' | tr "\n" " "`

जहां 24 9 01 माता-पिता का पीआईडी ​​है।

यह बहुत बदसूरत लग रहा है लेकिन यह पूरी तरह से काम करता है।


यदि आप जानते हैं कि मूल प्रक्रिया के पिड को पास करते हैं, तो यहां एक शेल स्क्रिप्ट है जो काम करनी चाहिए:

for child in $(ps -o pid,ppid -ax | \
   awk "{ if ( \$2 == $pid ) { print \$1 }}")
do
  echo "Killing child process $child because ppid = $pid"
  kill $child
done

यदि आप नाम से प्रक्रिया को मारना चाहते हैं:

killall -9 -g someprocessname

या

pgrep someprocessname | xargs pkill -9 -g

यह स्क्रिप्ट भी काम करती है:

#/bin/sh while true do echo "Enter parent process id [type quit for exit]" read ppid if [ $ppid -eq "quit" -o $ppid -eq "QUIT" ];then exit 0 fi for i in `ps -ef| awk '$3 == '$ppid' { print $2 }'` do echo killing $i kill $i done done


यहां @ zhigang के उत्तर की एक भिन्नता है जो एडब्ल्यूके के बिना करता है, केवल बैश की मूल पार्सिंग संभावनाओं पर निर्भर करता है:

function killtree {
  kill -STOP "$1"
  ps -e -o pid= -o ppid= | while read -r pid ppid
                           do
                             [[ $ppid = $1 ]] || continue
                             killtree "$pid"  || true # Skip over failures
                           done
  kill -CONT "$1"          
  kill -TERM "$1"
}

यह मैक और लिनक्स दोनों पर ठीक काम करता प्रतीत होता है। ऐसी परिस्थितियों में जहां आप प्रक्रिया समूहों को प्रबंधित करने में सक्षम होने पर भरोसा नहीं कर सकते - जैसे सॉफ़्टवेयर के एक टुकड़े का परीक्षण करने के लिए स्क्रिप्ट लिखना, जिसे कई वातावरण में बनाया जाना चाहिए - यह पेड़-चलने वाली तकनीक निश्चित रूप से सहायक है।


Ysth की टिप्पणी से प्रेरित होकर

kill -- -PGID

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


psutil का उपयोग कर अजगर के साथ ऐसा करना बहुत आसान है। बस पाइप के साथ psutil स्थापित करें और फिर आपके पास प्रक्रिया हेरफेर उपकरण का एक पूर्ण सूट है:

def killChildren(pid):
    parent = psutil.Process(pid)
    for child in parent.get_children(True):
        if child.is_running():
            child.terminate()

प्रक्रिया समूह आईडी ( PGID ) का उपयोग कर एक ही प्रक्रिया पेड़ से संबंधित सभी प्रक्रियाओं को मार दें

  • kill -- -$PGID डिफ़ॉल्ट सिग्नल का उपयोग करें ( TERM = 15)
  • kill -9 -$PGID सिग्नल kill -9 -$PGID उपयोग करें (9)

आप एक ही प्रक्रिया पेड़ के किसी भी प्रक्रिया-आईडी ( PID ) से PGID पुनर्प्राप्त कर सकते हैं

  • kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*') (सिग्नल TERM )
  • kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*') (सिग्नल KILL )

$PID शेष रिक्त स्थान और ओएसएक्स संगतता पर योगदान के लिए Speakus और Speakus लिए विशेष धन्यवाद।

व्याख्या

  • kill -9 -"$PGID" => सभी बच्चे और पोते को सिग्नल 9 ( kill -9 -"$PGID" ) भेजें ...
  • PGID=$(ps opgid= "$PID") => पेड़ की किसी भी प्रक्रिया-आईडी से प्रक्रिया-समूह-आईडी पुनर्प्राप्त करें, न केवल प्रक्रिया-अभिभावक-आईडीps opgid= $PID की एक भिन्नता ps -o pgid --no-headers $PID जहां pgid को pgrp द्वारा प्रतिस्थापित किया जा सकता है।
    परंतु:
    • ps प्रमुख स्थानों को सम्मिलित करता है जब PID पांच अंकों से कम होता है और tanager द्वारा देखा गया सही गठबंधन होता है। आप उपयोग कर सकते हैं:
      PGID=$(ps opgid= "$PID" | tr -d ' ')
    • ओएसएक्स से ps हमेशा हेडर प्रिंट करता है, इसलिए Speakus का प्रस्ताव है:
      PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
  • grep -o [0-9]* केवल क्रमिक अंक प्रिंट करता है (रिक्त स्थान या वर्णमाला शीर्षलेख मुद्रित नहीं करता है)।

आगे कमांड लाइनें

PGID=$(ps -o pgid= $PID | grep -o [0-9]*)
kill -TERM -"$PGID"  # kill -15
kill -INT  -"$PGID"  # correspond to [CRTL+C] from keyboard
kill -QUIT -"$PGID"  # correspond to [CRTL+\] from keyboard
kill -CONT -"$PGID"  # restart a stopped process (above signals do not kill it)
sleep 2              # wait terminate process (more time if required)
kill -KILL -"$PGID"  # kill -9 if it does not intercept signals (or buggy)

सीमा

  • जैसा कि davide और हबर्ट davide द्वारा देखा गया है, जब एक ही पेड़ से संबंधित प्रक्रिया द्वारा kill जाता है, तो पूरे पेड़ की हत्या को समाप्त करने से पहले खुद को मारने के जोखिम को मार दें।
  • इसलिए, एक अलग प्रक्रिया-समूह-आईडी वाली प्रक्रिया का उपयोग करके कमांड को चलाने के लिए सुनिश्चित रहें

लम्बी कहानी

> cat run-many-processes.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./child.sh background &
./child.sh foreground
echo "ProcessID=$$ ends ($0)"

> cat child.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
./grandchild.sh background &
./grandchild.sh foreground
echo "ProcessID=$$ ends ($0)"

> cat grandchild.sh
#!/bin/sh
echo "ProcessID=$$ begins ($0)"
sleep 9999
echo "ProcessID=$$ ends ($0)"

'&' का उपयोग करके पृष्ठभूमि में प्रक्रिया पेड़ चलाएं

> ./run-many-processes.sh &    
ProcessID=28957 begins (./run-many-processes.sh)
ProcessID=28959 begins (./child.sh)
ProcessID=28958 begins (./child.sh)
ProcessID=28960 begins (./grandchild.sh)
ProcessID=28961 begins (./grandchild.sh)
ProcessID=28962 begins (./grandchild.sh)
ProcessID=28963 begins (./grandchild.sh)

> PID=$!                    # get the Parent Process ID
> PGID=$(ps opgid= "$PID")  # get the Process Group ID

> ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
28348 28349 28349 28349 pts/3    28969 Ss   33021   0:00 -bash
28349 28957 28957 28349 pts/3    28969 S    33021   0:00  \_ /bin/sh ./run-many-processes.sh
28957 28958 28957 28349 pts/3    28969 S    33021   0:00  |   \_ /bin/sh ./child.sh background
28958 28961 28957 28349 pts/3    28969 S    33021   0:00  |   |   \_ /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3    28969 S    33021   0:00  |   |   |   \_ sleep 9999
28958 28963 28957 28349 pts/3    28969 S    33021   0:00  |   |   \_ /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3    28969 S    33021   0:00  |   |       \_ sleep 9999
28957 28959 28957 28349 pts/3    28969 S    33021   0:00  |   \_ /bin/sh ./child.sh foreground
28959 28960 28957 28349 pts/3    28969 S    33021   0:00  |       \_ /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3    28969 S    33021   0:00  |       |   \_ sleep 9999
28959 28962 28957 28349 pts/3    28969 S    33021   0:00  |       \_ /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3    28969 S    33021   0:00  |           \_ sleep 9999
28349 28969 28969 28349 pts/3    28969 R+   33021   0:00  \_ ps fj

आदेश pkill -P $PID पोते को मार नहीं है:

> pkill -P "$PID"
./run-many-processes.sh: line 4: 28958 Terminated              ./child.sh background
./run-many-processes.sh: line 4: 28959 Terminated              ./child.sh foreground
ProcessID=28957 ends (./run-many-processes.sh)
[1]+  Done                    ./run-many-processes.sh

> ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
28348 28349 28349 28349 pts/3    28987 Ss   33021   0:00 -bash
28349 28987 28987 28349 pts/3    28987 R+   33021   0:00  \_ ps fj
    1 28963 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh foreground
28963 28967 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999
    1 28962 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh foreground
28962 28966 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999
    1 28961 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh background
28961 28965 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999
    1 28960 28957 28349 pts/3    28987 S    33021   0:00 /bin/sh ./grandchild.sh background
28960 28964 28957 28349 pts/3    28987 S    33021   0:00  \_ sleep 9999

कमांड kill -- -$PGID समेत सभी प्रक्रियाओं को मारता है।

> kill --    -"$PGID"  # default signal is TERM (kill -15)
> kill -CONT -"$PGID"  # awake stopped processes
> kill -KILL -"$PGID"  # kill -9 to be sure

> ps fj
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
28348 28349 28349 28349 pts/3    29039 Ss   33021   0:00 -bash
28349 29039 29039 28349 pts/3    29039 R+   33021   0:00  \_ ps fj

निष्कर्ष

मुझे लगता है कि इस उदाहरण में PID और PGID बराबर हैं ( 28957 9 28957 )।
यही कारण है कि मैंने मूल रूप से kill -- -$PID पर्याप्त था। लेकिन मामले में मेकफ़ाइल के भीतर प्रक्रिया Makefile , प्रक्रिया आईडी समूह आईडी से अलग है।

मुझे लगता है कि kill -- -$(ps -o pgid= $PID | grep -o [0-9]*) एक अलग समूह आईडी (एक अन्य प्रक्रिया पेड़) से बुलाए जाने पर एक संपूर्ण प्रक्रिया पेड़ को मारने का सबसे आसान सरल चाल है।


pkill -TERM -P 27888

यह उन सभी प्रक्रियाओं को मार देगा जिनमें मूल प्रक्रिया आईडी 27888 है।

या अधिक मजबूत:

CPIDS=$(pgrep -P 27888); (sleep 33 && kill -KILL $CPIDS &); kill -TERM $CPIDS

जो 33 सेकंड बाद हत्या कर रहा है और विनम्रतापूर्वक प्रक्रियाओं को समाप्त करने के लिए कहता है।

सभी वंशजों को समाप्त करने के लिए यह उत्तर देखें।





signals