Hoe Linux-signalen in Bash-scripts te gebruiken

fatmawati achmad zaenuri/Shutterstock.com

De Linux-kernel stuurt signalen naar processen over gebeurtenissen waarop ze moeten reageren. Goed opgevoede scripts verwerken signalen elegant en robuust en kunnen achter zichzelf opruimen, zelfs als je op Ctrl+C drukt. Hier is hoe.

Signalen en processen

Signalen zijn korte, snelle eenrichtingsberichten die worden verzonden naar processen zoals scripts, programma’s en daemons. Ze laten het proces weten over iets dat is gebeurd. De gebruiker heeft mogelijk op Ctrl+C gedrukt of de toepassing heeft geprobeerd naar het geheugen te schrijven waartoe het geen toegang heeft.

Als de auteur van het proces heeft geanticipeerd dat er een bepaald signaal naartoe zou kunnen worden gestuurd, kunnen ze een routine in het programma of script schrijven om dat signaal af te handelen. Zo’n routine heet a signaal handler. Het vangt of vangt het signaal op en voert een actie uit als reactie daarop.

Linux gebruikt veel signalen, zoals we zullen zien, maar vanuit het oogpunt van scripting is er slechts een kleine subset van signalen waarin je waarschijnlijk geïnteresseerd bent. Met name in niet-triviale scripts moeten signalen die het script vertellen af ​​te sluiten (waar mogelijk) worden opgevangen (waar mogelijk) en moet een sierlijke afsluiting worden uitgevoerd.

Scripts die tijdelijke bestanden maken of firewallpoorten openen, kunnen bijvoorbeeld de kans krijgen om de tijdelijke bestanden te verwijderen of de poorten te sluiten voordat ze worden afgesloten. Als het script gewoon sterft op het moment dat het het signaal ontvangt, kan uw computer in een onvoorspelbare staat worden achtergelaten.

Hier leest u hoe u signalen in uw eigen scripts kunt verwerken.

Maak kennis met de signalen

Sommige Linux-commando’s hebben cryptische namen. Niet zo het commando dat signalen opvangt. Het heet trap. We kunnen ook gebruik maken van trap met de -l (lijst) optie om ons de volledige lijst met signalen te tonen die Linux gebruikt.

trap -l

De signalen in Ubuntu weergeven met trap -l

Hoewel onze genummerde lijst eindigt op 64, zijn er eigenlijk 62 signalen. Seinen 32 en 33 ontbreken. Ze zijn niet geïmplementeerd in Linux. Ze zijn vervangen door banen in de gcc compiler voor het afhandelen van realtime threads. Alles van sein 34, SIGRTMINom 64 te signaleren, SIGRTMAXzijn realtime signalen.

U ziet verschillende lijsten op verschillende Unix-achtige besturingssystemen. Op OpenIndiana zijn bijvoorbeeld de signalen 32 en 33 aanwezig, samen met een heleboel extra signalen die het totaal op 73 brengen.

De signalen in OpenIndiana weergeven met trap -l

Er kan naar signalen worden verwezen met naam, nummer of verkorte naam. Hun verkorte naam is gewoon hun naam met de leidende “SIG” verwijderd.

Signalen worden om veel verschillende redenen opgewekt. Als je ze kunt ontcijferen, staat hun doel in hun naam. De impact van een signaal valt in een van de volgende categorieën:

  • Beëindigen: Het proces wordt beëindigd.
  • Negeren: Het signaal heeft geen invloed op het proces. Dit is een informatief signaal.
  • Kern: Er wordt een dump-core-bestand gemaakt. Dit wordt meestal gedaan omdat het proces op de een of andere manier is overtreden, zoals een geheugenschending.
  • Hou op: Het proces wordt gestopt. Dat wil zeggen, het is gepauzeerdniet beëindigd.
  • Doorgaan: Vertelt een gestopt proces om de uitvoering voort te zetten.

Dit zijn de signalen die u het vaakst zult tegenkomen.

  • SIGHUP: Signaal 1. De verbinding met een externe host, zoals een SSH-server, is onverwachts verbroken of de gebruiker is uitgelogd. Een script dat dit signaal ontvangt, kan netjes worden beëindigd of ervoor kiezen om opnieuw verbinding te maken met de externe host.
  • SIGINT: Signaal 2. De gebruiker heeft de Ctrl+C-combinatie ingedrukt om een ​​proces te forceren om te sluiten, of de kill commando is gebruikt met signaal 2. Technisch gezien is dit een onderbrekingssignaal, geen beëindigingssignaal, maar een onderbroken script zonder signaalhandler zal meestal eindigen.
  • SIGQUIT: Signaal 3. De gebruiker heeft op de combinatie Ctrl+D gedrukt om een ​​proces te forceren te stoppen, of de kill commando is gebruikt met signaal 3.
  • SIGFPE: Signaal 8. Het proces probeerde een illegale (onmogelijke) wiskundige bewerking uit te voeren, zoals delen door nul.
  • SIGKILL: Signaal 9. Dit is het equivalent van een guillotine. Je kunt het niet vangen of negeren, en het gebeurt onmiddellijk. Het proces wordt onmiddellijk beëindigd.
  • SIGTERME: Signaal 15. Dit is de meer attente versie van SIGKILL. SIGTERM Vertelt ook dat een proces moet worden beëindigd, maar het kan worden opgesloten en het proces kan zijn opschoningsprocessen uitvoeren voordat het wordt afgesloten. Dit maakt een gracieus afsluiten mogelijk. Dit is het standaardsignaal dat wordt opgewekt door de kill opdracht.

Signalen op de opdrachtregel

Een manier om een ​​signaal op te vangen is door gebruik te maken van trap met het nummer of de naam van het signaal en een reactie die u wilt laten gebeuren als het signaal wordt ontvangen. We kunnen dit demonstreren in een terminalvenster.

Dit commando vangt de SIGINT signaal. Het antwoord is om een ​​regel tekst af te drukken naar het terminalvenster. We gebruiken de -e (enable escapes) optie met echo zodat we de “nformaat specificatie.

trap 'echo -e "+c Detected."' SIGINT

Ctrl+C vastzetten op de opdrachtregel

Elke keer dat we op de Ctrl+C-combinatie drukken, wordt onze tekstregel afgedrukt.

Om te zien of er een val op een signaal is gezet, gebruikt u de -p (afdrukval) optie.

trap -p SIGINT

Controleren of er een val op een sein is gezet

Gebruik makend van trap zonder opties doet hetzelfde.

Gebruik een koppelteken om het signaal terug te zetten naar zijn niet-gevangen, normale toestand-” en de naam van het ingesloten signaal.

trap - SIGINT
trap -p SIGINT

Een val uit een signaal verwijderen

Geen uitvoer van de trap -p commando geeft aan dat er geen trap is ingesteld op dat signaal.

Signalen in scripts vangen

We kunnen hetzelfde algemene formaat gebruiken trap commando in een script. Dit script vangt drie verschillende signalen op, SIGINT, SIGQUITen SIGTERM.

#!/bin/bash

trap "echo I was SIGINT terminated; exit" SIGINT
trap "echo I was SIGQUIT terminated; exit" SIGQUIT
trap "echo I was SIGTERM terminated; exit" SIGTERM

echo $$
counter=0

while true
do 
  echo "Loop number:" $((++counter))
  sleep 1
done

De boom trap uitspraken staan ​​bovenaan het script. Merk op dat we de exit commando binnen het antwoord op elk van de signalen. Dit betekent dat het script op het signaal reageert en vervolgens afsluit.

Kopieer de tekst naar je editor en sla het op in een bestand met de naam “simple-loop.sh”, en maak het uitvoerbaar met behulp van de chmod opdracht. U moet dat met alle scripts in dit artikel doen als u het op uw eigen computer wilt volgen. Gebruik in elk geval gewoon de naam van het juiste script.

chmod +x simple-loop.sh

Een script uitvoerbaar maken met chmod

De rest van het script is heel eenvoudig. We moeten de proces-ID van het script weten, dus we hebben het script voor ons. De $$ variabele bevat de proces-ID van het script.

We maken een variabele genaamd counter en zet deze op nul.

De while loop zal voor altijd worden uitgevoerd, tenzij het met geweld wordt gestopt. Het verhoogt de counter variabele, echoot het naar het scherm en slaapt even.

Laten we het script uitvoeren en er verschillende signalen naar sturen.

./simple-loop.sh

Een script dat het identificeert, is beëindigd met Ctrl+C

Wanneer we op “Ctrl + C” drukken, wordt ons bericht afgedrukt naar het terminalvenster en wordt het script beëindigd.

Laten we het opnieuw uitvoeren en de . verzenden SIGQUIT signaal met behulp van de kill opdracht. We moeten dat doen vanuit een ander terminalvenster. U moet de proces-ID gebruiken die door uw eigen script is gerapporteerd.

./simple-loop.sh
kill -SIGQUIT 4575

Een script dat het identificeert, is beëindigd met SIGQUIT

Zoals verwacht meldt het script dat het signaal arriveert en eindigt. En tot slot, om het punt te bewijzen, doen we het nog een keer met de SIGTERM signaal.

./simple-loop.sh
kill -SIGTERM 4584

Een script dat het identificeert is beëindigd met SIGTERM

We hebben geverifieerd dat we meerdere signalen in een script kunnen vangen en op elk afzonderlijk kunnen reageren. De stap die dit alles van interessant naar nuttig bevordert, is het toevoegen van signaalbehandelaars.

Omgaan met signalen in scripts

We kunnen de antwoordreeks vervangen door de naam van een functie in uw script. De trap commando roept vervolgens die functie aan wanneer het signaal wordt gedetecteerd.

Kopieer deze tekst naar een editor en sla het op als een bestand met de naam “grace.sh”, en maak het uitvoerbaar met chmod.

#!/bin/bash

trap graceful_shutdown SIGINT SIGQUIT SIGTERM

graceful_shutdown()
{
  echo -e "nRemoving temporary file:" $temp_file
  rm -rf "$temp_file"
  exit
}

temp_file=$(mktemp -p /tmp tmp.XXXXXXXXXX)
echo "Created temp file:" $temp_file

counter=0

while true
do 
  echo "Loop number:" $((++counter))
  sleep 1
done

Het script zet een val voor drie verschillende signalen: SIGHUP, SIGINTen SIGTERM—met behulp van een enkele trap uitspraak. Het antwoord is de naam van de graceful_shutdown() functie. De functie wordt aangeroepen wanneer een van de drie ingesloten signalen wordt ontvangen.

Het script maakt een tijdelijk bestand aan in de map “/tmp”, met behulp van mktemp. De bestandsnaamsjabloon is “tmp.XXXXXXXXXX”, dus de naam van het bestand zal “tmp” zijn. gevolgd door tien willekeurige alfanumerieke tekens. De naam van het bestand wordt op het scherm weergegeven.

De rest van het script is hetzelfde als het vorige, met a counter variabel en een oneindig while lus.

./grace.sh

Een script dat een gracieus afsluiten uitvoert door een tijdelijk bestand te verwijderen

Wanneer het bestand een signaal krijgt waardoor het wordt gesloten, wordt de graceful_shutdown() functie wordt aangeroepen. Hiermee wordt ons enkele tijdelijke bestand verwijderd. In een echte situatie kan het elke opschoning uitvoeren die uw script vereist.

Ook hebben we al onze gevangen signalen gebundeld en met één enkele functie behandeld. U kunt signalen afzonderlijk opvangen en naar hun eigen speciale handlerfuncties sturen.

Kopieer deze tekst en sla het op in een bestand met de naam “triple.sh”, en maak het uitvoerbaar met de chmod opdracht.

#!/bin/bash

trap sigint_handler SIGINT
trap sigusr1_handler SIGUSR1
trap exit_handler EXIT

function sigint_handler() {
  ((++sigint_count))

  echo -e "nSIGINT received $sigint_count time(s)."

  if [[ "$sigint_count" -eq 3 ]]; then
    echo "Starting close-down."
    loop_flag=1
  fi
}

function sigusr1_handler() {
  echo "SIGUSR1 sent and received $((++sigusr1_count)) time(s)."
}

function exit_handler() { 
  echo "Exit handler: Script is closing down..."
}

echo $$
sigusr1_count=0
sigint_count=0
loop_flag=0

while [[ $loop_flag -eq 0 ]]; do
  kill -SIGUSR1 $$
  sleep 1
done

We definiëren drie vallen bovenaan het script.

  • een vallen SIGINT en heeft een handler genaamd sigint_handler().
  • De tweede vangt een signaal genaamd SIGUSR1 en gebruikt een handler genaamd sigusr1_handler() .
  • Val nummer drie vangt de EXIT signaal. Dit signaal wordt door het script zelf afgegeven wanneer het wordt gesloten. Een signaalhandler instellen voor: EXIT betekent dat je een functie kunt instellen die altijd wordt aangeroepen wanneer het script eindigt (tenzij het wordt gedood met een signaal) SIGKILL). Onze handler heet exit_handler() .

SIGUSR1 en SIGUSR2 zijn signalen die worden verstrekt zodat u aangepaste signalen naar uw scripts kunt sturen. Hoe u ze interpreteert en erop reageert, is geheel aan u.

Als we de signaalbehandelaars voor nu terzijde laten, zou de hoofdtekst van het script u bekend moeten zijn. Het echoot de proces-ID naar het terminalvenster en maakt enkele variabelen aan. Variabele sigusr1_count registreert het aantal keren SIGUSR1 werd behandeld, en sigint_count registreert het aantal keren SIGINT werd behandeld. De loop_flag variabele wordt op nul gezet.

De while lus is geen oneindige lus. Het stopt met lussen als de loop_flag variabele is ingesteld op een waarde die niet nul is. Elke draai van de while lus gebruikt kill om de te sturen SIGUSR1 signaal naar dit script, door het naar de proces-ID van het script te sturen. Scripts kunnen signalen naar zichzelf sturen!

De sigusr1_handler() functie verhoogt de sigusr1_count variabele en stuurt een bericht naar het terminalvenster.

Elke keer dat de SIGINT signaal wordt ontvangen, de siguint_handler() functie verhoogt de sigint_count variabele en echoot de waarde ervan naar het terminalvenster.

Als de sigint_count variabele is gelijk aan drie, de loop_flag variabele is ingesteld op één en er wordt een bericht verzonden naar het terminalvenster om de gebruiker te laten weten dat het afsluitproces is gestart.

Omdat loop_flag is niet langer gelijk aan nul, de while lus wordt beëindigd en het script is voltooid. Maar die actie verhoogt automatisch de EXIT signaal en de exit_handler() functie wordt aangeroepen.

./triple.sh

Een script dat SIGUSR1 gebruikt, waarbij drie Ctrl+C-combinaties nodig zijn om te sluiten, en het EXIT-signaal opvangt bij afsluiten

Na drie keer drukken op Ctrl+C wordt het script beëindigd en wordt automatisch de exit_handler() functie.

Lees de signalen

Door signalen op te vangen en ermee om te gaan in eenvoudige handlerfuncties, kunt u uw Bash-scripts achter zichzelf laten opruimen, zelfs als ze onverwacht worden beëindigd. Dat geeft je een schoner bestandssysteem. Het voorkomt ook instabiliteit de volgende keer dat u het script uitvoert, en – afhankelijk van het doel van uw script – kan het zelfs beveiligingslekken voorkomen.

VERWANT: Hoe u de beveiliging van uw Linux-systeem kunt controleren met Lynis

Leave a Comment