SIGTERM'i Bash'deki çocuğa ilet


86

Buna benzeyen bir Bash komut dosyası var:

#!/bin/bash
echo "Doing some initial work....";
/bin/start/main/server --nodaemon

Şimdi eğer betiği çalıştıran bash kabuğu bir SIGTERM sinyali alıyorsa, çalışan sunucuya da bir SIGTERM göndermelidir (bloklar, böylece tuzak mümkün değildir). Mümkün mü?

Yanıtlar:


91

Deneyin:

#!/bin/bash 

_term() { 
  echo "Caught SIGTERM signal!" 
  kill -TERM "$child" 2>/dev/null
}

trap _term SIGTERM

echo "Doing some initial work...";
/bin/start/main/server --nodaemon &

child=$! 
wait "$child"

Normalde, bashbir çocuk işlemi yürütülürken herhangi bir sinyali yoksayır. Sunucunun &başlatılması $!, sunucunun PID'sini (kullanılacak waitve ile kullanılacak şekilde kill) tutarak kabuğun iş kontrol sistemine arkaplan edecektir . Arama wait, daha sonra belirtilen PID (sunucu) ile işin bitmesini veya herhangi bir sinyalin atılmasını bekler .

Kabuk aldığında SIGTERM(veya sunucu bağımsız olarak çıktığında), waitçağrı geri döner (sunucunun çıkış koduyla veya bir sinyal alındığında + 128 sinyal numarasıyla). Daha sonra, kabuk SIGTERM aldıysa, _termçıkmadan önce SIGTERM tuzak işleyicisi olarak belirtilen işlevi çağırır (herhangi bir temizleme yaptığımız ve sinyali kullanarak sunucu işlemine manuel olarak geçirdiğimiz kill).


İyi görünüyor! Deneyeceğim ve test ettiğimde cevap vereceğim.
Lorenz

7
Ancak exec, verilen programla kabuğu değiştirir, sonraki waitçağrının neden gerekli olduğu konusunda net değilim ?
iruvar

5
1_CR'nin amacının geçerli olduğunu düşünüyorum. Ya basitçe kullanın exec /bin/start/main/server --nodaemon(bu durumda kabuk işlemi sunucu işlemi ile değiştirilir ve herhangi bir sinyal yaymanız gerekmez) ya da kullanırsınız /bin/start/main/server --nodaemon &, ancak sonra execgerçekten anlamlı değildir.
Andreas Veithen

2
Kabuk betiğinizin yalnızca çocuk sonlandırıldıktan sonra sonlandırılmasını _term()istiyorsanız , işlevde wait "$child"yeniden uygulamanız gerekir . Bu, yeniden başlatılmadan önce kabuk betiğinin ölmesini bekleyen başka bir denetleme sürecine sahipseniz ya da bir EXITmiktar temizleme işlemi yapmak için tuzağa düştüyseniz ve yalnızca alt işlem bittikten sonra çalışmasını gerektiriyorsa, bu gerekli olabilir.
LeoRochael

1
@AlexanderMills Diğer cevapları okuyun. Ya arıyorsun execya da tuzak kurmak istiyorsun .
Stuart P. Bentley

78

Bash, şu anda beklediği süreçlere SIGTERM gibi sinyalleri iletmez. Eğer ederek senaryoyu bitirmek istiyorsanız içine segueing sunucunuza, kullanmak gerekir (doğrudan sunucu başlamıştı sanki, sinyalleri ve başka bir şey işlemek için izin) exec, hangi süreç açılırken ile kabuk değiştirme :

#!/bin/bash
echo "Doing some initial work....";
exec /bin/start/main/server --nodaemon

Eğer bazı nedenlerden dolayı etrafında kabuk tutmak gerekiyorsa bir arada kullanmalıdır (yani. Sunucu sonlandırır sonra biraz temizlik yapmak gerekir) trap, waitve kill. SensorSmith'in cevabına bakınız .


Bu doğru cevap! Çok daha özlü ve OP'nin orjinal sorusuna hitap ediyor
BrDaHa

20

Andreas Veithen , çağrıdan geri dönmeye gerek duymamanız durumunda (OP örneğinde olduğu gibi) yalnızca execkomut aracılığıyla aramanın yeterli olduğunu ( @Stuart P. Bentley'in cevabı ) belirtmektedir . Aksi takdirde, "geleneksel" trap 'kill $CHILDPID' TERM(@ cuonglm'in cevabı) bir başlangıçtır, ancak waitçağrı aslında tuzak işleyicisi çalıştıktan sonra, çocuk işleminden önce hala devam etmeden önce geri döner. Bu nedenle, "ekstra" bir çağrı yapılması waitönerilir ( @ user1463361'in cevabı ).

Bu bir gelişme olsa da, yine de bir yarış koşulu vardır ki bu, işlemin asla çıkamayacağı anlamına gelir (sinyalleyici TERM sinyali göndermeyi denemezse). Güvenlik açığı penceresi, tuzak işleyiciyi kaydetme ile çocuğun PID'sini kaydetme arasındadır.

Aşağıdaki, bu güvenlik açığını ortadan kaldırır (yeniden kullanım işlevlerinde paketlenmiştir).

prep_term()
{
    unset term_child_pid
    unset term_kill_needed
    trap 'handle_term' TERM INT
}

handle_term()
{
    if [ "${term_child_pid}" ]; then
        kill -TERM "${term_child_pid}" 2>/dev/null
    else
        term_kill_needed="yes"
    fi
}

wait_term()
{
    term_child_pid=$!
    if [ "${term_kill_needed}" ]; then
        kill -TERM "${term_child_pid}" 2>/dev/null 
    fi
    wait ${term_child_pid}
    trap - TERM INT
    wait ${term_child_pid}
}

# EXAMPLE USAGE
prep_term
/bin/something &
wait_term

2
Mükemmel iş - Cevabımdaki bağlantıyı buraya işaret edecek şekilde güncelledim (bunun üzerine daha kapsamlı bir çözüm olarak, StackExchange Kullanıcı Arabirimi'nin betiği düzeltmek için cuonglm'in cevabında bana kredisiz kaldığı için hala biraz sinirleniyorum) aslında ne gerekiyorsa onu yap ve OP'den sonra açıklayıcı bir metnin hemen hemen hepsini yazmadı .
Stuart P. Bentley

2
@ StuartP.Bentley, teşekkürler. Ben idi bu iki (kabul edilmez) cevapları ve harici bir referans gerekli montaj sürpriz, sonra yarış durumu aşağı koşmak zorunda kaldı. Referanslarımı ne kadar küçük ek şeref verebileceğim olarak linklere yükselteceğim.
SensorSmith 30:18

3

Sağlanan çözüm benim için işe yaramıyor, çünkü bekleme komutu gerçekten bitmeden işlem öldürüldü. O makale buldum http://veithen.github.io/2014/11/16/sigterm-propagation.html , uygulamanın benim durumumda son pasajı çalışma İyi bir özel sh koşucu ile OpenShift başladı. Sh betiği gereklidir, çünkü Java işleminin PID'si 1 ise imkansız olan konu dökümlerini almak için bir yeteneğe ihtiyacım var.

trap 'kill -TERM $PID' TERM INT
$JAVA_EXECUTABLE $JAVA_ARGS &
PID=$!
wait $PID
trap - TERM INT
wait $PID
EXIT_STATUS=$?
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.