Jelzések és kezelésük

Hasonló dokumentumok
UNIX folyamatok kommunikációja

Operációs rendszerek. 4. gyakorlat: Szignálok küldése, kezelése

OPERÁCIÓS RENDSZEREK. A mai program. Hiba és eseménykezelés. Alapfogalmak. Eseménykezelés, szignálozás

UNIX folyamatok kommunikációja

OPERÁCIÓS RENDSZEREK 1. PROCESSZKEZELÉS

UNIX: folyamatok kommunikációja

Vezérlési szerkezetek

Matlab alapok. Baran Ágnes. Baran Ágnes Matlab alapok Elágazások, függvények 1 / 15

Operációs rendszerek 1.

A PROGAMOZÁS ALAPJAI 1. Függvény mint függvény paramétere. Függvény mint függvény paramétere. Függvény mint függvény paramétere

7. Laboratóriumi gyakorlat: Vezérlési szerkezetek II.

Programozás I. 5. Előadás: Függvények

Programozás alapjai gyakorlat. 4. gyakorlat Konstansok, tömbök, stringek

S z á m í t ó g é p e s a l a p i s m e r e t e k

Operációs rendszerek

Járműfedélzeti rendszerek II. 2. előadás Dr. Bécsi Tamás

BASH script programozás II. Vezérlési szerkezetek

1. Bevezetés szeptember 9. BME Fizika Intézet. Szám. szim. labor ea. Tőke Csaba. Tudnivalók. feladat. Tematika. Moodle Házi feladatok

Programozás C nyelven FELÜLNÉZETBŐL elhullatott MORZSÁK. Sapientia EMTE

1. Alapok. #!/bin/bash

Programozás 6. Dr. Iványi Péter

A programozás alapjai

Concurrency in Swing

3. Osztályok II. Programozás II

Programozás C- és Matlab nyelven C programozás kurzus BMEKOKAM603 Függvények. Dr. Bécsi Tamás 6. Előadás

A programozás alapjai 1 Rekurzió

Architektúra, megszakítási rendszerek

Kivételek, kivételkezelés a C++ nyelvben

Imperatív programozás

Programozás C++ -ban 2007/7

Programozás II. 2. gyakorlat Áttérés C-ről C++-ra

Programozás alapjai C nyelv 5. gyakorlat. Írjunk ki fordítva! Írjunk ki fordítva! (3)

HORVÁTH ZSÓFIA 1. Beadandó feladat (HOZSAAI.ELTE) ápr 7. 8-as csoport

Vezérlési szerkezetek. Szelekció Ciklusok

6. fejezet: Ciklusok

Informatika terméktervezőknek

8. gyakorlat Pointerek, dinamikus memóriakezelés

A MATLAB alapjai. Kezdő lépések. Változók. Aktuális mappa Parancs ablak. Előzmények. Részei. Atomerőművek üzemtana

PHP alapjai, bevezetés. Vincze Dávid Miskolci Egyetem, IIT

Mechatronika és mikroszámítógépek 2017/2018 I. félév. Bevezetés a C nyelvbe

Objektum Orientált Programozás. 11. Kivételkezelés 44/1B IT MAN

Task-alapú párhuzamosítás C# környezetben

Pénzügyi algoritmusok

Internet programozása. 3. előadás

Számítástechnika I. BMEKOKAA152 BMEKOKAA119 Infokommunikáció I. BMEKOKAA606. Dr. Bécsi Tamás 3. előadás

Programozás C nyelven (3. ELŐADÁS) Sapientia EMTE

Függvény pointer. Feladat: Egy tömbben soroljunk fel függvényeket, és hívjuk meg valahányszor.

A C programozási nyelv VI. Parancssori argumentumok File kezelés

Socket programozás Példák

Bevezetés a programozásba I 10. gyakorlat. C++: alprogramok deklarációja és paraméterátadása

A C programozási nyelv II. Utasítások. A függvény.

Óbudai Egyetem. C programozási nyelv

és az instanceof operátor

Programozás C++ -ban

Java VIII. Az interfacei. és az instanceof operátor. Az interfészről általában. Interfészek JAVA-ban. Krizsán Zoltán

Powershell 2. gyakorlat

Operációs rendszerek. 9. gyakorlat. Reguláris kifejezések - alapok, BASH UNIVERSITAS SCIENTIARUM SZEGEDIENSIS UNIVERSITY OF SZEGED

Programozás alapjai gyakorlat. 2. gyakorlat C alapok

Programozás C és C++ -ban

C++ programozási nyelv

Linux alapok. Parancsok általános alakja parancs kapcsolók paraméterek

Programozás II. ATM példa Dr. Iványi Péter

A függvény kód szekvenciáját kapcsos zárójelek közt definiáljuk, a { } -ek közti részt a Bash héj kód blokknak (code block) nevezi.

Programozás alapjai C nyelv 4. gyakorlat. Mit tudunk már? Feltételes operátor (?:) Típus fogalma char, int, float, double

AWK programozás, minták, vezérlési szerkezetek

Programozás BMEKOKAA146. Dr. Bécsi Tamás 3. előadás

Uniprogramozás. várakozás. várakozás. Program A. Idő. A programnak várakoznia kell az I/Outasítások végrehajtására mielőtt továbbfuthatna

/* Az iter függvény meghívása és a visszatérő érték átadása a gyok változóba */ gyok = iter( n, a, e ) ;

Programozás alapjai 5. gyakorlat Vezérlési szerkezetek egymásba ágyazása

SQL*Plus. Felhasználók: SYS: rendszergazda SCOTT: demonstrációs adatbázis, táblái: EMP (dolgozó), DEPT (osztály) "közönséges" felhasználók

Mintavételes szabályozás mikrovezérlő segítségével

Osztályok. 4. gyakorlat

Processzusok (Processes), Szálak (Threads), Kommunikáció (IPC, Inter-Process Communication)

C programozási nyelv

C programozás. 1 óra Bevezetés

InFo-Tech emelt díjas SMS szolgáltatás. kommunikációs protokollja. Ver.: 2.1

Bevezetés a Python programozási nyelvbe

4. Laborgyakorlat. A fájlokról ezeket az adatokat, a fájlrendszer tárolja. Számunkra az 1, 3, 4. oszlopok lesznek az érdekesek.

Folyamatok követése. Tartalom. 1. Folyamatok

Szerző. Varga Péter ETR azonosító: VAPQAAI.ELTE cím: Név: Kurzuskód:

C programozási nyelv Pointerek, tömbök, pointer aritmetika

OOP. #6 (VMT és DMT) v :33:00. Eszterházy Károly Főiskola Információtechnológia tsz. Hernyák Zoltán adj.

Mutatók és címek (ism.) Programozás alapjai C nyelv 8. gyakorlat. Indirekció (ism) Néhány dolog érthetőbb (ism.) Változók a memóriában

A rendszer célja. Funkciók

Java és web programozás

Mit tudunk már? Programozás alapjai C nyelv 4. gyakorlat. Legnagyobb elem keresése. Feltételes operátor (?:) Legnagyobb elem keresése (3)

Operációs rendszerek. 11. gyakorlat. AWK - szintaxis, vezérlési szerkezetek UNIVERSITAS SCIENTIARUM SZEGEDIENSIS UNIVERSITY OF SZEGED

Programozás I. gyakorlat

Eseménykezelés. Aszinkron kommunikáció

A feladat lényege egy felhasználói típusnak a zsák típusnak a megvalósítása.

Programozás I gyakorlat. 10. Stringek, mutatók

Az osztályok csomagokba vannak rendezve, minden csomag tetszőleges. Könyvtárhierarhiát fed: Pl.: java/util/scanner.java

Dr. Schuster György október 14.

SZERVER OLDALI JAVASCRIPT. 3. hét Javascript nyelvi elemek

Operációs rendszerek. 6. gyakorlat: Processzusok közti kommunikáció (osztott memória, üzenetsor)

Processzusok (Processes), Szálak (Threads), Kommunikáció (IPC, Inter-Process Communication)

Szerző Lővei Péter LOPSAAI.ELTE IP-08PAEG/25 Daiki Tennó

Hírlevél küldés kézikönyv

HP LaserJet M9040/9050 MFP Gyors referencia útmutató

BATCH Programozás. Tartalomjegyzék

Átírás:

Jelzések és kezelésük A jelzés egy nagyon egyszerű kommunikációs forma, az információ egy speciális egész szám, amelyet úgy is szoktak nevezni, hogy a jelzés típusa/értéke (signum). Jelzést általában a rendszer szokott küldeni valamilyen hiba vagy esemény bekövetkezésekor (pl. érvénytelen memóriahivatkozás, 0-val osztás, gyerek folyamat terminált stb.), de jelzést küldhetnek a folyamatok is egymásnak. Jelzések segítségével kommunikálhatnak egymással a folyamatok, vagy kommunikációjuk összehangolására, szinkronizációra is fel tudják használni a jelzéseket. Ha egy folyamat jelzést kap, akkor a következőképpen reagálhat: 1. Elkapja a jelzést és végrehajt egy jelzéskezelő függvényt (kivéve SIGKILL és SIGSTOP) 2. Figyelmen kívül hagyja (ignore) a jelzést (kivéve SIGKILL és SIGSTOP). 3. A jelzéshez tartozó alapértelmezett (default) művelet hajtódik végre. A jelzésértékekhez SIG kezdetű neveket definiáltak (makro), amely elnevezések utalnak a felhasználási módjukra (pl.: SIGTERM = terminálj). A jelzésértékekről, alapértelmezett működésükről a signal(7)-es man oldalon tájékozódhatunk, lássunk ebből egy részletet: The entries in the "Action" column of the table specify the default action for the signal, as follows: Term Default action is to terminate the process. Ign Default action is to ignore the signal. Core Default action is to terminate the process and dump core. Stop Default action is to stop the process. First the signals described in the original POSIX.1 standard. 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 poin 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 process SIGTSTP 18,20,24 Stop Stop typed at tty SIGTTIN 21,21,26 Stop tty input for background process SIGTTOU 22,22,27 Stop tty output for background process A fenti táblázatból látszik, hogy ha a SIGTERM jelzést, nem kezeljük le, és nem hagyatjuk figyelmen kívül (ignore), akkor alapértelmezésként (Term) a folyamatot terminálja a rendszert, míg a SIGCHLD jelzés figyelmen kívül lesz hagyva (Ign) ugyanilyen esetben. A SIGKILL és SIGSTOP jelzések esetén, mindig az alapértelmezett működés fog életbe lépni, nem lehet kezelni vagy figyelmen kívül hagyni őket.

A jelzéskezelő függvényt a rendszer hívja meg a jelzés beérkezésekor, ezért meghatározott a függvény prototípusa. Egy paramétere van, amely megegyezik az elkapott jelzés értékével és nincs visszatérési értéke. Tehát a jelzéskezelő függvények típusa: void f(int signum). A signal nevű függvénnyel tudjuk beállítani, hogy adott jelzésérték esetén a folyamat miként reagáljon a jelzésre. A függvény meghívásakor a rendszer eltárolja, hogy mi a teendő, ha a folyamat a későbbiekben kap egy adott jelzést. Megadhatjuk a signal függvénynek a jelzéskezelő függvény címét, ha szeretnénk elkapni és kezelni a jelzést. #include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); Paraméterek: signum: a jelzés értéke handler: a következők egyike lehet a jelzéskezelő függvény címe (ha elkapni és kezelni akarjuk) SIG_IGN: figyelmen kívül hagyás (ignore) SIG_DFL: alapértelmezett műveletet (default) Visszatérési érték: jelzéskezelő függvény címe vagy hiba esetén SIG_ERR Hibák: EINVAL: érvénytelen jelzésérték A következő példában megpróbálunk szegmens hibát előidézni, és elkapni a rendszer által küldött SIGSEGV jelzést. #include<stdlib.h> A jelzéskezelő függvény switch(signum){ case SIGSEGV: puts("szegmens hiba"); break; default: puts("egyéb"); exit(1); A jelzéskezelő függvény bejegyzése. Első paraméter a jelzésérték, második a jelzéskezelő címe. int *p; signal(sigsegv,kezelo); p[3] = 3; Itt probálunk szegmenshibát okozni. A példában jelzéskezelő eljárás egy exit függvényt hívott, amely a folyamatot terminálta. Ha a jelzéskezelő eljárás nem kiugrással ér véget (return), akkor oda térünk vissza, ahonnan kiugrottunk (néhány függvénytől eltekintve majdnem oda, lásd később), tehát a szegmenshibát okozó memóriahivatkozáshoz. Ezt megismételve persze újra szegmens hibát kapunk. Ha többször is meghívjuk a signal függvényt ugyan azzal a jelzés típussal, akkor a rendszer az utolsó hibátlan hívás eredményét tárolja el. Nézzünk erre egy példát, amelyben kipróbáljuk a SIGINT jelzés esetén

alkalmazható háromféle működési módot. A Ctrl+C billentyű leütés hatására a parancsértelmező folyamat küld egy SIGINT jelzést a folyamatnak. #include<unistd.h> puts("signal kezelő"); Az indulás után a folyamat bejegyezteti a jelzéskezelő függvényt. Ha 5 másodpercen belül kapunk SIGINT jelzést, akkor a kezelő lefut, majd a folyamat folytatja a működést ott, ahol abbahagyta (majdnem ott, mivel nem várja ki a sleep által beállított 5 másodpercet). signal(sigint,kezelo); puts("sigint kezelés"); sleep(5); signal(sigint,sig_ign); puts("sigint ignore"); sleep(5); signal (SIGINT, SIG_DFL); puts("sigint default"); sleep(5); Ezután a SIG_IGN konstans segítségével tudatja a rendszerrel, hogy figyelmen kívül akarja hagyni a SIGINT jelzést. Az ezután leütött Ctrl+C billentyű valóban figyelmen kívül lesz hagyva. Végül a SIG_DFL konstans segítségével az alapértelmezett működésre váltunk. Ekkor leütött Ctrl+C billntyű hatására véget ér a folyamat. Blokkolt folyamatok esetén, ha megszakíthatatlan a blokkolás, akkor rendszer visszatartja a jelzést, amíg a folyamat nem lesz aktív, megszakítható blokkolás esetén a rendszer felébreszti a folyamatot, hogy kezelhesse a jelzést (pl.: beolvasás terminálról). Amikor egy jelzést kezelünk közben az illető jelzés blokkolva van, u.i. problémát okozhat, ha egy befejezetlen jelzéskezelés közben újra indulna a kezelő. Ez a blokkolás nem a figyelmen kívül hagyó művelet (SIG_IGN), hanem a rendszer a háttérben kezel egy blokkolt jelzések halmazát, amelyet a felhasználói folyamatok is elérhetnek, módosíthatnak (sigprocmask). A jelzéskezelő végrehajtása után a rendszer a törli az illető jelzés blokkolását. A blokkolás alatt beérkezett ugyanolyan jelzésekből egy darabot kap meg a folyamat a blokkolás feloldása után. A SIGKILL és SIGSTOP jelzéseket nem lehet blokkolni. Jelzéskezelő eljárások készítése során, a jelzéskezelőből való visszatérésre többféle szokásos módszer terjedt el: 1. A jelzéskezelő eljárásban kilépünk a programból. 2. A jelzéskezelő eljárás végrehajtása után visszatérünk oda, ahonnan kiugrottunk. 3. A jelzéskezelő eljárásból a folyamat egy korábbi pontjába ugrunk vissza. 1. A jelzéskezelő eljárásban kilépünk a programból. Ha a jelzés alapértelmezett működése a terminálás, akkor nem az exit függvénnyel szokás kilépni, hanem a folyamat átállítja a jelzéskezelést alapértelmezett működésre és küld magának (raise) egy jelzést. Ekkor a szülő folyamat értesül róla (return status), hogy (milyen) jelzés szakította meg a folyamatot.

#include<stdlib.h> Kilépés előtti tevékenységek (fájlok lezárása stb.) switch(signum){ case SIGSEGV: //KEZELÉS... signal(sigsegv,sig_dfl); raise(sigsegv); break; default: puts("egyéb"); int *p; signal(sigsegv,kezelo); p[3] = 3; Átállítás alapértelmezett jelzéskezelésre SIGSEGV jelzés küldés saját magának 2. A jelzéskezelő eljárás végrehajtása után visszatérünk oda, ahonnan kiugrottunk. Ha a jelzéskezelő eljárás nem a programból való kilépéssel ér véget (exit), akkor általában oda térünk vissza, ahonnan kiugrottunk. Vannak olyan függvények, amelyek végrehajtása közben beérkezett jelzés esetén, a függvény már nem folytatható és a függvény EINTR hibával ér véget. Ilyenkor a jelzéskezelő eljárás lefutása után, a végrehajtás a függvény után fog folytatódni. Ezen függvények hibalistájában általában szerepel az EINTR hiba (pl.: read, write, vagy a korábbi példában látott sleep függvény, amely a beérkezett jelzés hatására EINTR hibával véget ér és nem várja ki a megadott időtartamig még fennmaradó időt.) Ezeknél a függvényeknél (open, read, write stb.) általában azt szeretnénk, hogy a függvény újra végrehajtódjon. Ezt megtehetjük, hogy addig ismételjük ciklusban, amíg a sikertelen a végrehajtás, és a hiba oka EINTR. Erre szolgál a TEMP_FAILURE_RETRY makro: #ifndef TEMP_FAILURE_RETRY #define TEMP_FAILURE_RETRY(expression) \ ( extension \ ({ long int result; \ do result = (long int) (expression); \ while ( result == -1L && errno == EINTR); \ result; )) #endif Int main(){ TEMP_FAILURE_RETRY(read(fd,buffer,sizeof(buffer))); 3. A jelzéskezelő eljárásból a folyamat egy korábbi pontjába ugrunk vissza. Ezt megtehetjük a siglongjmp függvénnyel, amelynek segítségével visszaugorhatunk a sigsetjmp függvény által elmentett környezetbe. Olyan függvénybe persze nem léphetünk vissza, amelyik már véget ért.

A következő példában 0-val osztunk, aminek következtében SIGFPE jelzést kapunk. A jelzéskezelőben átállítjuk a változó értékét 1-re, majd visszaugrunk az osztás elé és újra elvégezzük. #include<stdlib.h> #include<setjmp.h> int x; sigjmp_buf a; switch(signum){ case SIGFPE: x=1; siglongjmp(a,1); break; default: puts("egyéb"); x=0; signal(sigfpe,kezelo); sigsetjmp(a,sigfpe); printf("1/x = %d\n",1/x); Átállítjuk x-et 1-re, majd ugrunk az a változóba mentett környezetbe. Itt mentjük el a környezetett, amikor először járunk itt, majd ide fogunk vissza ugrani. Amikor először járunk itt, akkor x=0, tehát nullával osztunk. Amikor másodszor, akkor x=1. (Ha longjmp-vel ugrunk ki a jelzéskezelőből, a jelzés blokkolását a rendszer nem oldja fel. Ezt nekünk kell megtenni a sigprocmask függvénnyel. A siglongjmp esetén erre nincs szükség.)

Jelzések küldése Jelzést küldeni a kill függvénnyel tudunk. Nem minden folyamatnak küldhetünk jelzést, a küldőnek rendszergazdaként kell futnia, vagy a küldő valódi felhasználó azonosítójának meg kell egyeznie a címzett felhasználó azonosítójával. #include <sys/types.h> #include <signal.h> int kill(pid_t pid, int signum); Paraméterek: pid: címzett folyamat(ok) meghatározására szolgáló paraméter ha pid>0: folyamat, amelynek azonosítója=pid ha pid=0: hívó folyamattal azonos csoportban (process group) lévők ha pid=-1: minden folyamat, akinek jogosult küldeni (kivéve init) ha pid<-1: olyan folyamatok, amelyek csoportazonosítója=-pid signum: a jelzés értéke ha signum=0, akkor nem küld szignált, hanem teszteli, hogy létezik-e a folyamat vagy a csoport (sikeres visszatérési érték esetén létezik) Visszatérési érték: siker esetén 0 hiba esetén -1 Hibák: EINVAL: érvénytelen szignál típus/érték EPERM: nincs jogosultságunk a szignált küldeni a folyamatnak ESRCH: a pid folyamat nem létezik A következő példában a standard inputról beolvasott pid-del rendelkező folyamatnak próbálunk meg SIGTERM szignált küldeni, majd a perror függvény segítségével meggyőződünk a sikerességéről. int pid; printf("pid: "); scanf("%d",&pid); kill(pid,sigterm); perror("kill"); Lássunk még néhány hasznos függvényt: raise: szignált küld a saját magának pause: szignál beérkezéséig várakozik sigprocmask: blokkolt szignálok kezelése sigaction: szignálok kezelése (többet tud, mint a signal függvény)

Javasolt feladatok: 1. Írjon programot, amely fork-kal létrehoz egy gyerek folyamatot. A gyerek folyamat másodpercenként írja ki a számokat 0-tól 10-ig, a szülő várjon 3 másodpercet, majd küldjön egy SIGTERM jelzést a gyereknek, majd újabb 3 másodperc múlva egy SIGKILL jelzést. A gyerek folyamat kezelje le a SIGTERM jelzést (írja ki, hogy "SIGTERM jelzést kaptam"). 2. Írjon programot, amely az indítási paraméterként (argv[1]) megadott nevű folyamatoknak küld SIGTERM jelzést. Összefoglalás: jelzéskezelő függvény: void kezelo(int signum); jelzéskezelő bejegyzése: signal jelzés küldése: kill Egy folyamat a jelzésre a következőképpen reagálhat: 1. Elkapja a jelzést és végrehajt egy jelzéskezelő függvényt. 2. Figyelmen kívül hagyja a jelzést (SIG_IGN). 3. A jelzéshez tartozó alapértelmezett művelet hajtódik végre (SIG_DFL). Az alapértelmezett működés a jelzés értékétől függ: term, ignore, stop, continue. A SIGKILL és SIGSTOP jelzések esetén, mindig az alapértelmezett működés fog életbe lépni, nem lehet kezelni, figyelmen kívül hagyni, és nem lehet blokkolni. A jelzéskezelő futása során az adott jelzés blokkolva van, nehogy a kezelő újrainduljon (ezt a blokkolási listát a folyamat maga is tudja kezelni a sigprocmask függvénnyel). Jelzéskezelő eljárások készítése során, a jelzéskezelőből való visszatérésre többféle szokásos módszer terjedt el: 1. A jelzéskezelő eljárásban kilépünk a programból. (exit helyett SIG_DFL-re visszaállunk, majd raise) 2. A jelzéskezelő eljárás végrehajtása után visszatérünk oda, ahonnan kiugrottunk (néhány függvény esetén ne feledkezzünk meg TEMP_FAILURE_RETRY makróról). 3. A jelzéskezelő eljárásból a folyamat egy korábbi pontjába ugrunk vissza (sigsetjmp, soglongjmp).