Signály Roury Další možnosti Závěr Meziprocesová komunikace (IPC) Tématicky zaměřený vývoj aplikací v jazyce C skupina Systémové programování – Linux Jiří Novosad Fakulta informatiky Masarykova univerzita novosad@fi.muni.cz Brno, 15. 10. 2014 J. Novosad IPC Brno, 15. 10. 2014 1 / 22 Signály Roury Další možnosti Závěr Signály principy a práce se signály J. Novosad IPC Brno, 15. 10. 2014 2 / 22 Signály Roury Další možnosti Závěr Signály #include princip přerušení – událostmi řízený program příchod signálu → přerušení činnosti → obsloužení signálu → návrat k předchozí práci (nebo také ne) některé signály lze ignorovat, některé blokovat, pro většinu lze měnit reakce na ně – výjimkami jsou SIGKILL a SIGSTOP obyčejné signály vs. real-time (spolehlivé) signály nikdy nepoužívejte přímo hodnoty, u real-time signálů SIGRTMIN+n – SIGRTMAX seznam signálů: man 7 signal, např.: SIGTERM "Termination" - signál ukončení SIGKILL "Kill" - signál pro nepodmíněné ukončení SIGSEGV Odkaz na nepřípustnou adresu v paměti SIGUSR1 Signál definovaný uživatelem ... J. Novosad IPC Brno, 15. 10. 2014 3 / 22 Signály Roury Další možnosti Závěr Posílání signálů #include #include int kill(pid_t pid, int sig); signály lze posílat nejen konkrétnímu procesu, ale i skupině procesů, nebo všem běžícím procesům zaslání signálu je omezeno oprávněním uživatele int raise(int sig); signály může proces posílat i sám sobě – mechanismus výjimek int sigqueue(pid_t pid, int sig, const union sigval value); pro real-time signály indikuje, zda se podařilo vložit signál do fronty navíc u všech signálů umožňuje zaslat procesu i data (int nebo ukazatel) J. Novosad IPC Brno, 15. 10. 2014 4 / 22 Signály Roury Další možnosti Závěr Reakce na signály #include sighandler_t signal(int signum, sighandler_t handler); int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); funkci signal() používejte jen pro nastavení handleru na SIG_IGN nebo SIG_DFL pro vlastní handlery používejte výhradně funkci sigaction() handler musí být co nejjednodušší pokud už měníte globální proměnnou, deklarujte ji jako volatile, ideálně typu sig_atomic_t i handler může být přerušen signálem – pokud je to nutné, blokujte signály (viz. dále) J. Novosad IPC Brno, 15. 10. 2014 5 / 22 Signály Roury Další možnosti Závěr Příklad – sigaction() volatile sig_atomic_t done = 0; void my_handler(int sig) { done = 1; } int main () { struct sigaction action; /* action structure for specific signal */ /* establish the signal handler */ sigemptyset(&action.sa_mask); action.sa_flags = 0; action.sa_handler = my_handler; sigaction(SIGTERM, &action, NULL); sigaction(SIGINT, &action, NULL); while (!done) { /* do some work */ sleep(1); } printf("cleaning up\n"); /* close files, free memory, write output, ... */ return 0; } J. Novosad IPC Brno, 15. 10. 2014 6 / 22 Signály Roury Další možnosti Závěr Blokování signálů Umožňuje zajistit přijetí signálu až v určitý okamžik (odložení příjmu signálu). sigprocmask(SIG_BLOCK,...) sigprocmask(SIG_UNBLOCK,...) signál obsluha signálu SIGKILL a SIGSTOP nelze blokovat. #include int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); int sigpending(sigset_t *set); Operace s maskou signálů viz. man 3 sigsetops J. Novosad IPC Brno, 15. 10. 2014 7 / 22 Signály Roury Další možnosti Závěr Synchronní doručení signálů Pokud je třeba signály používat intenzivněji, stojí za zvážení jejich synchronní použítí. Funkce sigwait(), sigwaitinfo(), sigsuspend() Pro skutečně synchronní chování by signály, na které se čeká, měly být nejříve vymaskovány – zabrání se doručení signálu před dokončením volání funkce. J. Novosad IPC Brno, 15. 10. 2014 8 / 22 Signály Roury Další možnosti Závěr Poznámky k signálům Signály jsou drahé (asynchronní přerušení) → přemýšlejte, zda je signál nejvhodnějším řešením. Kvůli optimalizaci kompilátoru deklarujte globální proměnné jako volatile. V obslužné rutině provádějte jen to nejnutnější – žádné zapisování do souborů, alokace paměti apod. Async-signal-safe functions (man 7 signal) C99: jen nastavení globální proměnné typu sig_atomic_t Pozor na errno – signály jsou asynchronní! I obslužná rutina může být přerušena signálem. J. Novosad IPC Brno, 15. 10. 2014 9 / 22 Signály Roury Další možnosti Závěr úkol Napište program, který po doručení (konkrétního, výběr je na vás) signálu zapíše do syslogu informaci o zpracování signálu J. Novosad IPC Brno, 15. 10. 2014 10 / 22 Signály Roury Další možnosti Závěr Roury anonymní a pojmenované roury J. Novosad IPC Brno, 15. 10. 2014 11 / 22 Signály Roury Další možnosti Závěr Roury $ ls | less někdy se setkáte i s pojmem datová kolona jednosměrný proud bajtů mezi dvěma konci roury roura je dvojice file deskriptorů (int pipefd[2]) – pipefd[0] pro čtení a pipefd[1] pro zápis velikost roury je omezená (65536 bajtů) nepojmenované roury jsou určeny pouze pro příbuzné procesy #include int pipe(int pipefd[2]); J. Novosad IPC Brno, 15. 10. 2014 12 / 22 Signály Roury Další možnosti Závěr Roury $ ls | less někdy se setkáte i s pojmem datová kolona jednosměrný proud bajtů mezi dvěma konci roury roura je dvojice file deskriptorů (int pipefd[2]) – pipefd[0] pro čtení a pipefd[1] pro zápis velikost roury je omezená (65536 bajtů) nepojmenované roury jsou určeny pouze pro příbuzné procesy #include int pipe(int pipefd[2]); často používáno v kombinaci s funkcí dup2() #include int dup2(int oldfd, int newfd); J. Novosad IPC Brno, 15. 10. 2014 12 / 22 Signály Roury Další možnosti Závěr Roury $ ls | less někdy se setkáte i s pojmem datová kolona jednosměrný proud bajtů mezi dvěma konci roury roura je dvojice file deskriptorů (int pipefd[2]) – pipefd[0] pro čtení a pipefd[1] pro zápis velikost roury je omezená (65536 bajtů) nepojmenované roury jsou určeny pouze pro příbuzné procesy #include int pipe(int pipefd[2]); často používáno v kombinaci s funkcí dup2() #include int dup2(int oldfd, int newfd); zjednodušené použití nepojmenovaných rour – funkce popen() a pclose(), cílový proces je spouštěn pomocí shellu → značná režie navíc (+ bezpečnost!) J. Novosad IPC Brno, 15. 10. 2014 12 / 22 Signály Roury Další možnosti Závěr Příklad – pipe() I int main(int argc, char *argv[]) { int pfd[2]; /* pipe */ pid_t child; /* child’s PID */ char buf; /* char buffer */ if (pipe(pfd) == -1) { perror("creating pipe failed"); return 1; } child = fork(); if (child == -1) { perror("fork() failed"); return 1; } else if (child == 0) { /* child process - reader */ close(pfd[1]); /* Close unused write end */ while (read(pfd[0], &buf, 1) > 0) { write(STDOUT_FILENO, &buf, 1); } write(STDOUT_FILENO, "\n", 1); close(pfd[0]); } else { /* Parent writes string to pipe */ close(pfd[0]); /* Close unused read end */ write(pfd[1], "Ahoj!", strlen("Ahoj!")); close(pfd[1]); /* Reader will see EOF */ wait(NULL); /* Wait for child */ } return 0; } J. Novosad IPC Brno, 15. 10. 2014 13 / 22 Signály Roury Další možnosti Závěr Příklad – pipe() II int main(int argc, char *argv[]) { ... if (pipe(pfd) == -1) { perror("pipe() failed"); return 1; } child = fork(); if (child == -1) { perror("fork() failed"); return 1; } else if (child == 0) { /* child process - sender */ close(pfd[0]); /* Close unused read end */ if (dup2(pfd[1], STDOUT_FILENO) == -1) { perror("creating pipe failed"); return 1; } execlp("ls", "ls", "-l", NULL); perror("exec failed"); return 1; } else { /* Parent - reader */ close(pfd[1]); /* Close unused write end */ while (read(pfd[0], &buf, 1) > 0) write(my_file_descriptor, &buf, 1); write(my_file_descriptor, "\n", 1); close(pfd[0]); wait(NULL); /* Wait for child */ } return 0; } J. Novosad IPC Brno, 15. 10. 2014 14 / 22 Signály Roury Další možnosti Závěr úkol Napodobte chování shellu při řetězení aplikací pomocí datových kolon (|) Aplikace dostane jako parametry řetezce představující příkazy ke spuštění – nezapoměňte, že součástí příkazu mohou být parametry spouštěného programu Oddělovačem příkazů bude dvojtečka (:) Vaše aplikace pak zařídí spuštění zadaných příkazů a propojení standardního výstupu první aplikace se standardním vstupem druhé pomocí volání dup2(2). Příklad volání: $ echo -e "Ahoj\nPomoc" | ./myshell tr ’[a-z]’ ’[A-Z]’ : sort -r POMOC AHOJ J. Novosad IPC Brno, 15. 10. 2014 15 / 22 Signály Roury Další možnosti Závěr Pojmenované roury (FIFO) nepojmenované roury jsou určeny pouze pro příbuzné procesy co když ale potřebujeme propojit nepříbuzné procesy? J. Novosad IPC Brno, 15. 10. 2014 16 / 22 Signály Roury Další možnosti Závěr Pojmenované roury (FIFO) nepojmenované roury jsou určeny pouze pro příbuzné procesy co když ale potřebujeme propojit nepříbuzné procesy? FIFO alias pojmenovaná roura součást souborového systému – pouze jako referenční bod pro přístup procesů, do souborového systému se nic nezapisuje je možné nastavit přístupová práva jako kterémukoliv jinému souboru pro komunikaci je nutné, aby oba konce roury byly otevřené s rourou se pak pracuje jako se souborem – open(), read(), write(), close(), unlink() #include #include int mkfifo(const char *pathname, mode_t mode); J. Novosad IPC Brno, 15. 10. 2014 16 / 22 Signály Roury Další možnosti Závěr úkol stejnou úlohu jako pro nepojmenované roury implementujte pomocí mkfifo() J. Novosad IPC Brno, 15. 10. 2014 17 / 22 Signály Roury Další možnosti Závěr Další možnost další možnosti IPC, kterým se budeme věnovat v příštích cvičeních J. Novosad IPC Brno, 15. 10. 2014 18 / 22 Signály Roury Další možnosti Závěr Další možnosti IPC sdílená paměť dva či více procesů přistupuje ke sdílenému paměťovému místu žádné kopírování dat, synchronizaci přístupu zajišťují procesy jeden z procesů paměť alokuje, ostatní se k alokovanému segmentu připojují J. Novosad IPC Brno, 15. 10. 2014 19 / 22 Signály Roury Další možnosti Závěr Další možnosti IPC sdílená paměť dva či více procesů přistupuje ke sdílenému paměťovému místu žádné kopírování dat, synchronizaci přístupu zajišťují procesy jeden z procesů paměť alokuje, ostatní se k alokovanému segmentu připojují sockety – síťová rozhraní obousměrný komunikační kanál komunikace mezi procesy běžícími na stejném počítači, ale i pro komunikaci s procesem na jiném počítači spojovaná komunikace (model klient-server) vs. datagramová komunikace J. Novosad IPC Brno, 15. 10. 2014 19 / 22 Signály Roury Další možnosti Závěr Závěr domácí úkoly a zdroje J. Novosad IPC Brno, 15. 10. 2014 20 / 22 Signály Roury Další možnosti Závěr Domácí úkol vytvořte systémového daemona1, který přijímá signály prvním parametrem daemona je název souboru, do kterého bude zapisovat události ve tvaru