Azaz a folyamatok informálása
előre definiált jelek segítségével...
Jelzések kezelése (hagyományos)
signal
kill, raise
alarm, pause
setjmp, longjmp
POSIX kiegészítések (megbízható jelzések)
Folyamatokkal végzett műveletek
A jelzés olyan szoftveres megszakítás, amelyet egy
folyamat kap valamilyen esemény bekövetkeztekor. A jelzéseket főként
kivételek fellépésekor, valamilyen riasztáskor, váratlan befejezéskor, vagy
ritkábban folyamatok közti kommunikációra használják. Ezek jelentik a
legegyszerűbb, legrégibb, de egyben a legmerevebb kommunikációs formát a folyamatok
között. A jelzéseket felfoghatjuk akár egy folyamat felé irányuló figyelmeztetésként is.
Minden jelzésnek van egy neve, amely kötelezően a SIG előtaggal kezdődik. Ezek a nevek
szimbolikus konstansok formájában vannak definiálva, és szigorúan pozitív egész
számokat jelölnek.
Egy jelzés által hordozott információ a folyamat számára minimális, hiszen
csupán a típust (magát a jelzés számát) foglalja magába. Minden jelzésnek van egy
forrása és valamilyen okból keletkezik.
A jelzések
okait a következőképpen
csoportosíthatjuk:
a.
futtatás során fellépő hibák:
- a folyamat megengedett határain kívül eső címzés (ekkor egy SIGSEGV
jel keletkezik),
- írási próbálkozás egy csak olvasható memóriazónára (például a kódszegmensbe),
- magasabb privilégiumszinttel rendelkező utasítások végrehajtása,
- hardverhiba detektálása esetén,
b.
szoftverhiba egy rendszerfüggvény hívásakor:
- nem létező rendszerfüggvény,
- egy pipe állományba való írás, anélkül, hogy egy másik folyamat olvasná azt
(SIGPIPE),
- nem megfelelő paraméter használata egy függvényhívásban,
- valamilyen szükséges erőforrás hiánya egy függvény végrehajtása során
(például nincs elegendő külső memória egy állomány betöltésére),
c.
kommunikáció két folyamat vagy folyamatcsoportok között úgy, hogy a folyamat
egy jelet kap a kill függvényen keresztül,
d.
háttérben futó folyamat befejezése a kill
parancs használatával,
e. egy
folyamat erőszakos befejezése a rendszer által egy SIGKILL jel
segítségével (például egy shutdown parancs esetén),
f. egy
folyamat az idő függvényében egy SIGALRM jelet küld magának,
g. a
felhasználó a billentyűzetről megszakít egy terminálon futó
folyamatot,
- egy folyamat feladása (Ctrl-\),
- megszakítás generálása (Ctrl-C/Break vagy DELETE),
- a terminálhoz való újrakapcsolódás,
h. egy
folyamat befejezése, amint éppen az exit
függvényt hajtja végre, vagy amint a folyamat a SIGCHLD jelet generálja a kill rendszerfüggvény segítségével,
i. egy
folyamat megjelölése.
A jelzés típusa értelemszerűen tükrözi a jelzés okát is. A Függelékben
bemutatjuk a UNIX SVR2 által definiált 19 jelzéstípust. A Unix SVR4 és 4.3+BSD
verzióiban már 31 típusú jelzés szerepel. Ezek részletes bemutatását lásd a
bibliográfiában.
Egy folyamat, amely valamilyen jelet kapott a következőképpen
viselkedhet:
a.
Figyelmen kívül hagyja a jelet és folyatatja az aktivitását. A jelzések közül
csak a SIGKILL nem hanyagolható el (a rendszernek jogában áll befejezni
bármelyik folyamatot).
b. A
jel kezelését a rendszer magjára bízza. Ebben az esetben, kivétel a SIGCHLD és
SIGPWR (és újabban a SIGINFO, SIGURG és SIGWINCH), amelyeket figyelmen kívül
hagy, az összes többi jelzés a folyamat befejezéséhez vezet – ez az implicit
működés.
c. Egy
saját eljárással automatikusan lekezeli a jelet, ahogy az megjelenik. Az
eljárás befejeztével a program ott folytatódik, ahol abbahagyta.
A fork rendszerfüggvény hívására a
létrejövő gyerekfolyamat a szülőtől örökli a jelzésekhez
kapcsolódó eseményeket is.
Az exec függvény a rendszer magjára hagyja a
jelzések kezelését, még akkor is, ha ezeket úgy állítottuk be, hogy a folyamat kezelje le. Csak
az előrelátott, eredeti tevékenységeket tartja meg, figyelmen kívül hagyva
egyes jelzéseket a folyamat felé. Ez azért van, mert az exec hívására az
aktuális folyamat kódszegmense elvesztődik, s ezáltal a jelzések kezelését
végző eljárások is megsemmisülnek.
Függetlenül attól, hogy miként reagál egy program egy bizonyos jelre, a
tevékenység befejeztével – ha a jelet nem hagyjuk figyelmen kívül – a rendszer
újrainicializálja az értékeket, felkészülve ezáltal egy későbbi jel
fogadására. Kivételt képeznek ez alól a SIGILL és a SIGTRAP jelek, amelyek
nagyon gyakran előfordulnak, ezért ezek többszöri aktualizálása igencsak
lassítaná a kezelésüket.
Ha egy függvény végrehajtása során a rendszer egy jelet érzékel, a hívás
hibával fog befejeződni. Sajátos esetben, ha a rendszer egy lassú periférián
próbál ki/bemeneti műveletet végezni és egy jelzést kap, a függvény -1
értéket (azaz hibát) fog visszatéríteni. Ebben az esetben az errno változó az
EINTR értéket fogja tartalmazni. Ilyenkor az errno változó tesztelése után, a
program újra próbálkozhat a művelet elvégzésével.
A folyamathoz érkezett jelzéseket a rendszer nem tárolja egy várakozási
sorban. Éppen ezért, ha a folyamat egy jelet figyelmen kívül hagyott, az
mindörökre elveszett. Egyetlen kivétel a SIGCHLD jel, amelyet a gyerek küld a
szülőnek, tudatva, hogy pályafutását befejezte. Ezt a jelet a szülő
egy wait függvényhívás során érzékelheti. Ez azért fontos, mert egy gyerek már
azelőtt befejeződhet, hogy a szülő kiadná a wait parancsot. Ha
viszont a jelet nem őriznénk meg, a szülő leblokálna miközben hiába
várná a gyerek befejeződését. Mivel a bejövő jelzéseket a folyamatok
nem tudják megőrizni ez a mechanizmus nem túl hatékony, és sok hibával
járhat.
Összefoglalás: Egy jelet elküldhetünk bármely pillanatban, időszakosan egy másik
folyamattól, de általában a kerneltől indulnak valamilyen különleges
esemény hatására. A jelek nem tartalmaznak információt csak amit a típusuk
által hordoznak. A jelzések másik hátránya, hogy a folyamatok nem tudják
azonosítani a beérkezett jelek forrását.
Jelzések kezelése (hagyományos)
A signal függvény szerepe biztosítani a jelzések
programból történő kezelését.
Alakja:
#include <signal.h>
int (*signal (jelzes, fuggveny))();
int jelzes;
int (*fuggveny)();
vagy
#include <signal.h>
void (*signal (int jelzes, void (*fuggveny)(int)))(int);
Az első változat a régebben használatos stílus (amikor még nem volt
értelmezve a void típus). A második változatban az
argumentumok típusa is fel van tüntetve: egy egész szám és egy mutató egy
függvényhez.
A jelzes paraméter a jel száma vagy az ennek
megfelelő szimbólum, amelynek a kezelését szeretnénk testreszabni.
A fuggveny argumentum leírja, hogyan kezelje a
folyamat az adott jelet. A következő értékeket veheti fel:
- SIG_DFL – A jelzések kezelését a rendszer magja
végzi. Ez a jelzések implicit kezelése.
- SIG_IGN – Figyelmen kívül hagyja az adott
jelet (kivétel a SIGKILL és a SIGSTOP).
- mutató egy
függvényhez, amely lekezeli a jelet – Ebben az esetben egy jel érkezésekor az adott függvény hívódik meg. Ez a
függvény a kezelő rutin nevét viseli. Egyetlen argumentuma van, ami éppen
a kezelni kívánt jel számát jelenti. Miután egy jelzést ily módon lekezeltünk,
ugyanannak a jelnek a következő érzékelése már implicit módon történik.
Tehát, ha ugyanezt a függvényt még egyszer szeretnénk használni, megfelelő
intézkedéseket kell tennünk.
A függvény visszatérési értéke az előzőleg definiált függvény
erre a jelre. Hiba esetén a SIG_ERR konstanst kapjuk, amelynek definíciója:
#define SIG_ERR (void (*)())-1;
A signal függvény bemutatására tekintsük a következő példát:
#include <signal.h>
#include <errno.h>
static void sig_usr1(int);
/* kill -USR1 <pid> paranccsal generálható */
static void sig_intr(int);
/* Ctrl-C-re generálva és újratöltve */
static void sig_quit(int);
/* Ctrl-\-vel generálva, majd visszaállítva */
static void sig_alarm(int); /*
az alarm(t)-ben megadott t idő eltelte után generálódik */
int main(void)
{
if (signal(SIGALRM, sig_alarm) == SIG_ERR)
perror("hiba: signal(SIGALRM, ...)");
if (signal(SIGUSR1, sig_usr1) == SIG_ERR)
perror("hiba: signal(SIGUSR1, ...)");
if (signal(SIGINT, sig_intr) == SIG_ERR)
perror("hiba: signal(SIGINT, ...)");
if (signal(SIGQUIT, sig_quit) == SIG_ERR)
perror("hiba: signal(SIGQUIT, ...)");
for (;;) pause();
}
static void sig_alarm(int sig)
{
printf("SIGALRM jelet vettem...\n");
return;
}
static void sig_quit(int sig)
{
printf("SIGQUIT jelet vettem...\n");
if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)
perror("nem lehet visszaallitani ezt a
jelet...");
return;
}
static void sig_intr(int sig)
{
printf("SIGINT jelet vettem...\n");
if (signal(SIGINT, sig_intr) == SIG_ERR)
perror("nem lehet ujra betolteni...");
return;
}
static void sig_usr1(int sig)
{
printf("SIGUSR1 jelet vettem...\n");
alarm(1);
printf("a riasztas 1 masodperc mulva elindul!\n");
return;
}
Az alarm függvény a hívásától számított 1 másodperc leteltével egy SIGALRM
jelet generál.
Ha a programot háttérben futtatjuk, a következőképpen viselkedik:
$ a.out&
[1]
324
a shell kiírja a folyamat azonosítóját
$ kill -USR1
324
a folyamatnak egy SIGUSR1 jelet küld
SIGUSR1 jelet
vettem...
generálja a jelet 1 másodperc múlva
a riasztas 1 masodperc mulva elindul!
SIGALRM jelet
vettem...
a SIGALRM jelet generálta
$ kill
324
egy SIGTERM jelet küld
Ha azonban a programot nem a háttérben futtatjuk:
$ a.out
leütünk egy Ctrl-C billentyűt
SIGINT jelet vettem...
SIGINT jelet vettem...
leütünk egy Ctrl-\ billentyűt
SIGQUIT jelet vettem...
még egyszer leütünk egy Ctrl-\ billentyűt
$
A SIGINT-et kezelő rutin újra és újra betölti saját magát minden
alkalommal amikor végrehajtódik. Erre azért van szükség, mert alapértelmezésben
ez a beállítás csak az első jel érkezéséig él. Ezzel szemben a SIGQUIT-hoz
tartozó eljárást nem töltjük be csak egyszer, ezért másodjára már az eredeti
rutin szerint jár el, és befejezi a programot.
A kill függvény egy jelet küld egy folyamatnak vagy egy folyamatcsoportnak.
A raise megengedi a folyamatnak, hogy saját magának is küldjön jelet. Az
első függvény a POSIX.1 verzióban van definiálva, míg a második az ANSI
C-ben.
Szintaxisuk:
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int jel);
int raise(int jel);
Mindkét folyamat 0 értéket térít vissza, ha a művelet sikeres volt, és
-1-et hiba esetén. A pid változó azt a folyamatot, vagy folyamatcsoportot
jelöli, aminek a jel jelet szeretnénk küldeni.
A pid változó értékei a következők lehetnek:
- pid > 0 – a jelet a pid azonosítójú folyamatnak küldi,
- pid = 0 – a jelet minden olyan folyamatnak elküldi az
aktuális csoporton belül, amelyekhez van joga (kivétel a swapper (0), az init
(1) és a pagedaemon (2) folyamatok); ez a dolog nagyon gyakori, amikor a kill 0
utasítással töröljük a háttérben futó folyamatokat, anélkül, hogy az
azonosítóikat megadnánk,
- pid < 0,
- pid ≠ -1 – a jelet minden olyan
folyamatnak elküldi, amelyeknek az ID-je megegyezik a pid változó abszolút
értékével (és természetesen van ehhez joga az adott folyamatnak),
- pid = -1 – a POSIX.1 ezt a lehetőséget nem specifikálta;
az SVR4 és 4.3+BSD ezt az értéket a broadcast jeleknél használja; ezek nem
küldődnek el a fent már említett folyamatokhoz; ha a küldő a
superuser, akkor a jelet minden folyamat megkapja; ezt a SIGTERM jel küldésekor
szokták használni, azért, hogy a rendszert felfüggesszék.
A folyamatok közti jelküldésnél a szabály az, hogy a küldőnek legyen
joga a jelet elküldeni (a valódi vagy effektív felhasználó ID-ja legyen
egyenlő a valódi vagy effektív vevő uid-jével). A superuser bármelyik
folyamathoz küldhet jeleket.
Kivételt képez a fenti szabály alól a SIGCONT jel, amelyet bármely
folyamatnak el lehet küldeni, amely ugyanahhoz a géphez tartozik.
A POSIX.1 szabvány definiálja a 0 jelet is, amely a nul jelzésnek felel
meg. A kill függvény 0 paraméterrel nem küldi el a megadott jelet, hanem csak
ellenőrzi, hogy létezik-e a pid azonosítójú folyamat. Ha a folyamat nem
létezik, a függvény -1-et térít vissza és az errno értéke ESRCH lesz.
A raise függvény implementációja a kill függvény segítségével:
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
int raise(int jel)
{
return (kill(getpid(), jel));
}
Az alarm függvény lehetővé teszi egy időzítő beállítását. A
megadott idő (mp másodperc) elteltével egy SIGALRM jelet ad ki. Ha a folyamat
a jelet figyelmen kívül hagyja, vagy nem érzékeli, a függvény alapbeállítás
szerint befejezi a folyamatot. Alakja:
#include <unistd.h>
unsigned int alarm(unsigned int mp);
A visszatérített érték vagy 0, vagy az előző SIGALRM jelzés
kiadása óta eltelt másodpercek száma.
Minden folyamathoz egyetlen időmérő (óra) tartozik, ezért a
függvénynek egy újabb meghívása esetén az előző (mp) érték
felülíródik. Ha az mp értéke 0, az előzőleg kiadott SIGALRM kérések
törlődnek.
Mivel a SIGALRM implicit a folyamat befejezéséhez vezet, több folyamat,
amely az alarm függvényt használja érzékeli ezt a jelet. Mielőtt a
folyamat befejeződne lehetőségünk nyílik különféle utómunkák (például
törlések) végrehajtására.
A pause függvény felfüggeszti (várakozási állapotba
helyezi) a hívó folyamatot a legelső jel érkezéséig.
#include <unistd.h>
int pause(void);
Ha a bejövő jelet a folyamat nem kezeli le vagy figyelmen kívül
hagyja, akkor ez a művelet a folyamat befejezéséhez vezet. A program pause
függvényből csak a kapott jel lekezelése után jön ki. Ezért a függvény
minden esetben a -1 értéket téríti vissza, míg az errno változó értéke EINTR
lesz.
A pause függvényt legtöbbször az alarm-mal együttesen szoktuk használni.
Az alarm függvényt nagyon gyakran alkalmazzuk olyan esetekben, amikor egy
bizonyos műveletet valamilyen időintervallumon belül szeretnénk
elvégezni. Amennyiben a műveletet a megadott idő alatt nem sikerült
elvégezni, a végrehajtást felfüggesztjük.
Az alábbi példa a standard bemenetről olvas és a standard kimenetre
ír:
#include <setjmp.h>
#include <signal.h>
#include "hdr.h"
static void sig_alarm();
int main(void)
{
int n;
char line[MAXLINE];
if (signal(SIGALRM, sig_alarm) == SIG_ERR)
perror("hiba: signal(SIGALRM, ...)");
alarm(10);
if ((n = read(0, line, MAXLINE)) < 0)
perror("read hiba");
alarm(0);
write(1, line, n);
exit(0);
}
static void sig_alarm(int sig)
{
return;
}
A kódnak két hátránya is van:
- ha a rendszer az alarm és az első read utasítás végrehajtása között a megengedettnél
(10 másodperc) többet késik, a read hívás mindörökre leáll,
- ha a rendszerfüggvények túlsúlyban vannak, a read hívás nem szakad meg a
SIGALRM jelzés kezelésekor; ebben az esetben az alarm függvénynek nincs
értelme.
Azért, hogy ezt a kellemetlenséget elkerüljük, segítségünkre vannak a
setjmp és a longjmp függvények.
A jelzésekkel összeköttetésben, egy programban gyakran szükségünk van (nem
helyi) ugrások végrehajtására. Erre két függvényünk van. A setjmp segítségével
rögzíthetünk egy ugrási pontot a processzor állapotának a lementésével, míg a
longjmp függvény elvégzi az ugrást egy, a paraméterén keresztül megadott
pontra.
#include <setjmp.h>
int setjmp(jmp_buf jmpenv);
void longjmp(jmp_buf jmpenv, int val);
A setjmp függvényből két esetben térhetünk vissza:
a. az ugrási pont sikeres megállapítása esetén; a függvény 0 értéket térít
vissza,
b. egy nem helyi ugrás elvégzése esetén a függvény visszatérési értéke az a
val érték lesz, amellyel a longjmp függvényt hívtuk.
Most lássuk az előbbi példa javított változatát (setjmp és longjmp
függvények használatával):
#include <setjmp.h>
#include <signal.h>
#include "hdr.h"
static void sig_alarm();
static jmp_buf env_alarm;
int main(void)
{
int n;
char line[MAXLINE];
if (signal(SIGALRM, sig_alarm) == SIG_ERR)
perror("hiba: signal(SIGALRM, ...)");
if (setjmp(env_alarm) != 0)
err_quit("lejart az olvasasra szant ido");
alarm(10);
if ((n = read(0, line, MAXLINE)) < 0)
perror("read hiba");
alarm(0);
write(1, line, n);
exit(0);
}
static void sig_alarm(int sig)
{
longjmp(env_alarm, 1);
}
A POSIX szabvány (megbízható jelzéskezelés)
A POSIX jelzés-feldolgozást irányító rutinjait úgy tervezték, hogy azok alapvetően jelzés-halmazokkal foglalkozzanak: ehhez bevezették a sigset_t adattípust, mely adattípushoz definiálták ennek kezelésekor használható műveleteket is. Ezek a műveletek a következők:
#include <signal.h>
sigset_t sHalmaz;
sigemptyset
int sigemptyset(sigset_t *set);
A sigemptyset() függvény az argumentumában megadott jelzés-halmazt üresre
állítja, azaz olyan állapotúra, hogy egyetlen jelzés sem kerül bele.
sigfillset
int sigfillset(sigset_t *set);
A sigfillset() függvény az argumentumában megadott jelzés-halmazt olyan értékre állítja be, hogy a rendszerben az összes definiált jelzés-típus benne legyen.
sigaddset
int sigaddset(sigset_t *set,int sig);
A sigaddset() függvénnyel lehet az első
argumentumában megadott jelzés-halmazba a második argumentumban megadott
jelzés-típust betenni.
sigdelset
int sigdelset(sigset_t *set,int sig);
A sigdelset() függvénnyel pedig az első
argumentumában levo jelzés-halmazból a második argumentumában megadott jelzést
kivenni.
sigmember
int sigismember(sigset_t *set,int sig);
A sigismember() függvény visszatérési értéke 1 (igaz), ha az első
argumentumában megadott jelzés-halmaz tartalmazza a második argumentumában
megadott jelzést.
sigaction
Az egyes jelzések kezelésének mikéntjét a sigaction() rendszerhívással jelölhetjük ki. Ennek paraméterezése a következő:
int sigaction(int sig, const struct sigaction *newact, struct sigaction
*oldact);
A sigaction() rendszerhívás első argumentuma azt határozza meg, hogy
melyik jelzés kezelését akarjuk megváltoztatni vagy lekérdezni.
A második argumentumban lehet kijelölni az első argumentumban megadott
jelzés-típus új (mostantól érvényes) kezelésmódját, illetve ott NULL pointert
megadva a rendszerhívás nem módosítja a jelzés éppen érvényes kezelésmódját. A
jelzés-kezelés módját leíró struct sigaction struktúra felépítése a következő:
struct sigaction {
void (*sa_handler)();
sigset_t sa_mask;
int sa_flags;
};
Az sa_handler komponens értéke vagy a SIG_IGN vagy SIG_DFL szimbólikus konstansok valamelyike, vagy pedig a jelzés-kezelő függvény címe lehet. A harmadik argumentumban (ha ott nem NULL értéket adunk át) az operációs rendszer visszaadja a jelzés eddigi kezelésének módjára vonatkozó információkat. Az alábbi példában a SIGHUP jelzéshez rendeljük a "jelzeskezelo" függvényt, amely egyszerűen kiírja a folyamat azonosítóját illetve a kapott jelzés kódját:
#include<signal.h>
void
jelzeskezelo(int sig) // A
jelzéskezelő függvény melyet hozzárendelünk egy jelzéshez
{
printf("%d
folyamat %d jeletkaptam\n",getpid(),sig);
}
int main()
{
struct sigaction
newAction, oldAction; // deklaráljuk
a szükséges változókat
newAction.sa_handler=jelzeskezelo; // beállítjuk a sigaction típusú
struktúrában a jelzéshez rendelni kívánt függvényt
if
(sigaction(SIGHUP, &newAction, &oldAction)<0) // hozzárendeljük a SIGHUP jelzéshez a
struktúra által leírt viselkedést
perror("sigaction
hiba \n"); // hiba
esetén perror függvényhívással jelezzük ezt.
pause(); // várakozunk
egy jelzésre
return 0;
}
maszkolt jelzések
A POSIX szabvány definiálja a maszkolt jelzések
fogalmát: ez egy folyamatonként nyilvántartott attribútum,
mely tartalmazza az összes olyan jelzést, amelyeket a folyamat blokkolt, vagyis
azokat a jelzéseket, amelyeknek a kiváltását a folyamat megtiltotta (az
operációs rendszernek).
Természetesen egy folyamatnak küldhetnek olyan jelzéseket, amiket az éppen blokkolt, de azoknak a kiváltásával az operációs rendszer várni fog a blokkolás befejezéséig. Az ilyen jelzéseket, amiket egy folyamat blokkol, de küldött már neki valaki: várakozó jelzéseknek is nevezik.
Ha egy jelzéskezelő függvény van az sa_handler komponensben, akkor az
sa_mask komponens a jelzéskezelő függvény futása alatt a még maszkolni kívánt
jelzések halmazát kell, hogy tartalmazza (vagyis a jelzéskezelő futásakor a
maszkolt jelzések halmaza ki fog bővülni az sa_mask halmazban megadott
jelzésekkel, a jelzéskezelő lefutásának végéig). Az éppen kiváltott jelzés
automatikusan belekerül az sa_mask halmazba, így azt nem kell beletenni abba.
Az előző példában ha a SIGHUP jelzés kezelése esetén szeretnénk a
SIGINT és SIGFPE jelzések blokkolását is a következőképpen kell
módosítanunk a sigaction struktúra tartalmát:
sigset_t sHalmaz; // bejelentjük
a blokkolni kívánt jeleket tartalmazó változót (jelhalmaz)
sigemptyset(&sHalmaz); // beállítjuk
a jelhalmazt üresre
sigaddset(&sHalmaz,SIGINT) // hozzáadjuk
a jelhalmazhoz egyenként a SIGINT és SIGFPE jelzéseket
sigaddset(&sHalmaz,SIGFPE);
newAction.sa_mask = sHalmaz; // a
sigaction struktúra sa_mask mezőjének megfeleltetjük a fent létrehozott
változót
sigprocmask
Lehetőség van az éppen maszkolt jelzések halmazának lekérdezésére és
módosítására is: erre használható a sigprocmask() rendszerhívás. Ennek
prototípusa a következő:
int sigprocmask(int hogyan, const sigset_t *newset, sigset_t *oldset);
Az első argumentum értéke háromféle lehet: vagy SIG_BLOCK vagy SIG_UNBLOCK
vagy pedig SIG_SETMASK. SIG_BLOCK érték esetén a második (newset)
argumentumban levo jelzések hozzáadódnak a maszkolt jelzések halmazához.
SIG_UNBLOCK esetén a második argumentumban levő jelzések ki lesznek véve a
maszkolt jelzések halmazából, míg a SIG_SETMASK esetén a maszkolt jelzések
halmazát newset-re állítja az operációs rendszer, az addigi tartalmától
függetlenül. Ha a newset argumentum értéke NULL, akkor a maszkolt jelzések halmaza
nem módosul. Ha az oldset argumentum értéke nem NULL, akkor az általa kijelölt
helyre az operációs rendszer eltárolja az éppen maszkolt jelzések halmazát. Az
előző példa esetében írjuk át a jelzéskezelő függvényt úgy, hogy
kérdezze le a blokkolt jelzések halmazát majd vonja ki belőlük az
aktuálisan kapott jelzést:
void jelzeskezelo(int sig) // A jelzéskezelő függvény, melyet
hozzárendelünk egy jelzéshez
{
printf("%d
folyamat %d jelet kaptam\n",getpid(),sig);
sigset_t sHalmaz; // bejelentjük a jeleket tartalmazó változót (jelhalmaz)
sigprocmask(SIG_BLOCK,NULL,
&sHalmaz); // a harmadik
argumentumba betöltődnek az aktuálisan blokkolt jelzések
printf(“A jelenleg
blokkolt jelzések halmaza:
\n [“);
for (i=1;i<32;i++) {
if (sigismember(&sHalmaz,i))
printf(“%d, “); // a sigismember
függvénnyel tesztlejük és kiiratjuk a blokkolt jelzéseket
}
printf(“ ]\n”);
sigemptyset(&sHalmaz); // kiüresítjük a
jelzéshalmazt
sigaddset(&sHalmaz,sig); // hozzáadjuk az
aktuális jelzést (a sig paraméteren keresztül kapja ezt a jelzéskezelő)
sigprocmask(SIG_UNBLOCK,&sHalmaz,
NULL); // kivonjuk az aktuálisan
blokkolt jelzések halmazából
}
A POSIX definálja még a sigpending() illetve a sigsuspend()
rendszerhívásokat is: ez előbbivel egy folyamat lekérdezheti a számára küldött,
de várakozó jelzések halmazát; míg a sigsuspend() függvénnyel a maszkolt
jelzések halmaza módosítható, majd az operációs rendszer a folyamat futását
felfüggeszti addig, amíg legközelebb egy jelzést nem kap.
módosítók
Az sa_flags a jelzés-kezelés folyamatát
befolyásolja, és a POSIX többek között az SA_NOCLDSTOP flaget definiálja
a következőképpen: ha ez be van állítva, akkor az operációs rendszer nem fog
SIGCHLD jelzést generálni olyankor, amikor a folyamat valamely
gyermek-folyamatának felfüggesztődik vagy tovább folytatódik a futása.
A továbbiakban lássunk egy példát a SA_SIGINFO flag használatára, melynek
segítségével lehetőségünk nyílik lekérdezni a kapott jel küldési
körülményeire vonatkozó információkat. A példában konkrétan a jelt küldő
folyamat azonosítóját és az ezen azonosítóval rendelkező folyamatot
futtató felhasználó nevét fogjuk lekérdezni:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<pwd.h>
#include<sys/types.h>
void fuggveny(int jel,siginfo_t *info, void* context) // A jelzéskezelő függvény, melyet
hozzárendelünk egy jelzéshez
{
int folyamatazonosito = info->si_pid; // a siginfo_t struktúra si_pid mezője megadja a
jelet küldő folyamat pid-jét
int felhasznaloID= info->si_uid; // a siginfo_t struktúra si_uid mezője megadja
a jelet küldő folyamat felhasználó azonosítóját
printf("%d jelet kaptam: %d folyamattol \n",jel,
folyamatazonosito);
struct passwd *p; // a
felhasználóra vonatkozó információkat tartalmazó struktúra
p = getpwuid(info->si_uid); // és a getpwuid függvény hívása
printf("Corresponding username: %s \n",p->pw_gecos);
return;
}
int main()
{
struct sigaction
newAction,oldAction;
newAction.sa_sigaction = fuggveny;
newAction.sa_flags = SA_SIGINFO; // a
SA_SIGINFO flag beállítása a sigaction hívás előtt
sigaction(SIGINT,&newAction,&oldAction);
pause();
return 0 ;
}
Részletesebb információ, illetve számos példa található itt:
http://www.gnu.org/software/libc/manual/html_node/Signal-Handling.html#Signal-Handling
Csővezeték (pipe)
Copyright
(C) Buzogány László, 2002
About