Signaaleja lähetetään myös muissa kuin virhetilanteissa. Jos terminaalissa painetaan ctrl+c, lähetetään suorittavalle prosessille signaali SIGINT joka tavallisesti terminoi prosessin. ctrl+z saa aikaan signaalin SIGSTOP joka asettaa prosessin taustalle. Taustalla oleva prosessi saadaan herätettyä esim. komennolla fg joka saa aikaan prosessin herättävän signaalin SIGCONT.
Prosessi voi myös määritellä itse sen, miten signaalin vastaanotettaessa toimitaan. Tämä tapahtuu määrittelemällä tietylle signaalille signaalikäsittelijä (engl. signal handler). Jos prosessi vastaanottaa signaalin jolla on käsittelijä, kutsuu KJ signaalin tullessa prosessin signaalinkäsittelijää. Käsittelijän koodin suorittamisen jälkeen suoritus palaa jälleen prosessin normaaliin koodiin.
Oikeastaan kaikilla prosessin signaaleilla on joku signaalin käsittelijä. Aluksi prosessin kaikilla signaaliella on oletusarvoiset signaalinäsittelijät joista suurin osa (esim. SIGINT:n käsittelijä) terminoivat prosessin. Prosessi voi tarvittaessa ylikirjoittaa oletusarvoisia signaalikäsittelijöitä.
Lista käytössä olevista signaaleista ja niiden oletusarvoisista signallinkäsittelijöistä löytyy komennolla man 7 signal, seuraavassa ote sivulta:
Signal Value Action Comment ------------------------------------------------------------------------- SIGHUP 1 Term Hangup detected on controlling terminal or death of controlling process SIGINT 2 Term Interrupt from keyboard SIGQUIT 3 Core Quit from keyboard SIGILL 4 Core Illegal Instruction SIGABRT 6 Core Abort signal from abort(3) SIGFPE 8 Core Floating point exception SIGKILL 9 Term Kill signal SIGSEGV 11 Core Invalid memory reference SIGPIPE 13 Term Broken pipe: write to pipe with no readers SIGALRM 14 Term Timer signal from alarm(2) SIGTERM 15 Term Termination signal SIGUSR1 30,10,16 Term User-defined signal 1 SIGUSR2 31,12,17 Term User-defined signal 2 SIGCHLD 20,17,18 Ign Child stopped or terminated SIGCONT 19,18,25 Continue if stopped SIGSTOP 17,19,23 Stop Stop processKohdassa Action oletusarvoisen signaalinkäsittelijän toiminta: Term ja Core tarkoitavat, että prosessi terminoidaan. Ign tarkoittaa, että signaalin tullessa ei tehdä mitään. Stop asettaa prosessin taustalle.
Komentotulki lähettää signaalin (tai pyytää KJ:ltä signaalin lähetystä) mille tahansa prosessille käyttämällä kill-komentoa, joka toimii seuraavasti:
kill 32698 lähetetään prosessille 32698 signaali SIGTERM kill -INT 32698 lähetetään prosessille 32698 signaali SIGINT kill -2 32698 lähetetään prosessille 32698 signaali 2 eli SIGINT kill -9 32698 lähetetään prosessille 32698 signaali 9 eli SIGKILL SIGKILL on varma tapa tappaa prosessiEli parametrina on signaalin numero tai nimen loppuosa (esim. -INT) sekä signaalin kohteen PID. Jos signaalin nimeä tai numeroa ei anneta, lähetetään signaali SIGTERM.
Signaali voidaan myös lähettää komennolla killall joka saa parametriksi signaalinumeron tai nimen sekä suoritettavan ohjelman nimen, esim. killall -9 a.out tappaa kaikki a.out:ia suorittavat prosessit.
Ohjelmakoodista signaalin lähettäminen tapahtuu komennolla jonka nimi on myös kill, ote man-sivulta:
NAME kill - send signal to a process SYNOPSIS #include < sys/types.h> #include < signal.h> int kill(pid_t pid, int sig); DESCRIPTION The kill system call can be used to send any signal to any process. If pid is positive, then signal sig is sent to pid.Esimerkkejä kill-komennon käytöstä pian.
Huom: nimestään huolimatta kill ei siis missään tapauksessa tarkoita prosessin tappamista, vaan signaalin lähettämistä. Usein toki signaalin lähettäminen tappaa signaalin kohteena olevan prosessin.
#include < stdio.h> #include < signal.h> #include < unistd.h> void handleri(int sig){ printf("vastaanotettiin signaali numero %d\n", sig); }Määriteltiin signaalikäsittelijä, joka on funktio, jolla on yksi int-parametri ja joka ei palauta mitään. KJ kutsuu signaalinäsittelijää siinä vaiheessa kun prosessi saa signaalin. Signaalin käsittelyn jälkeen palataan suorittamaan sihen kohtaan koodia, mihin prosessi signaalin tulohetkellä jäi.
Pääohjelmassa signaalinkäsittelijä rekisteröidään, jotta KJ osaa kutsua käsittelijää signaalin tullessa:
int main(int argc, char *argv[]){ struct sigaction act; act.sa_handler = handleri; act.sa_flags = SA_RESETHAND; sigemptyset(&act.sa_mask); sigaction(SIGINT, &act, NULL);Rekisteröinti tapahtuu funktiolla sigaction, jolla on kolme parametria. Ensimmäinen parametri on signaalin nimi, eli mille signaalille käsittelijä asetetaan. Toisena parametrina on osoitin struct sigaction- tietueeseen, joka määrittelee miten signaalin tullessa toimitaan. Kolmas parametri on esimerkissä NULL, jos kolmanneksi parametriksi olisi asetettu osoite struct sigactioniin, olisi funktio asettanut parametriin tiedon siitä miten signaali käsiteltiin ennen sigaction-funktion kutsua. Jos tästä tiedosta ei olla kiinnostuneita, voidaan siis kolmanneksi parametriksi laittaa NULL.
struct sigaction -tietueella on kolme kenttää:
sa_handler signaalin käsittelevä funktio sa_flags ohjeita signaalin käsittelyyn sa_mask mitä tehdään muille signaaleille signaalikäsittelyn aikanaEnsimmäinen kenttä siis on osoitin signaalikäsittelijään eli käytännössä signaalikäsittelijäfunktion nimi. Toinen kenttä saa esimerkissämme arvon SA_RESETHAND joka tarkoittaa sitä, että signaalin käsittelyn tapahtuessa palataan oletusarvoiseen signaalin käsittelytapaan. Jos halutaan, että signaalin käsittelijä jää päälle, tulee parametrille asettaa arvo 0. Kentän muista arvoista lisää myöhemmin.
Jos kesken signaalin käsittelyn aikana tulee uusi samantyyppinen signaali (eli esimerkissä SIGINT), uuden signaalin käsittelyä viivästytetään, ja käsittely tapahtuu vasta kun ensimmäinen signaali on käsitelty kokonaan. Kolmas kenttä sa_mask määrittelee sen, miten muihin signaaleihin suhtaudutaan signaalikäsittelyn aikana. Esimerkissä asetettiin kentän arvoksi tyhjä signaalimaski joka tarkoittaa sitä, että mikään muu signaali ei ole estettynä signaalikäsittelyn aikana. Eli jos signaalikäsittelyn aikana tulee joku toinen signaali, käsitellään se kesken SIGINT:n käsittelyn. Signaalimaskien muodostamiseen palaamme pian.
sigaction-funktion lisäksi signaalikäsittelijöitä voidaan määritellä myös hieman helppokäyttöisemmällä funktiolla signal. Funktion signal-käyttö sisältää useita ongelmia, eikä se ole suositeltavaa. Tällä kurssilla emme käytä funktiota signal ollenkaan.
void handleri(int sig){ printf("vastaanotettiin signaali numero %d\n", sig); }Signaalinkäsittelijässä ei yleensä ole turvallista kutsua esim. stdio.h:ssa ja stdlib.h:ssa määriteltyjä funktioita. Ongelmia syntyy silloin, jos signaalinkäsittelijän suoritus keskeytyy saman prosessin toisen signaalinkäsittelijän suorituksen takia. Esim. printf on määritelty stdio.h:ssa, eli sitä ei tulisi käyttää signaalinkäsittelijässä. Ruudulle kirjoittamisessa parempi tapa on käyttää write-funktiota ja ohjaamalla kirjoitus tiedostokuvaajaan 0 eli näytölle:
void handleri2(int sig){ char viesti[80] = "vastaanotettiin signaali\n"; write(0, viesti, strlen(viesti) ); }Numeroarvon muuttaminen printattavaan muotoon ilman stdio.h:ta ja stdlib.h:ta on hiukan hankalaa.
Jos mahdollista, kannattaa signaalinkäsittelijässä tehdä niin vähän asioita kuin mahdollista, esim. asettaa flagi, joka kertoo signaalin tapahtuneen. Varsinaninen signaaliin liittyvä toimenpide voidaan sitten tarvittaessa tehdä muualla ohjelmakoodissa.
Seuraavassa list funktioista, joita on turvallista kutsua signaalinkäsittelijässä: _Exit() _exit() abort() accept() access() aio_error() aio_return() aio_suspend() alarm() bind() cfgetispeed() cfgetospeed() cfsetispeed() cfsetospeed() chdir() chmod() chown() clock_gettime() close() connect() creat() dup() dup2() execle() execve() fchmod() fchown() fcntl() fdatasync() fork() fpathconf() fstat() fsync() ftruncate() getegid() geteuid() getgid() getgroups() getpeername() getpgrp() getpid() getppid() getsockname() getsockopt() getuid() kill() link() listen() lseek() lstat() mkdir() mkfifo() open() pathconf() pause() pipe() poll() posix_trace_event() pselect() raise() read() readlink() recv() recvfrom() recvmsg() rename() rmdir() select() sem_post() send() sendmsg() sendto() setgid() setpgid() setsid() setsockopt() setuid() shutdown() sigaction() sigaddset() sigdelset() sigemptyset() sigfillset() sigismember() signal() sigpause() sigpending() sigprocmask() sigqueue() sigset() sigsuspend() sleep() socket() socketpair() stat() symlink() sysconf() tcdrain() tcflow() tcflush() tcgetattr() tcgetpgrp() tcsendbreak() tcsetattr() tcsetpgrp() time() timer_getoverrun() timer_gettime() timer_settime() times() umask() uname() unlink() utime() wait() waitpid() write().
Jos ohjelma käyttää vain yhtä signaalia tai signaalinkäsittelijät suoritetaan aina siten, että on varmaa, että mikään muu signaalikäsittelijä ei pääse yhtäaikaa suoritukseen, voi signaalikäsittelijöiden koodissa käyttää myös stdio.h:n ja stdlib.h:n funktioita.
Seuraavassa otteita ohjelmasta 7-2.c. Ohjelma käyttää myös komentoa alarm, joka toimii seuraavasti:
NAME alarm - set an alarm clock for delivery of a signal SYNOPSIS #include < unistd.h> unsigned int alarm(unsigned int seconds); DESCRIPTION alarm arranges for a SIGALRM signal to be delivered to the process in seconds seconds. If seconds is zero, no new alarm is scheduled. In any event any previously set alarm is cancelled.Pääohjelmassa toimitaan seuraavasti:
int main(int argc, char *argv[]){ struct sigaction act; act.sa_handler = handleri; act.sa_flags = SA_RESETHAND; sigemptyset(&act.sa_mask); sigaction(SIGINT, &act, 0); sigaction(SIGALRM, &act, 0); alarm(10); pause();Eli asetataan sama käsittelijäfunktio sekä SIGINT:ille että SIGALRM:ille. Tämän jälkeen kutsulla alarm(10); pyydetään signaalia SIGALRM 10 sekunnin kuluttua. Sitten kutsutaan pause(); eli ruvetaan odottamaan signaalin saapumista. Signaalikäsittelijä on määritelty seuraavasti:
void handleri(int sig){ signaali = sig; if ( sig==SIGINT ) alarm(0); }Ohjelmalla on globaali muuttuja int signaali, johon handleri asettaa vastaanotetun signaalin numeron. Jos signaali ei ollut alarmin tekemä, tehdään kutsu alarm(0) eli poistetaan alarm toiminnasta.
Ohjelma siis pysähtyy pause()-kutsuun niin kauaksi aikaa kunnes signaali vastaanotetaan. Tämän jälkeen tarkastetaan globaalin muuttujan (jonka arvon signaalin käsittelijä asetti) avulla kumpi signaali tapahtui:
alarm(10); pause(); if ( signaali==SIGALRM ) printf("vastaanotettiin SIGALARM\n"); else if ( signaali==SIGINT ) printf("vastaanotettiin SIGINT\n"); return 0; }
Idea ohjelmassa seuraava. Vanhempi luo lapsiprosessin. Lapsiprosessi rekisteröi seuraavat signaalikäsittelijät SIGUSR1:lle ja SIGUSR2:lle:
// signaalinkäsittelijä, joka kirjoittaa ruudulle viestin void handleri1(int sig){ char viesti[] = "lapsi vastaanotti SIGUSR1:n ja aloittaa\n"; // kirjoitetaan merkkijono tiedostokuvaajaan 0, eli näytölle write(0, viesti, strlen(viesti) ); } // signaalinkäsittelijä joka saa suorittavan prosessin lopettamaan itsensä void handleri2(int sig){ char viesti[] = "lapsi vastaanotti SIGUSR2:n ja lopettaa\n"; // kirjoitetaan merkkijono tiedostokuvaajaan 0, eli näytölle write(0, viesti, strlen(viesti) ); exit(0); }handleri1:n suorittaminen siis ei aiheuta muuta kun merkkijonon tulostamisen näytölle. handleri2 tulostaa merkkijonon ja lopettaa koodia suorittavan prosessin.
Heti käsittelijät rekisteröityään lapsi tekee pause():n eli jää odottamaan signaalia. Saatuaan signaalin lapsi jatkaa loopiin missä se tulostaa ruudulle lukuja. Lapsen koodi:
cid = fork(); if ( cid==0 ) { // lapsi rekisteröi kaksi signaalihandleria struct sigaction act1, act2; // SIGUSR1:lle handleri1 joka ei tapa lasta act1.sa_handler = handleri1; act1.sa_flags = SA_RESETHAND; sigemptyset(&act1.sa_mask); sigaction(SIGUSR1, &act1, NULL); // SIGUSR2:lle handleri2 joka saa lapsen lopettamaan act2.sa_handler = handleri2; act2.sa_flags = SA_RESETHAND; sigemptyset(&act2.sa_mask); sigaction(SIGUSR2, &act2, NULL); // odotetaan ensimmäistä signaalia pause(); // lapsi aloittaa prosessoinnin, ikuinen looppi eli odotetaan, että // vanhempi signaloi ja lopettaa lapsen toiminnan int i = 0; while( 1 ){ sleep(1); printf("%d\n",i++); } }Vanhempi lähettää aluksi lapselle signaalin SIGUSR1, joka päästää lapsen pause():sta. Jonkun ajan kuluttua vanhempi sitten lopettaa lapsen lähettämällä SIGUSR2:n. Vanhemman koodi:
sleep(1) // odotetaan hiukan... kill( cid, SIGUSR1 ); // annetaan lapsen jatkaa pausesta sleep(5); // odotetaan ... kill( cid, SIGUSR2 ); // lopetetaan lapsi wait(NULL); // wait lopetettua lasta varten return 0; }Vanhempi siis antaa lapselle luvan jatkaa suoritusta lähettämällä SIGUSR1:n. Sitten kun lasta ei enää tarvita, lähettää vanhempi signaalin SIGUSR2 joka saa aikaan sen, että lapsi terminoi itsensä.
Kyse on pikemminkin prosessien välisestä synkronoinnista, eli prosessit voivat signaalien avulla tahdistaa toistensa toimintaa. Kunnollista kommunikointia eli esim. lukujen tai merkkijonojen lähettämistä signaalien avulla ei voi tehdä. Tätä varten on olemassa muita mekanismejä, esim. putket ja jaetut muistialueet, joita käsittelemme seuraavilla viikoilla.
7-3.c:ssa on kuitenkin pieni ongelma, joka on korjattu laittamalla aikuisen koodiin ennen kill-komentoja sleep(1)-komento. Mitä ongelmallista voisi tapahtua ilman sleep:iä?
Esimerkissä 7-2.c signaalit SIGINT ja SIGALRM voivat saapua ohjelmalle suunilleen yhtäaikaa, esim. siten että, SIGINT on juuri tapahtunut ja kun ollaan suorittamassa signaalikäsittelijää, tulee SIGALRM. Näin käydessä toinen signaalikäsittelijä suoritetaan ensimmäisen signaalikäsittelijän ollessa kesken. Esimerkissä 7-2.c tästä ei seuraa mitään kovin vakavaa, mutta isommissa ohjelmissa vastaavilla tilanteilla voi olla vakavia seurauksia.
Katsotaan vielä tarkemmin esimerkkiä 7-2.c. Signaalinkäsittelijä, jonka tehtävänä oli huolehtia molemmista signaaleista oli seuraava:
void handleri(int sig){ signaali = sig; if ( sig==SIGINT ) alarm(0); }Ideana tässä on se, että jos käsitellään SIGINT:iä, niin komennolla alarm(0) asetetaan käynnissä oleva herätyskello pois päältä, jotta signaalia SIGALRM ei enää tule. Ongelma tässä on kuitenkin se, että SIGALRM voi tulla juuri sillä hetkellä kun signaalinkäsittelijän suoritus on kesken ja alarm(0)-komentoa ei ole vielä ehditty tekemään. Tällöin SIGALRM:n aiheuttama signaalin käsittely aloitetaan kesken SIGINT:n käsittelyn ja ohjelma "menee sekaisin".
Tilanne on hyvin tyypillinen rinnakkaisuuden aiheuttama ilmiö, jota kutsutaan kilpailutilanteeksi (engl. race condition), jolla tarkoitetaan sitä, että järjestelmän oikein toimiminen riippuu tapahtumien ajoituksesta. Järjestelmä siis toimii yleensä oikein, mutta jos kaksi asiaa tapahtuu suunilleen yhtä aikaa, on vaarana se, että ohjelma toimii väärin.
Signaaleihin liittyvät kilpailutilanteet on kuitenkin mahdollista estää. Ohjelman 7-2.c viat on korjattu ohjelmassa 7-4.c josta otteita seuraavassa:
Signaalinkäsittelijän rekisteröintiin on tehty pieni lisäys:
struct sigaction act; act.sa_handler = handleri; act.sa_flags = SA_RESETHAND; sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask, SIGALRM); sigaddset(&act.sa_mask, SIGINT); sigaction(SIGINT, &act, 0); sigaction(SIGALRM, &act, 0);Kuten aiemmin mainittiin, niin jos kesken signaalin käsittelyn aikana tulee uusi samantyyppinen signaali kuin mitä juuri käsitellään, uuden signaalin käsittelyä viivästytetään, ja käsittely tapahtuu vasta kun ensimmäinen signaali on käsitelty kokonaan. Ongelmatilanne saattaa syntyä jos SIGINT:n käsittelyn aikana tapahtuu SIGARLM tai päinvastoin.
struct sigaction:in kenttä sa_mask sisältää ns. signaalimaskin, joka määrittelee mitkä signaalit estetään signaalinkäsittelijän suorituksen aikana.
Ensin tehdään sigemptyset(&act.sa_mask); joka nollaa signaalimaskin. Tämän jälkeen lisätään maskiin SIGALRM ja SIGINT:
sigaddset(&act.sa_mask, SIGALRM); sigaddset(&act.sa_mask, SIGINT);Tämä saa aikaan sen, että jos signaalinkäsittelyn aikana tapahtuu SIGALRM tai SIGINT, ei niitä vastaavaa signaalinäsittelyä suoriteta ennen kun signaalinkäsittely on ohi. Tällä siis estetään yllä kuvattu kilpailutilanne, jossa signaalinkäsittely keskeytyy toisen saapuvan signaalin takia.
Ongelmaksi jää vielä se, että kun signaalinkäsittelystä palataan, suoritetaan käsittelyn aikana blokattujen signaalien käsittelijät. Ongelma on hoidettu 7-4.c:ssa muuttamalla signaalikäsittelijää seuraavasti:
void handleri(int sig){ signaali = sig; if ( sig==SIGINT ) alarm(0); struct sigaction act; act.sa_handler = SIG_IGN; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGINT, &act, 0); sigaction(SIGALRM, &act, 0); }Jos siis esim. SIGINT:n käsittelyä suorittaessa SIGALRM tapahtuu, on signaali estettynä ja signaali suoritetaan vasta signaalinkäsittelijän suorituksen jälkeen. Emme kuitenkaan halua, että signaalinkäsittelijää suoritetaan enää missään tapauksessa, ja tämän takia yllä asetetaan SIGINT:lle ja SIGALRM:lle oletushandleri SIG_IGN joka jättää signaalin huomiotta. Eli jos SIGINT tai SIGALRM vielä tapahtuu, ei tehdä mitään.
Hiukan monimutkaista, mutta valitettavasti asiat muuttuvat monimutkaisiksi heti kun rinnakkaisuus astuu kuvaan.
Periaatteena on se, että ensin rakennetaan signaalimaski tyyppiä sigmask_t olevaan muuttujaan ja tämän jälkeen asetetaan maski prosessille.
Tarkastellaan ohjelmaa 7-5.c, joka asettaa signaalimaskin:
int main(int argc, char *argv[]){ sigset_t mask; // muuttuja, johon rakennetaan signaalimaski sigfillset(&mask); sigdelset(&mask, SIGINT); // nyt mask sisältää kaikki muut signaalit paitsi SIGINT:n // asetetaan rakennettu signaalimaski prosessille sigprocmask(SIG_SETMASK, &mask, NULL);Aluksi ohjelma muodostaa maskin, jossa on kaikki muut signaalit paitsi SIGINT. Tämä tapahtuu ensin täyttämällä muuttujassa mask oleva maski funktiolla sigfillset ja tämän jälkeen poistamalla SIGINT maskista funktiolla sigdelset. Tämän jälkeen muuttujaan mask rakennettu maski asetetaan ohjelmalle komennolla sigprocmask(SIG_SETMASK, &mask, NULL);.
Funktion toiminnan esittely man-sivulta:
SYNOPSIS #include < signal.h> int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); The sigprocmask call is used to change the list of currently blocked signals. The behaviour of the call is dependent on the value of how, as follows. SIG_BLOCK The set of blocked signals is the union of the current set and the set argument. SIG_UNBLOCK The signals in set are removed from the current set of blocked signals. It is legal to attempt to unblock a signal which is not blocked. SIG_SETMASK The set of blocked signals is set to the argument set. If oldset is non-null, the previous value of the signal mask is stored in oldset.Huom: muuttujaan sigset_t mask rakennettu maski asettuu siis voimaan vasta sigprocmask()-komennon antamisen yhteydessä.
Esimerkissä signaalimaski rakennettiin käyttäen funktioita sigfillset ja sigdelset. Myös muita signaalimaskien manipulointiin tarkoitettuja funktioita on olemassa. Ote man-sivulta:
SIGSETOPS(3) Linux Programmer's Manual SIGSETOPS(3) NAME sigemptyset, sigfillset, sigaddset, sigdelset, sigismember - POSIX sig- nal set operations. SYNOPSIS #include < signal.h> int sigemptyset(sigset_t *set); int sigfillset(sigset_t *set); int sigaddset(sigset_t *set, int signum); int sigdelset(sigset_t *set, int signum); int sigismember(const sigset_t *set, int signum); DESCRIPTION The sigsetops(3) functions allow the manipulation of POSIX signal sets. sigemptyset initializes the signal set given by set to empty, with all signals excluded from the set. sigfillset initializes set to full, including all signals. sigaddset and sigdelset add and delete respectively signal signum from set. sigismember tests whether signum is a member of set. RETURN VALUE sigemptyset, sigfillset, sigaddset and sigdelset return 0 on success and -1 on error. sigismember returns 1 if signum is a member of set, 0 if signum is not a member, and -1 on error.Ohjelma 7-5.c siis blokkaa muut signaalit paitsi SIGINT:n, jolle rekisteröidään seuraava käsittelijä:
void handleri(int sig){ int i; sigset_t pend; // haetaan muuttujaan pend tieto mitkä signaalit ovat odottamassa sigpending(&pend); for ( i=1; i<18; i++ ) { if ( sigismember(&pend, i) ) printf("signaali %s odottaa \n", signame[i]); } printf("\n"); }Käsittelijässä käytetään funktiota sigpending, joka toimii seuraavasti:
#include < signal.h> int sigpending(sigset_t *set); DESCRIPTION The sigpending call allows the examination of pending signals (ones which have been raised while blocked). The signal mask of pending sig- nals is stored in set.Eli sigpending hakee sigset_t-tyyppiseen muuttujaan tiedon siitä mitä estettyjä signaaleja prosessilla on odottamassa.
for-loopissa käydään läpi signaalinumerot 1-18 ja katsotaan mitä vastaava signaali on asetettuna odottavia signaaleja kuvaavassa muuttujassa pend. Printausta varten signaalinumero mapataan signaalin nimeksi käyttäen golbaaliksi määriteltyä merkkijonotaulukkoa:
char signame[19][10] = { "", "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "", "SIGABRT", "", "SIGFPE", "SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM", "", "SIGCHLD" };Kyseessä siis 19 paikkainen taulukko, joka koostuu korkeintaan 10 merkkiä pitkistä merkkijonoista.
Ohjelma siis blokkaa kaikki muut signaalit paitsi SIGINT:n. Aina SIGINT:n saapuessa tulostetaan mitä blokatuista signaaleista on tapahtunut, eli odottamassa (pending). Jos joku odottava signaali otettaisiin pois prosessin signaalimaskista, siirryttäisiin heti suorittamaan odottavan prosessin signaalinkäsittelijää.