termA nezamrzne. Pouze zobrazuje zpětné volání ve vstupním poli. Jednoduše stiskněte Enter
pokračujte v zadávání.
Než vysvětlíte svůj problém, trochu kontextu o tom, jak read
příkaz funguje. Načítá vstupní data z stdin
do EOF
se narazí. Je bezpečné vyslovit volání na read
příkaz je neblokující, pokud jde o čtení ze souborů na disku. Ale když stdin
je připojen k terminálu, příkaz se zablokuje, dokud uživatel něco nenapíše.
Jak obslužné programy signálů fungují?
Jednoduché vysvětlení, jak funguje zpracování signálu. Viz níže uvedený úryvek v C
který funguje pouze na SIGINT
(také znám jako CTRL+C
)
#include <stdio.h>
#include <signal.h>
/* signal handler definition */
void signal_handler(int signum){
printf("Hello World!\n");
}
int main(){
//Handle SIGINT with a signal handler
signal(SIGINT, signal_handler);
//loop forever!
while(1);
}
Zaregistruje obsluhu signálu a poté vstoupí do nekonečné smyčky. Když narazíme na Ctrl-C
, všichni se shodneme na tom, že obsluha signálu signal_handler()
by se měl spustit a "Hello World!"
vytiskne na obrazovku, ale program byl v nekonečné smyčce. Chcete-li vytisknout "Hello World!"
muselo to být tak, že to přerušilo smyčku pro spuštění obsluhy signálu, že? Mělo by tedy opustit smyčku i program. Podívejme se:
gcc -Wall -o sighdl.o signal.c
./sighdl.o
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
Jak ukazuje výstup, pokaždé jsme vydali Ctrl-C
, "Hello World!"
vytiskne, ale program se vrátí do nekonečné smyčky. Je to až po vydání SIGQUIT
signál s Ctrl-\
program skutečně skončil. V závislosti na vašem ulimit
nastavení, vypíše jádro nebo vytiskne číslo přijatého signálu.
Zatímco interpretace, že smyčka skončí, je rozumná, nezohledňuje primární důvod pro zpracování signálu, tedy asynchronní zpracování událostí. To znamená, že obsluha signálu jedná mimo standardní tok ovládání programu; ve skutečnosti je celý program uložen v kontextu a nový kontext je vytvořen pouze pro zpracování signálu. Jakmile obslužný program signálu dokončí své akce, kontext se přepne zpět a spustí se normální tok provádění (tj. while(1)
).
Chcete-li odpovědět na vaše otázky,
Závěr potvrdil, že když bash provádí externí příkaz v popředí, nezpracovává žádné přijaté signály, dokud se proces na popředí neskončí
Klíčová věc, kterou je zde třeba poznamenat, je externí velitelská část. V prvním případě, kde sleep
je externí proces, ale ve druhém případě read
je vestavěný ze samotného shellu. Takže šíření signálu do těchto dvou se v obou těchto případech liší
type read
read is a shell builtin
type sleep
sleep is /usr/bin/sleep
1. Otevřete terminál s názvem termA a spusťte vytvořený soubor callback.sh s /bin/bash callback.sh poprvé, informace se okamžitě objeví.
Ano, toto chování se očekává. Protože v tuto chvíli je definována pouze funkce a handler pasti je registrován k funkci myCallback
a signál ještě není přijat ve skriptu. Jak postupuje provádění, zpráva z read
výzva je vyvolána poprvé.
2. Otevřete nový terminál s názvem termB a spusťte
pkill -USR1 -f callback.sh
poprvé se informace okamžitě objeví v termA
Ano, zatímco read
příkaz čeká na řetězec následovaný Enter stisknutí klávesy, které signalizuje EOF
, přijme signál SIGUSR1
z druhého terminálu se uloží aktuální kontext provádění a řízení se přepne na obsluhu signálu, která vytiskne řetězec s aktuálním datem.
Jakmile obslužná rutina dokončí provádění, kontext se vrátí na while
smyčka, ve které je read
příkaz stále čeká na vstupní řetězec. Až do read
příkaz je úspěšný, všechny následující depeše signálu by pouze vytiskly řetězec uvnitř obslužného programu signálu.
Pokračujte v termB, spusťte
pkill -USR1 -f callback.sh
podruhé.
Stejně jako dříve, read
příkaz není ve vaší smyčce while dokončen pro jednou, pouze pokud úspěšně načte řetězec, spustí se další iterace smyčky a vyvolá se nová výzva.
Zdroj obrázku:The Linux Programming Interface od Michaela KerrisK
Závěr potvrdil, že když bash provádí externí příkaz v popředí, nezpracovává žádné přijaté signály, dokud proces na popředí neskončí.
bash
dělá zpracovávat signály na C
/ úroveň implementace, ale nespustí obslužné rutiny nastavené s trap
dokud proces popředí neskončí. To je vyžadováno standardem:
When a signal for which a trap has been set is received while the shell is waiting for the completion of a utility executing a foreground command, the trap associated with that signal shall not be executed until after the foreground command has completed.
Všimněte si, že standard nerozlišuje mezi vestavěnými a externími příkazy; v případě "utility" jako read
, který není spouštěn v samostatném procesu, není zřejmé, zda se signál vyskytuje v jeho "kontextu" nebo v kontextu hlavního shellu a zda je handler nastaven na trap
by měl být spuštěn před nebo za read
vrátí.
problém 1:callback.sh obsahuje nekonečnou smyčku while, jak vysvětlit, že nezpracovává žádné přijaté signály, dokud proces na popředí neskončí., v tomto případě proces na popředí nikdy neskončí.
To je špatně. bash
neprovádí while
smyčka v samostatném procesu. Protože zde není žádný proces na popředí bash
čeká, může spustit jakýkoli obslužný program nastavený s trap
ihned. Pokud read
byly externím příkazem, nikoli while
by byl proces v popředí.
problém 2:Ne
please input something for foo: shown
v termínu A;přejděte na termB, spusťte
pkill -USR1 -f callback.sh
potřetí.V termA se znovu zobrazí následující informace:
callback function called at Mon Nov 19 09:07:24 HKT 2018
Stále žádné
please input something for foo:
zobrazeno v termA. Proč informaceplease input something for foo:
zmrznout?
Nezamrzá. Stačí stisknout Enter a znovu se zobrazí.
Je to prostě tak
a) bash
restartuje read
zabudované na místě když je přerušen signálem -- nevrátí se a neprojde znovu přes while
smyčka
b) nebude znovu zobrazovat výzvu nastavenou na -p
v tom případě.
Všimněte si, že bash
ani znovu nezobrazí výzvu v případě SIGINT
z klávesnice bylo zpracováno, i když zahodí řetězec přečtený tak daleko od uživatele:
$ cat goo
trap 'echo INT' INT
echo $$
read -p 'enter something: ' var
echo "you entered '$var'"
$ bash goo
24035
enter something: foo<Ctrl-C>^CINT
<Ctrl-C>^CINT
<Ctrl-C>^CINT
bar<Enter>
you entered 'bar'
Tím se vytiskne you entered 'foobar'
pokud byl signál odeslán z jiného okna s kill -INT <pid>
místo pomocí Ctrl-C z terminálu.
Všechny věci v této poslední části (jak read
je přerušeno atd.) je velmi bash
charakteristický. V jiných shellech jako ksh
nebo dash
a dokonce i v bash
při spuštění v režimu POSIX (bash --posix
), jakýkoli obsluhovaný signál ve skutečnosti přeruší read
vestavěný. Ve výše uvedeném příkladu shell vypíše you entered ''
a ukončete po prvním ^C, a pokud read
je volána ze smyčky, smyčka bude restartována.