Useissa käyttöjärjestelmissä on mahdollista keskeyttää (katkaista) suorituksessa oleva ohjelma jollakin näppäimellä tai näppäinyhdistelmällä, esim. control-C:llä tai Break-näppäimellä.
Kyseiset
näppäimet tai näppäinkombinaatiot ovat tietysti käyttöjärjestelmästä riippuvia.
signal.h
Asiaan liittyvät hommat ovat standardikirjastossa
signal.h
.
Siinä on määritelty joukko signaaleja sekä funktio signal
, jolla
voidaan yrittää liittää johonkin signaaliin joko jokin valmiiksi määritellyistä
käsittelytavoista taikka ohjelmassa itsessään määritelty funktio.
SIGABRT | ohjelman epänormaali päättyminen,
kuten abort -funktion aiheuttama
|
---|---|
SIGFPE | virheellinen aritmeettinen operaatio
kuten nollalla jako tai ylivuoto; nimi johtuu sanoista "floating-point exception",
mutta signaalin merkitys on siis ainakin periaatteessa yleisempi (mutta
useissa järjestelmissä esim. liukulukulaskennan ylivuoto voi aiheuttaa
SIGFPE :n, kokonaislukulaskennan ylivuoto ei)
|
SIGILL | luvaton konekäsky (illegal instruction), tai yleisemmin "detection of an invalid function image" |
SIGINT | vuorovaikutteinen "huomiota pyytävä" signaali (interactive attention request) |
SIGSEGV | luvaton muistiviittaus; nimi johtuu historiallisista syistä sanoista "segmentation violation" |
SIGTERM | ohjelman saama lopettamispyyntö (termination request). |
Signaalin käsittelytavaksi voidaan ilmoittaa
SIG_IGN
, joka tarkoittaa signaalin jättämistä huomiotta
(engl. ignore), tai SIG_DFL
, joka tarkoittaa
järjestelmän oletusarvoista käsittelytapaa, taikka funktio.
Viimeksi mainitussa tapauksessa funktion tulee olla tyypitön
(void
) ja sillä tulee olla yksi int
-tyyppinen
argumentti; signaali aiheuttaa funktion kutsumisen siten, että sille
välittyy argumenttina signaalin koodi. Koodit ovat järjestelmäkohtaisia
kokonaislukuja, mutta edellä mainitut SIGABRT
ovat
järjestelmäriippumattomia tapoja viitata näihin koodeihin.
Raaimmassa tapauksessa (tuollaiset keskeytysyritykset halutaan estää kokonaan) voi tehdä näin: sopivaan kohtaan jonnekin tiedoston alkuun
#include <signal.h>
ja sitten vaikkapa pääohjelman alkuun
signal(SIGINT, SIG_IGN);
Periaatteessa ei ole mitään takeita siitä, että tuo todella estää keskeytykset,
koska on toteutuksesta riippuva asia, aiheuttaako
Jos kokeillaan tyypillisessä Unix-ympäristössä, niin edellä kuvattu tapa estää control-C-keskeytykset vaan ei ns. quit-keskeytystä (joka kai tavallisimmin on liitetty control-\:aan). Jos senkin haluaa hoitaa niin on ehkä lisäksi sanottava
signal(SIGQUIT, SIG_IGN);
mutta tämä onkin sitten jo konekohtaista, ei standardi-C:tä;
standardi ei tunne SIGQUIT
-nimistä signaalia.
Tyypillisessä Unix-koneessa saa lisätietoja homman hoitamisesta komennoilla
Varoitus: Mitä enemmän tuollaisia keskeytystapoja onnistuu ohjelmallisesti estämään, sitä varmempaa on, että kun ohjelma joutuu omituiseen tilaan kuten ikiluuppiin, sitä ei saa enää kirveelläkään poikki. Tai ehkä juuri kirveellä. :-) Kun joskus noita kokeilin, sain aikaan tilanteita, joissa olin loppujen lopuksi onnellinen, etten ollut huomannut estää SIGKILL-keskeytyksiäkin. :-) Tosin niiden estämisen ei spesifikaatioiden mukaan pitäisi olla mahdollistakaan.
Aihepiiriä käsittelee myös C-fakin kohta 19.38: How can I trap or ignore keyboard interrupts like control-C?
Seuraavassa on yksinkertainen keskeytyksenkäsittelyrutiini tilanteisiin, joissa halutaan, että kesken laskennan voi pyytää ohjelmalta tietoja laskennan tilasta.
Tämä kävisi seuraavaan tapaan:
long kierros; void hoida(int i) { int merkki; printf("Kierros %ld\n", kierros); signal(SIGINT,&hoida); return; } int main(void) { signal(SIGINT, &hoida); ...
Tässä kierros
on globaali muuttuja, jota sitten
käytetään kierroslaskurina laskentasilmukassa. Realistisessa
tilanteessa keskeytyksenkäsittelyrutiini tulostaisi muutakin
tietoa tilanteesta.
Rutiinin sisällä on kutsu signal(SIGINT,&hoida);
, koska
signal(signaali,SIG_DFL)
. Kutsu siis huolehtii
siitä, että signaalin sattuessa myöhemmin sen käsittelee oma rutiinimme
eikä järjestelmän oletuskäsittely.
Ota huomioon edellä esitetty varoitus:
jos SIGINT
-signaalin ottaminen "ohjelman haltuun"
onnistuu, ohjelmaa ei ehkä pysty katkaisemaan normaalilla tavalla!
Ellei tiedetä, että jokin muu katkaisumenetelmä toimii,
kannattaisi rutiiniin lisätä koodi, joka tietojen
tulostamisen jälkeen kysyisi käyttäjältä, haluaako hän katkaista
ohjelman suorituksen. (Tästä taas seuraisi, että ohjelman suoritus
on pysähdyksissä, kunnes käyttäjä vastaa kysymykseen.)
Signaalien käsittely on hankalampaa kuin voisi luulla. Erityisesti
mainittakoon, edellä mainittujen järjestelmäriippuvuuksien lisäksi,
että normaali paluu (return;
)
SIGFPE
:n käsittelevästä funktiosta ei ole sallittu.
Täten aritmeettisten virheiden käsittely vaatii lisätoimenpiteitä,
jos ohjelman suoritusta halutaan jatkaa virhetilanteissa.
Ks. Korpela - Larmela: signal.h
.
Jukka Korpela.
Kirjoitetty 1998-09-09, täydennetty 2000-07-04 ja
2003-01-09.