Biorąc pod uwagę proces powłoki (np. sh
) I jego proces potomny (np. cat
), W jaki sposób mogę zasymulować zachowanie Ctrl+ Cprzy użyciu identyfikatora procesu powłoki?
Oto, co próbowałem:
Uruchamianie, sh
a następnie cat
:
[user@host ~]$ sh
sh-4.3$ cat
test
test
Przesyłanie SIGINT
do cat
z innego terminala:
[user@host ~]$ kill -SIGINT $PID_OF_CAT
cat
odebrał sygnał i zakończył (zgodnie z oczekiwaniami).
Wysyłanie sygnału do procesu nadrzędnego nie działa. Dlaczego sygnał nie jest propagowany cat
po wysłaniu do procesu nadrzędnego sh
?
To nie działa:
[user@host ~]$ kill -SIGINT $PID_OF_SH
Odpowiedzi:
Jak działa CTRL+C
Pierwszą rzeczą jest zrozumienie, jak działa CTRL+ C.
Po naciśnięciu przycisku CTRL+ Cemulator terminala wysyła znak ETX (koniec tekstu / 0x03).
TTY jest skonfigurowany w taki sposób, że po otrzymaniu tego znaku wysyła SIGINT do grupy procesów pierwszego planu terminala. Tę konfigurację można wyświetlić, robiąc
stty
i patrzącintr = ^C;
. Specyfikacja POSIX mówi, że po otrzymaniu INTR powinien wysłać SIGINT do grupy procesów pierwszego planu tego terminala.Jaka jest grupa procesów pierwszego planu?
Więc teraz pytanie brzmi, jak określić, jaka jest grupa procesów pierwszego planu? Pierwszą grupą procesów jest po prostu grupa procesów, które będą odbierać wszelkie sygnały generowane przez klawiaturę (SIGTSTOP, SIGINT itp.).
Najprostszym sposobem ustalenia identyfikatora grupy procesów jest użycie
ps
:Druga kolumna będzie identyfikatorem grupy procesów.
Jak wysłać sygnał do grupy procesów?
Teraz, gdy wiemy, jaki jest identyfikator grupy procesów, musimy zasymulować zachowanie POSIX wysyłania sygnału do całej grupy.
Można to zrobić
kill
, umieszczając-
przed identyfikatorem grupy.Na przykład jeśli identyfikator grupy procesów to 1234, możesz użyć:
Symuluj CTRL+, Cużywając numeru terminalu.
Tak więc powyższe obejmuje symulację CTRL+ Cjako proces ręczny. Ale co, jeśli znasz numer TTY i chcesz symulować CTRL+ Cdla tego terminala?
To staje się bardzo łatwe.
Załóżmy, że
$tty
jest to terminal, na który chcesz celować (możesz to uzyskać, uruchamiając gotty | sed 's#^/dev/##'
w terminalu).Spowoduje to wysłanie SIGINT do dowolnej grupy procesów pierwszego planu
$tty
.źródło
kill
polecenie może się nie powieść.sends a SIGINT to the foreground process group of the terminal.
fork
. Minimalny możliwy do uruchomieniaJak mówi vinc17, nie ma powodu do tego. Po wpisaniu sekwencji klawiszy generujących sygnał (np. Ctrl+ C) Sygnał jest wysyłany do wszystkich procesów, które są podłączone do terminala (powiązane z nim). Nie ma takiego mechanizmu dla sygnałów generowanych przez
kill
.Jednak polecenie takie jak
wyśle sygnał do wszystkich procesów w grupie procesów 12345; patrz kill (1) i kill (2) . Elementy potomne powłoki zwykle znajdują się w grupie procesów powłoki (przynajmniej jeśli nie są asynchroniczne), więc wysyłanie sygnału do ujemnego PID powłoki może zrobić, co chcesz.
Ups
Jak wskazuje vinc17, nie działa to dla interaktywnych powłok. Oto alternatywa, która może działać:
ps -pPID_of_shell
pobiera informacje o procesie dotyczące powłoki.o tpgid=
mówi,ps
aby wyświetlać tylko identyfikator grupy procesów terminalowych, bez nagłówka. Jeśli jest to mniej niż 10000,ps
wyświetli go z wiodącymi spacjami;$(echo …)
to szybkie sztuczka zdejmować wiodącym (i końcowe) przestrzenie.Dostałem to do pracy w pobieżnych testach na maszynie Debiana.
źródło
Pytanie zawiera własną odpowiedź. Wysłanie
SIGINT
docat
procesu za pomocąkill
jest idealną symulacją tego, co dzieje się po naciśnięciu^C
.Mówiąc dokładniej, znak przerwania (
^C
domyślnie) wysyłaSIGINT
do każdego procesu w grupie procesów pierwszego planu terminala. Jeśli zamiastcat
wykonywania bardziej skomplikowanego polecenia obejmującego wiele procesów, musisz zabić grupę procesów, aby uzyskać taki sam efekt jak^C
.Po uruchomieniu dowolnego polecenia zewnętrznego bez
&
operatora tła powłoka tworzy nową grupę procesów dla polecenia i powiadamia terminal, że ta grupa procesów jest teraz na pierwszym planie. Powłoka nadal znajduje się we własnej grupie procesów, która nie jest już na pierwszym planie. Następnie powłoka czeka na zakończenie polecenia.To tam wydaje się, że padłeś ofiarą powszechnego nieporozumienia: idea, że powłoka robi coś, aby ułatwić interakcję między procesami potomnymi i terminalem. To po prostu nieprawda. Po zakończeniu prac instalacyjnych (tworzenie procesu, ustawienie trybu terminalowego, tworzenie potoków i przekierowywanie innych deskryptorów plików oraz uruchamianie programu docelowego) powłoka po prostu czeka . To, co wpisujesz,
cat
nie przechodzi przez powłokę, niezależnie od tego, czy jest to normalne wejście, czy znak specjalny generujący sygnał^C
.cat
Proces ma bezpośredni dostęp do terminala za pośrednictwem własnych deskryptorów plików, a terminal ma możliwość wysyłania sygnałów bezpośrednio docat
procesu, ponieważ znajduje się na pierwszym planie grupa proces.Po zakończeniu
cat
procesu powłoka zostanie powiadomiona, ponieważ jest rodzicemcat
procesu. Następnie powłoka staje się aktywna i ponownie stawia się na pierwszym planie.Oto ćwiczenie, które zwiększy twoje zrozumienie.
W wierszu poleceń powłoki w nowym terminalu uruchom następujące polecenie:
Słowo
exec
kluczowe powoduje wykonanie powłokicat
bez tworzenia procesu potomnego. Powłoka jest zastąpiona przezcat
. PID, który wcześniej należał do powłoki, teraz jest PIDcat
. Sprawdź to za pomocąps
w innym terminalu. Wpisz kilka losowych linii i zobacz, żecat
powtarza je z powrotem do Ciebie, co dowodzi, że nadal zachowuje się normalnie, mimo że nie ma procesu powłoki jako elementu nadrzędnego. Co się stanie, gdy^C
teraz naciśniesz ?Odpowiedź:
źródło
exec cat
wciśnięciu^C
nie wylądowałby tylko^C
w kocie. Dlaczego miałby to zakończyć ten,cat
który teraz zastąpił powłokę? Ponieważ powłoka została zastąpiona, powłoka realizuje logikę wysyłania SIGINT do swoich dzieci po otrzymaniu^C
.Nie ma powodu, aby propagować
SIGINT
to dziecku. Ponadtosystem()
specyfikacja POSIX mówi: „Funkcja system () powinna ignorować sygnały SIGINT i SIGQUIT i blokować sygnał SIGCHLD, czekając na zakończenie polecenia”.Jeśli powłoka propaguje odebrane
SIGINT
, np. Po prawdziwym Ctrl-C, oznacza to, że proces potomny odbierzeSIGINT
sygnał dwukrotnie, co może mieć niepożądane zachowanie.źródło
system()
. Ale masz rację, jeśli złapie sygnał (oczywiście, że tak), nie ma powodu, aby propagować go w dół.setpgid
Przykład minimalnej grupy procesów POSIX C.Łatwiej jest to zrozumieć przy minimalnym uruchamialnym przykładzie bazowego API.
To ilustruje, w jaki sposób sygnał jest wysyłany do dziecka, jeśli dziecko nie zmieniło grupy procesów za pomocą
setpgid
.main.c
GitHub w górę .
Połącz z:
Uruchom bez
setpgid
Bez argumentów CLI
setpgid
nie jest wykonywane:Możliwy wynik:
i program się zawiesza.
Jak widzimy, pgid obu procesów jest taki sam, ponieważ jest dziedziczony
fork
.Następnie za każdym razem, gdy klikniesz:
Wyprowadza ponownie:
To pokazuje, jak:
kill(-pgid, SIGINT)
Wyjdź z programu, wysyłając inny sygnał do obu procesów, np. SIGQUIT za pomocą
Ctrl + \
.Biegnij z
setpgid
Jeśli uruchamiasz z argumentem, np .:
wtedy dziecko zmienia swój pgid, a teraz tylko jeden podpis jest drukowany za każdym razem tylko od rodzica:
A teraz, kiedy uderzysz:
tylko rodzic odbiera również sygnał:
Nadal możesz zabić rodzica za pomocą SIGQUIT:
jednak dziecko ma teraz inny PGID i nie odbiera tego sygnału! Można to zobaczyć z:
Będziesz musiał zabić go jawnie za pomocą:
Wyjaśnia to, dlaczego istnieją grupy sygnałów: w przeciwnym razie cały szereg procesów pozostałoby do ręcznego czyszczenia przez cały czas.
Testowane na Ubuntu 18.04.
źródło