Bir süreç grubunun tüm üyelerine sinyal göndermenin en iyi yolu nedir?


422

Bütün bir süreç ağacını öldürmek istiyorum. Herhangi bir yaygın komut dosyası dilini kullanarak bunu yapmanın en iyi yolu nedir? Basit bir çözüm arıyorum.


3
Sistem orak makinesi çalıştığında zombiler kaybolmalıdır. İtiraf edeceğim ki, zombilerin oyalandığı sistemleri gördüm, ama bu atipik.
Brian Knoblauch

7
Bazen o kalan zombiler bazı korkutucu faaliyetlerden sorumludur.
Kullanıcı1

chronosVeya herodeskomutlarından birini kullanın .
Michael Le Barbier Grünewald

8
kill $ (pstree <PID> -p -a -l | kesim -d, -f2 | kesim -d '' -f1)
vrdhn

@ MichaelLeBarbierGrünewald Bu programlara bağlantı verebilir misiniz?
strafor uçmak

Yanıtlar:


305

Öldürmek istediğiniz ağacın tek bir süreç grubu olup olmadığını söylemezsiniz. (Bu genellikle ağaç bir sunucu başlangıcından veya kabuk komut satırından çatallanmanın sonucuysa geçerlidir.) GNU ps'yi kullanarak işlem gruplarını aşağıdaki gibi keşfedebilirsiniz:

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

Eğer öldürmek istediğiniz bir süreç grubuysa, sadece kill(1)komutu kullanın, fakat ona bir işlem numarası vermek yerine , grup numarasının reddini verin . Örneğin, 5112 grubundaki her işlemi öldürmek için kullanın kill -TERM -- -5112.


3
kill -74313 -bash: kill: 74313: geçersiz sinyal belirtimi Kill -15 -GPID'yi eklersem mükemmel çalıştı.
Adam Peck

51
Her zamanki gibi her komutta olduğu gibi, - ile başlayan normal bir argümanın anahtar olarak yorumlanmamasını istiyorsanız, - ile önce -: kill - -GPID
ysth

9
pgrepişlem grubu kimliğini bulmanın daha kolay bir yolunu sunabilir. Örneğin, my-script.sh'nin işlem grubunu öldürmek için çalıştırın kill -TERM -$(pgrep -o my-script.sh).
Josh Kelley

9
Stackoverflow.com/questions/392022/… daha iyi bakmak onun çok daha zarif bir çözüm ve çocukların pids listelemek gerekiyorsa o zaman kullanın:ps -o pid --no-headers --ppid $PARENT_PID
Szymon Jeż

4
Ve formatı biraz değiştirir ve sıralarsanız, tüm süreçleri güzel bir şekilde gruplandırır ve her grupta (potansiyel olarak) grup üst öğesinden ps x -o "%r %p %y %x %c" | sort -nk1,2
başlayarak görürsünüz

199

İşlem Grubu Kimliği'ni ( ) kullanarak aynı işlem ağacına ait tüm işlemleri öldürünPGID

  • kill -- -$PGID     Varsayılan sinyali kullan ( TERM= 15)
  • kill -9 -$PGID     Sinyali kullanma KILL(9)

Sen alabilirsiniz PGIDherhangi birinden Proses-ID ( PIDaynı) işlem ağacının

  • kill -- -$(ps -o pgid= $PID | grep -o '[0-9]*')   (sinyal TERM)
  • kill -9 -$(ps -o pgid= $PID | grep -o '[0-9]*')   (sinyal KILL)

Kalan alanlara katkılar ve OSX uyumluluğu için tanager ve Speakus'a özel teşekkürler $PID.

açıklama

  • kill -9 -"$PGID"=> KILLTüm çocuk ve torunlara sinyal 9 ( ) gönder ...
  • PGID=$(ps opgid= "$PID")=> Process-Group-ID değerini yalnızca Process-Parent-ID değerinden değil, ağacın herhangi bir Process -ID'sinden al . Bir varyasyonu ps opgid= $PIDIS ile ikame edilmiş olabilir . Fakat: ps -o pgid --no-headers $PIDpgidpgrp
    • psPIDbeş basamaktan az olduğunda ve tanager tarafından fark edildiği gibi sağa hizalandığında önde gelen boşlukları ekler . Kullanabilirsiniz:
      PGID=$(ps opgid= "$PID" | tr -d ' ')
    • psOSX'ten her zaman başlığı yazdırır, bu nedenle Speakus şunları önerir:
      PGID="$( ps -o pgid "$PID" | grep [0-9] | tr -d ' ' )"
  • grep -o [0-9]* yalnızca art arda rakamlar yazdırır (boşluk veya alfabetik başlık yazdırmaz).

Diğer komut satırları

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)

sınırlama

  • Tarafından fark gibi davide ve Hubert Kario , ne zaman killaynı ağaca ait bir işlemle çağrılır,kill bütün ağaç öldürme sonlandırmadan önce kendisini öldürmeye riske atar.
  • Bu nedenle, farklı bir Process-Group-ID'ye sahip bir işlemi kullanarak komutu çalıştırdığınızdan emin olun .

Uzun Hikaye

> 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)"

İşlem ağacını '&' kullanarak arka planda çalıştırın

> ./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

Komut pkill -P $PIDtorunu öldürmez:

> 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

Komut kill -- -$PGID, torun da dahil olmak üzere tüm işlemleri öldürür.

> 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

Sonuç

Bu örnekte fark ettim PIDve PGIDeşittir ( 28957).
Bu yüzden başlangıçta kill -- -$PIDyeterli olduğunu düşündüm . Ama durumda süreç içinde yumurtlamaya edilir İşlem Kimliği farklıdır Grup kimliği .Makefile

kill -- -$(ps -o pgid= $PID | grep -o [0-9]*)Farklı bir grup kimliğinden (başka bir işlem ağacı) çağrıldığında tüm süreç ağacını öldürmenin en basit hilesi olduğunu düşünüyorum .


1
Merhaba @ davide. İyi soru. killKendi sinyalini almadan önce her zaman sinyali tüm ağaca göndermesi gerektiğini düşünüyorum . Ancak bazı özel durumlarda / uygulamalarda, killkendi kendine sinyal gönderebilir, kesintiye uğrayabilir ve sonra kendi sinyalini alabilir. Bununla birlikte, risk yeterince az olmalıdır ve çoğu durumda göz ardı edilebilir, çünkü bundan önce başka hatalar meydana gelmelidir. Davanızda bu risk göz ardı edilebilir mi? Dahası, diğer cevaplar bu ortak hataya sahiptir ( killsüreç ağacının bir kısmı öldürülmektedir). Umarım bu yardım .. Şerefe;)
olibre

3
Bu sadece alt komutların kendilerinin grup lideri olmaması durumunda işe yarar. Bunun gibi basit araçlar bile man. Öte yandan, çocuk sürecinden gandchild sürecini öldürmek istiyorsanız, kill -- -$pidişe yaramaz. Yani genel bir çözüm değil.
Hubert Kario

1
Örnek, çocuklarını öldürmeye çalışan "çocuk" (kullanıcı tarafından başlatılan komutun torunları). IOW, arka plan işlem hiyerarşisini öldürmeye çalışın child.sh.
Hubert Kario

1
> kill -QUIT - "$ PGID" # klavyeden [CRTL + C] ile aynı sinyal ---- QUIT doğru olarak INT olarak değiştirilmelidir
Maxim Kholyavkin

1
OSX seçeneği için - no-headers desteklenmediği için kod şu şekilde güncellenmelidir: PGID = "$ (ps -o pgid" $ PID "| grep [0-9] | tr -d '')"
Maxim Kholyavkin

166
pkill -TERM -P 27888

Bu, üst işlem kimliği 27888 olan tüm işlemleri öldürür.

Veya daha sağlam:

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

33 saniye sonra öldürmeyi planlayan ve kibarca süreçlerin sona erdirilmesini isteyen.

Tüm torunları sonlandırmak için bu cevaba bakınız .


19
Hızlı testimde, pgrep sadece yakın çocukları rapor etti, bu yüzden bu hiyerarşinin tamamını öldürmeyebilir.
haridsv

10
Katılıyorum @haridsv: pkill -Psadece çocuğa sinyal gönderir => torun sinyal almaz => Bu yüzden bunu açıklamak için başka bir cevap wroten var. Şerefe ;-)
olibre

1
Bir bash senaryosundan, kendi çocuklarınızı öldürmek için kullanın pkill -TERM -P ${$}.
François Beausoleil

3
@Onlyjob, uyumak ve sonra öldürmek tehlikeli değil mi? İşlem kimlikleri işletim sistemi tarafından bu süre zarfında yeniden kullanılmış olabilir: artık çocuklarınız olmayan işlemleri öldürüyor olabilirsiniz. Buna karşı emin olmak için pkill çağrısının tekrar yapılması gerektiğinden şüpheleniyorum.
François Beausoleil

2
@Onlyjob FYI, makinemde 19 saniyeden az bir sürede hafif G / Ç erişimi olan 32768 işlem, tek dişli $ time for i in {1..32768}; do ( echo $BASHPID >> pids ); done real 0m18.860s
üretebiliyorum

102

Bir işlem ağacını özyinelemeli olarak öldürmek için killtree () kullanın:

#!/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 $@

3
--Argümanlar için pso yerine orada çalışır hale OS X'de To değil işi ps: Komudu önce tanımlanır : döngüps ax -o "pid= ppid=" | grep -E "${_regex}" | sed -E "s/${_regex}/\1/g_regexforlocal _regex="[ ]*([0-9]+)[ ]+${_pid}"
artur

5
Durdurulmuş süreçler SIGTERM ile öldürülmez. Cevabımı
x-yuri

1
-1, #! / Usr / bin / env bash yerine #! / Bin / bash kullanır (veya daha iyisi henüz POSIX yalnızca yapılar ve / bin / sh kullanır)
Good Person

killtree()Güvenilir çalışmasını garanti etmek için SIGTERM yerine bir SIGKILL göndermek yeterli olur mu?
davide

3
eğer psdesteklemediği --ppid, birini kullanabilirsiniz pgrep -P ${_pid}yerine
Hubert Kario

16

rkill gelen komut pslist paketin sinyalini (veya verilen gönderirSIGTERMbelirtilen süreç ve tüm onun soyundan varsayılan olarak):

rkill [-SIG] pid/name...

11

brad'nin cevabı ben de tavsiye ederim, ancak seçeneği awkkullanırsanız tamamen ortadan kaldırabilirsiniz .--ppidps

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

-Ax'ı çıkarmadıkça bu benim için işe yaramıyor, nedense (Centos5). Aksi takdirde bu harika!
xitrium

9

Üst sürecin pid'ini geçerseniz, işleyecek bir kabuk betiği aşağıdadır:

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

"Ax" yerine "-ax" kullanırsanız ps'nin bazı sürümleri uyarı verir. Böylece: $ (ps -o pid, ppid axe | \ awk "{if (\ $ 2 == $ pid) {print \ $ 1}}") içindeki çocuklar için
Cory R. King

9

Burada açıklanan bir yöntemin biraz değiştirilmiş bir sürümünü kullanın: https://stackoverflow.com/a/5311362/563175

Yani şöyle görünüyor:

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

burada 24901 ebeveynin PID'sidir.

Oldukça çirkin görünüyor ama mükemmel bir iş çıkarıyor.


1
Sed yerine grep ile sadeleştirme ...pstree -p 24901 | grep -oP '(?<=\()[0-9]+(?=\))'
anishsane

2
eklemeniz -lgerekir pstree, bu yüzden uzun çizgiler kesilmez; ile okumak daha kolay yapılabilir kill `pstree -l -p 24901 |grep "([[:digit:]]*)" -o |tr -d '()'`( \niyi çalışacağı için uzaya dönüştürmeye gerek yok ), thx!
Kova Gücü

9

Zhigang'ın cevabının değiştirilmiş versiyonu:

#!/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

tüm başlangıçlarda değil, wait $pidyalnızca başlattığınız süreçlerde yapabilirsiniz , bu yüzden bu genel bir çözüm değildir
Hubert Kario

@Hubert Kario Bu durumda, bekleme sıfır dışında bir durumla çıkar ve komut dosyasını yürütmeye devam eder. Yanlış mıyım? Ama eğer bir waitçocuksa Terminatedmesajı bastırır .
x-yuri

9

Yorum yapamıyorum (yeterli itibar), bu yüzden bu gerçekten bir cevap olmasa da yeni bir cevap eklemek zorunda kalıyorum .

28 Şubat'ta @olibre tarafından verilen aksi halde çok güzel ve kapsamlı bir cevapla ilgili küçük bir sorun var. Çıktı , sütunu haklı gösterdiği için (rakamları tam olarak hizalayın) ps opgid= $PIDbeş basamaktan kısa bir PID için önde gelen boşluklar içerecektir ps. Komut satırının tamamında negatif bir işaret, ardından boşluk (lar) ve PID grubu gelir. Basit çözüm, boşlukları kaldırmak için boruya psbağlamaktır tr:

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

Teşekkür ederim tanager. Cevabımı düzeltirim;) Şerefe
olibre

8

Norman Ramsey'in cevabına eklemek için, bir süreç grubu oluşturmak istiyorsanız setid'e bakmaya değer olabilir.
http://pubs.opengroup.org/onlinepubs/009695399/functions/setsid.html

Çağıran süreç bir süreç grubu lideri değilse, setsid () işlevi yeni bir oturum oluşturur. Geri dönüşün ardından çağrı süreci, bu yeni oturumun oturum lideri, yeni bir süreç grubunun süreç grubu lideri olacak ve kontrol terminali bulunmayacaktır. Çağıran sürecin işlem grubu kimliği, çağıran işlemin işlem kimliğine eşit olarak ayarlanmalıdır. Çağıran süreç, yeni süreç grubundaki tek süreç ve yeni oturumdaki tek süreç olacaktır.

Yani başlangıç ​​sürecinden bir grup oluşturabileceğiniz anlamına geliyor. Ben php bu işlem başladıktan sonra tüm süreç ağaç öldürmek için kullanılır.

Bu kötü bir fikir olabilir. Yorumlarla ilgilenirim.


Aslında bu harika bir fikir ve çok iyi çalışıyor. İşlemleri aynı süreç grubuna koyabileceğim durumlarda kullanıyorum (veya hepsi aynı gruptaydı).
sickill

7

Ysth adlı kullanıcının yorumundan ilham aldı

kill -- -PGID

işlem numarası vermek yerine grup numarasının reddini verin. Her zamanki gibi her komutta olduğu gibi, a ile başlayan normal bir argümanın -anahtar olarak yorumlanmamasını istiyorsanız,--


Hata! Sadece seninle aynı cevabı verdiğimi fark ettim => +1. Ama dahası ben sadece nasıl yapıldığını anlatmaya PGIDgelen PID. Ne düşünüyorsun? Şerefe
olibre

5

Aşağıdaki kabuk işlevi diğer yanıtların çoğuna benzer, ancak Linux ve BSD'de (OS X, vb.) Dış bağımlılıklar olmadan çalışır pgrep:

killtree() {
    local parent=$1 child
    for child in $(ps -o ppid= -o pid= | awk "\$1==$parent {print \$2}"); do
        killtree $child
    done
    kill $parent
}

Bence 2. satırın sonunda fazladan "çocuk" kelimesi var.
mato

@mato - Bu ekstra değil. Kapsamını sınırlar$childAynı ada sahip diğer (yerel olmayan) değişkenleri rahatsız etmemek ve işlev sona erdikten sonra yerel değişkenin değerinin temizlenmesini sağlamak için bu işlevin .
Adam Katz

Oh, şimdi görüyorum, görevin bir parçası olduğunu düşündüm. Yorum yazmadan önce tekrar okumam (daha yavaş) sanırım. :) Teşekkürler.
mato

Btw, bir çocuk listesi oluşturmak ve daha sonra üstten (ebeveyn) öldürmeye başlamak daha iyi olmaz mıydı? .. Böylece, bir ebeveyn bir çocuğu yeniden yarattığında veya bir sonraki kodu çalıştırmaya devam ettiği ve olası davranışı değiştirebilecek bir durumdan kaçınabiliriz.
mato

5

Bunu psutil kullanarak python ile yapmak çok kolay . Sadece pip ile psutil yükleyin ve daha sonra tam bir süreç manipülasyon araçları paketine sahipsiniz:

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

5

Zhigang'ın cevabına dayanarak, bu kendini öldürmeyi önler:

init_killtree() {
    local pid=$1 child

    for child in $(pgrep -P $pid); do
        init_killtree $child
    done
    [ $pid -ne $$ ] && kill -kill $pid
}

4

Bir işlemi adıyla öldürmek istiyorsanız:

killall -9 -g someprocessname

veya

pgrep someprocessname | xargs pkill -9 -g

3

Bu benim bash betiği kullanarak tüm alt işlemleri öldürmek benim versiyonu. Özyineleme kullanmaz ve pgrep komutuna bağlıdır.

kullanım

killtree.sh PID SIGNAL

Killtrees.sh içeriği

#!/bin/bash
PID=$1
if [ -z $PID ];
then
    echo "No pid specified"
fi

PPLIST=$PID
CHILD_LIST=`pgrep -P $PPLIST -d,`

while [ ! -z "$CHILD_LIST" ]
do
    PPLIST="$PPLIST,$CHILD_LIST"
    CHILD_LIST=`pgrep -P $CHILD_LIST -d,`
done

SIGNAL=$2

if [ -z $SIGNAL ]
then
    SIGNAL="TERM"
fi
#do substring from comma to space
kill -$SIGNAL ${PPLIST//,/ }

1
Alt liste oluşturulduktan sonra yeni işlemler oluşturulursa bu başarısız olur.
ceving

3

Yalnızca Bash'in yerel ayrıştırma olasılıklarına dayanarak AWK'sız yapılan @ zhigang'ın cevabının bir çeşidi:

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"
}

Hem Mac'lerde hem de Linux'ta iyi çalışıyor gibi görünüyor. Süreç gruplarını yönetemeyeceğiniz durumlarda - birden fazla ortamda oluşturulması gereken bir yazılım parçasını test etmek için komut dosyaları yazarken olduğu gibi - bu ağaç yürüyüş tekniği kesinlikle yararlıdır.


1

Bilgeliğiniz için teşekkürler millet. Betiğim çıkışta bazı alt süreçler bırakıyordu ve olumsuzlama ipucu işleri kolaylaştırdı. Bu işlevi gerekirse diğer komut dosyalarında kullanılmak üzere yazdım:

# kill my group's subprocesses:          killGroup
# kill also myself:                      killGroup -x
# kill another group's subprocesses:     killGroup N  
# kill that group all:                   killGroup -x N
# N: PID of the main process (= process group ID).

function killGroup () {
    local prid mainpid
    case $1 in
        -x) [ -n "$2" ] && kill -9 -$2 || kill -9 -$$ ;;
        "") mainpid=$$ ;;
         *) mainpid=$1 ;;
    esac
    prid=$(ps ax -o pid,pgid | grep $mainpid)
    prid=${prid//$mainpid/}
    kill -9 $prid 2>/dev/null
    return
}

Şerefe.


1

sisteminizde pstree ve perl varsa, bunu deneyebilirsiniz:

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

1

Ebeveynleri çocuklardan önce öldürmek muhtemelen daha iyidir; aksi halde ebeveyn kendini öldürmeden önce tekrar yeni çocuklar doğurabilir. Bunlar öldürmeden kurtulur.

Benim ps versiyonum yukarıdakinden farklı; belki çok yaşlı, bu yüzden garip selamlama ...

Kabuk fonksiyonu yerine kabuk komut dosyası kullanmanın birçok avantajı vardır ...

Ancak, temelde zhigangs fikir


#!/bin/bash
if test $# -lt 1 ; then
    echo >&2 "usage: kiltree pid (sig)"
fi ;

_pid=$1
_sig=${2:-TERM}
_children=$(ps j | grep "^[ ]*${_pid} " | cut -c 7-11) ;
echo >&2 kill -${_sig} ${_pid}
kill -${_sig} ${_pid}
for _child in ${_children}; do
    killtree ${_child} ${_sig}
done

@zhighang betiği SIGSTOP'ların üst işlemi yürüttüğünü ve durdurulan işleme sinyal verdiğini unutmayın, bu nedenle (AFAIK), çocuk oluşturma işlemi ile sinyal teslimi arasında bir yarış durumuna neden olmamalıdır. Sürümünüz, çocukların listesini almak ve ebeveynlere sinyal dağıtımı arasında bir yarışa sahiptir.
Hubert Kario

1

Aşağıdakiler FreeBSD, Linux ve MacOS X üzerinde test edilmiştir ve sadece pgrep ve kill'e bağlıdır (ps -o sürümleri BSD altında çalışmaz). İlk argüman çocukların feshedilmesi gereken ana pid. ikinci argüman, ana pid'in de sonlandırılması gerekip gerekmediğini belirlemek için bir mantıksaldır.

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

Bu, SIGTERM'i bir kabuk komut dosyası içindeki herhangi bir alt / torun işlemine gönderir ve SIGTERM başarılı olmazsa, 10 saniye bekleyecek ve sonra kill gönderecektir.


Daha önce cevap:

Aşağıdakiler de işe yarıyor ancak BSD'de kabuğun kendisini öldürecek.

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

1

Zhigang, xyuri ve solidsneck'in çözümünü daha da geliştiriyorum:

 #!/bin/bash

if test $# -lt 1 ; then
    echo >&2 "usage: kiltree pid (sig)"
    exit 1 ;
  fi ;

_pid=$1
_sig=${2:-TERM}

# echo >&2 "killtree($_pid) mypid = $$"
# ps axwwf | grep -6 "^[ ]*$_pid " >&2 ;

function _killtree () {
    local _children
    local _child
    local _success

    if test $1 -eq $2 ; then # this is killtree - don't commit suicide!
        echo >&2 "killtree can´t kill it´s own branch - some processes will survive." ; 
        return 1 ;
      fi ;
    # this avoids that children are spawned or disappear.
    kill -SIGSTOP $2 ;

    _children=$(ps -o pid --no-headers --ppid $2) ;        
    _success=0 
    for _child in ${_children}; do
        _killtree $1 ${_child} $3 ;
        _success=$(($_success+$?)) ;
      done ;

    if test $_success -eq 0 ; then
        kill -$3 $2
      fi ;
    # when a stopped process is killed, it will linger in the system until it is continued
    kill -SIGCONT $2
    test $_success -eq 0 ;
    return $?
    }

_killtree $$ $_pid $_sig

Bu sürüm, atalarını öldürmekten kaçınacaktır - bu, önceki çözümlerde çocuk süreçlerinin sele neden olur.

Alt liste belirlenmeden önce süreçler düzgün şekilde durdurulur, böylece yeni çocuk oluşturulmaz veya yok olmaz.

Öldükten sonra, durdurulan işler sistemden kaybolmaya devam etmelidir.


1

Eski soru, biliyorum, ama tüm cevaplar sevmediğim ps aramaya devam ediyor gibi görünüyor.

Bu awk tabanlı çözüm özyineleme gerektirmez ve yalnızca ps'yi bir kez çağırır.

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)
}'

Veya tek satırda:

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)}'

Temel olarak fikir, bir ebeveyn: çocuk girişleri dizisi (a) oluşturmamız, ardından eşleşen ebeveynlerimiz için çocukları bulan dizinin etrafında dolaşıp, onları gittikçe ebeveynler listemize (p) eklediğimizdir.

Üst düzey süreci öldürmek istemiyorsanız,

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

system () satırının onu kill setinden çıkarmadan hemen önce.

Burada bir yarış koşulu olduğunu aklınızda bulundurun, ancak tüm çözümler için bu (görebildiğim kadarıyla) doğrudur. İhtiyacım olanı yapıyor çünkü ihtiyacım olan senaryo çok kısa ömürlü çocuklar yaratmıyor.

Okuyucu için bir alıştırma, 2 geçişli bir döngü yapmak olacaktır: ilk geçişten sonra, p listesindeki tüm işlemlere SIGSTOP gönderin, sonra ps'yi tekrar çalıştırmak için döngü yapın ve ikinci geçişten sonra SIGTERM, ardından SIGCONT gönderin. Güzel sonları umursamıyorsanız, ikinci geçiş sadece SIGKILL olabilir, sanırım.


0

Öldürmek istediğiniz şeyin pidesini biliyorsanız, genellikle oturum kimliğinden ve aynı oturumdaki her şeyden gidebilirsiniz. Ben iki kez kontrol ediyorum, ama ben ölmek istediğim döngüler rsyncs başlayan komut dosyaları için kullandım ve sadece rsync killall'd olurdu gibi (döngü nedeniyle) başka bir başlatmayın.

kill $(ps -o pid= -s $(ps -o sess --no-heading --pid 21709))

Pid'i bilmiyorsanız yine de daha fazla yuva yapabilirsiniz

kill $(ps -o pid= -s $(ps -o sess --no-heading --pid $(pgrep rsync )))


0

Sh içinde jobs komutu arka plan işlemlerini listeler. Bazı durumlarda, ilk önce en yeni işlemi öldürmek daha iyi olabilir, örn. Daha eski olanı paylaşılan bir soket oluşturmuş olabilir. Bu durumlarda PID'leri ters sırayla sıralayın. Bazen işlerin durmadan önce diske veya benzeri bir şeye yazmaları için biraz beklemek isteyebilirsiniz.

Ve eğer zorunda değilsen öldürme!

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

0

Kabuk betiğinde alt işlemi öldürme:

Çoğu zaman, bir sebepten dolayı asılan veya engellenen çocuk sürecini öldürmemiz gerekir. Örneğin. FTP bağlantısı sorunu.

İki yaklaşım vardır,

1) Her çocuk için, zaman aşımına ulaşıldığında çocuk sürecini izleyecek ve öldürecek ayrı yeni ebeveyn oluşturmak.

Test.sh dosyasını aşağıdaki gibi oluşturun,

#!/bin/bash

declare -a CMDs=("AAA" "BBB" "CCC" "DDD")
for CMD in ${CMDs[*]}; do
    (sleep 10 & PID=$!; echo "Started $CMD => $PID"; sleep 5; echo "Killing $CMD => $PID"; kill $PID; echo "$CMD Completed.") &
done
exit;

ve aşağıdaki komutu kullanarak diğer terminalde 'test' olarak adlandırılan işlemleri izlemek.

watch -n1 'ps x -o "%p %r %c" | grep "test" '

Yukarıdaki komut dosyası 4 yeni alt süreç ve ebeveyn oluşturacaktır. Her alt süreç 10 saniye boyunca çalışacaktır. Ancak 5 saniyelik zaman aşımı süresi dolduğunda, ilgili ana süreçler bu çocukları öldürür. Böylece çocuk uygulamayı tamamlayamayacak (10 saniye). Başka bir davranış görmek için bu zamanlamaları (anahtar 10 ve 5) oynayın. Bu durumda, çocuk 10 saniyelik bir zaman aşımına uğramadan önce yürütmeyi 5 saniye içinde tamamlar.

2) Zaman aşımına ulaşıldığında geçerli ana öğeyi izlemeli ve alt işlemi öldürmelidir. Bu, her çocuğu izlemek için ayrı bir ebeveyn oluşturmaz. Ayrıca tüm alt süreçleri aynı ebeveyn içinde düzgün bir şekilde yönetebilirsiniz.

Test.sh dosyasını aşağıdaki gibi oluşturun,

#!/bin/bash

declare -A CPIDs;
declare -a CMDs=("AAA" "BBB" "CCC" "DDD")

CMD_TIME=15;
for CMD in ${CMDs[*]}; do
    (echo "Started..$CMD"; sleep $CMD_TIME; echo "$CMD Done";) &
    CPIDs[$!]="$RN";
    sleep 1;
done

GPID=$(ps -o pgid= $$);
CNT_TIME_OUT=10;
CNT=0;
while (true); do
    declare -A TMP_CPIDs;

    for PID in "${!CPIDs[@]}"; do
        echo "Checking "${CPIDs[$PID]}"=>"$PID;

        if ps -p $PID > /dev/null ; then
          echo "-->"${CPIDs[$PID]}"=>"$PID" is running..";
          TMP_CPIDs[$PID]=${CPIDs[$PID]};
        else
          echo "-->"${CPIDs[$PID]}"=>"$PID" is completed.";
        fi
    done

    if [ ${#TMP_CPIDs[@]} == 0 ]; then
        echo "All commands completed.";
        break;
    else
        unset CPIDs;
        declare -A CPIDs;
        for PID in "${!TMP_CPIDs[@]}"; do
            CPIDs[$PID]=${TMP_CPIDs[$PID]};
        done
        unset TMP_CPIDs;

        if [ $CNT -gt $CNT_TIME_OUT ]; then
            echo ${CPIDs[@]}"PIDs not reponding. Timeout reached $CNT sec. killing all childern with GPID $GPID..";
            kill -- -$GPID;
        fi
    fi

    CNT=$((CNT+1));
    echo "waiting since $b secs..";
    sleep 1;
done

exit;

ve aşağıdaki komutu kullanarak diğer terminalde 'test' olarak adlandırılan işlemleri izlemek.

watch -n1 'ps x -o "%p %r %c" | grep "test" '

Yukarıdaki komut dosyası 4 yeni alt süreç oluşturacaktır. Tüm alt süreçlerin pids'lerini saklıyoruz ve yürütülmelerini tamamlayıp tamamlamadıklarını veya hala çalışıp çalışmadıklarını kontrol etmek için üzerlerine döngü yapıyoruz. Alt işlem CMD_TIME zamanına kadar yürütülür. Ancak CNT_TIME_OUT zaman aşımına ulaşırsa, Tüm çocuklar ebeveyn süreci tarafından öldürülür. Davranışı görmek için zamanlamayı değiştirebilir ve komut dosyasıyla oynayabilirsiniz. Bu yaklaşımın bir dezavantajı, tüm çocuk ağacını öldürmek için grup kimliği kullanmaktır. Ancak ebeveyn sürecinin kendisi aynı gruba aittir, bu yüzden de öldürülür.

Ebeveynin öldürülmesini istemiyorsanız üst sürece başka bir grup kimliği atamanız gerekebilir.

Daha fazla ayrıntıyı burada bulabilirsiniz,

Kabuk betiğinde alt işlemi öldürme


0

Bu komut dosyası da çalışır:

#/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


0

Biliyorum bu eski, ama bulduğum daha iyi bir çözüm:

killtree() { 
    for p in $(pstree -p $1 | grep -o "([[:digit:]]*)" |grep -o "[[:digit:]]*" | tac);do
        echo Terminating: $p 
        kill $p
    done
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.