Ctrl + C ile hangi işlemin iptal edileceğini kontrol edin


13

Linux'a önyükleme yapan ve küçük bir Bash betiği çalıştıran canlı bir CD'm var. Komut dosyası ikinci bir programı arar ve çalıştırır (genellikle derlenmiş bir C ++ ikili dosyasıdır).

Ctrl+ Tuşuna basarak ikinci programı iptal edebilmeniz gerekir C. Ne gerektiğini gerçekleştiğini ikinci program durur ve Bash komut temizleme çalışmaya devam eder. Ne aslında olur ana uygulama ve Bash komut hem sonlandırmak olmasıdır. Bu bir problem.

Bu yüzden trapyerleşik olana Bash'e SIGINT'i yoksaymasını söyledim . Ve şimdi Ctrl+ CC ++ uygulamasını sonlandırır, ancak Bash çalışmaya devam eder. Harika.

Oh evet ... Bazen "ikinci uygulama" başka bir Bash betiğidir. Ve bu durumda, Ctrl+ Cşimdi hiçbir şey yapmıyor .

Açıkçası bu işlerin nasıl çalıştığına dair anlayışım yanlış ... Kullanıcı Ctrl+ 'ya bastığında hangi işlemin SIGINT'i alacağını nasıl kontrol ederim C? Bu sinyali sadece belirli bir sürece yönlendirmek istiyorum .

Yanıtlar:


11

İnternetin yüzünü aradıktan saatler sürdükten sonra cevabı buldum.

  1. Linux bir süreç grubu kavramına sahiptir .

  2. TTY sürücüsünün Ön Plan İşlem Grubu kavramı vardır.

  3. Eğer bastığınızda Ctrl+ C, TTY gönderir SIGINTiçin her süreçte Önalan Süreci Grubunda. (Ayrıca bu blog girişine bakın .)

Derlenmiş ikili hem nedeni budur ve her iki olsun clobbered komut başlattı söyledi. Aslında sadece ana uygulamanın bu sinyali almasını istiyorum , başlangıç ​​komut dosyalarını değil .

Çözüm artık açık: Uygulamayı yeni bir süreç grubuna koymamız ve bu TTY için Ön Plan Süreç Grubu yapmamız gerekiyor. Görünüşe göre bunu yapmak için komut

setsid -c <applcation>

Ve hepsi bu. Artık kullanıcı Ctrl+ tuşuna bastığında C, SIGINT uygulamaya (ve sahip olabileceği tüm çocuklara) gönderilecek ve başka kimseye gönderilmeyecektir. Ben de bunu istedim.

  • setsid tek başına uygulamayı yeni bir süreç grubuna koyar (aslında, görünüşe göre bir grup süreç grubu olan tamamen yeni bir "oturum").

  • -cBayrağın eklenmesi, bu yeni işlem grubunu geçerli TTY için "ön plan" işlem grubu haline getirir. (Yani, + SIGINTtuşuna bastığınızda olur )CtrlC

Bash'in yeni bir süreç grubunda işlemleri ne zaman çalıştırdığı veya çalıştırmadığı hakkında birçok çelişkili bilgi gördüm. (Özellikle, "interaktif" ve "interaktif olmayan" mermiler için farklı gibi görünüyor.) Belki bunu akıllı boru hile ile çalışmak için alabilirsiniz önerileri gördüm ... Bilmiyorum. Ancak yukarıdaki yaklaşım benim için işe yarıyor gibi görünüyor.


5
Neredeyse tamamladınız ... bir komut dosyası çalıştırılırken varsayılan olarak iş kontrolü devre dışıdır, ancak ile etkinleştirebilirsiniz set -m. Her setsidçocuk çalıştırdığınızda kullanmaktan biraz daha temiz ve basittir .
psusi

@psusi Tavsiye için teşekkürler! Sadece bir çocuğum var, bu yüzden önemli değil. Şimdi Bash kılavuzuna nereden bakacağımı biliyorum ...
MathematicalOrchid

İronik olarak, ebeveynin sigint'i yakalamasını istediğim ters problemim var, ancak "akıllı boru hilesi" yüzünden değil. Ayrıca ilerleme grubu -> süreç grubu
Andrew Domaszek

2

F01'e yapılan yorumda belirttiğim gibi, SIGTERM'i alt sürece göndermelisiniz. Burada ^ C'yi yakalamayı ve bir alt sürece sinyal göndermeyi gösteren birkaç komut dosyası vardır.

İlk olarak, ebeveyn.

traptest

#!/bin/bash

# trap test
# Written by PM 2Ring 2014.10.23

myname=$(basename "$0")
child=sleeploop

set_trap()
{
    sig=$1
    msg="echo -e \"\n$myname received ^C, sending $sig to $child, $pid\""
    trap "$msg; kill -s $sig $pid" SIGINT
}
trap "echo \"bye from $myname\"" EXIT

echo "running $child..."
./$child 5  &
pid=$!

# set_trap SIGINT
set_trap SIGTERM
echo "$child pid = $pid"

wait $pid
echo "$myname finished waiting"

Ve şimdi, çocuk.

sleeploop

#!/bin/bash

# child script for traptest
# Written by PM 2Ring 2014.10.23

myname=$(basename "$0")
delay="$1"

set_trap()
{
    sig=$1
    trap "echo -e '\n$myname received $sig signal';exit 0" $sig
}

trap "echo \"bye from $myname\"" EXIT
set_trap SIGTERM
set_trap SIGINT

#Select sleep mode
if false
then
    echo "Using foreground sleep"
    Sleep()
    {
        sleep $delay
    }
else
    echo "Using background sleep"
    Sleep()
    {
        sleep "$delay" &
        wait $!
    }
fi

#Time to snooze :)
for ((i=0; i<5; i++));
do
    echo "$i: sleeping for $delay"
    Sleep
done

echo "$myname terminated normally"

Eğer tuzak SIGTERM gönderirse işler güzel davranır, ama tuzak SIGINT'i gönderirse sleeploop asla görmez.

Sleeploop SIGTERM'i yakalar ve uyku modu ön plandaysa, geçerli uykudan uyanana kadar sinyale yanıt veremez. Ancak uyku modu arka plansa, hemen yanıt verir.


Bu harika örnek için teşekkür ederim,
senaryomu

2

Başlangıç ​​betiğinizde.

  • ikinci programın PID'sini takip edin

  • SIGINT'i yakala

  • Bir SIGINT yakaladığında, ikinci program PID'sine SIGINT gönder


1
Bu yardımcı olmayabilir. Ana komut dosyasında SIGINT'i yakalarsanız, üst komut dosyasından veya başka bir kabuktan gönderirseniz, alt komut dosyaları SIGINT'i alamaz. Ama bu göründüğü kadar kötü değil, çünkü görünüşe göre kullanmak için tercih edilen sinyal olan SIGTERM'i çocuğa gönderebilirsiniz.
PM 2Ring

1
SIGTERM neden "tercih edilir"?
Matematiksel

Neredeyse benzerler. SIGINT kontrol terminali / kullanıcısı tarafından gönderilen sinyaldir, örn. Ctrl + C. SIGTERM, sürecin sona ermesini istiyorsanız gönderebileceğiniz şeydir. Burada daha fazla düşünce en.wikipedia.org/wiki/Unix_signal
f01
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.