Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\n_prev.gif  Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\n_up.gif  Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\n_next.gif  Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\n_home.gif

Jelzések (signals)

Azaz a folyamatok informálása előre definiált jelek segítségével...


 

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\n_link.gif  Definíció

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\n_link.gif  Jelzések kezelése (hagyományos)

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_link.gif  signal

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_link.gif  kill, raise

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_link.gif  alarm, pause

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_link.gif  setjmp, longjmp

 Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\n_link.gif  POSIX kiegészítések (megbízható jelzések)

 


 

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\n_link_p.gif  Folyamatokkal végzett műveletek

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\n_book.gif  Definíció

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.

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\n_book.gif  Jelzések kezelése (hagyományos)

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_book.gif  signal

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.

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_book.gif  kill, raise

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));
}

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_book.gif  alarm, pause

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.

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_book.gif  setjmp, longjmp

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);
}

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\n_book.gif  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;

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_book.gif  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.

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_book.gif  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.

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_book.gif  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.

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_book.gif  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.

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_book.gif  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.

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_book.gif  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;
}

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_book.gif  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

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_book.gif  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.

 Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\g_book.gif  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

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\buttons\n_link_n.gif  Csővezeték (pipe)

 

 


 

Copyright (C) Buzogány László, 2002

Leírás: Leírás: Leírás: Leírás: Leírás: D:\!SO2\html\art\simple.gif

About