1 / 78

UNIX jesień/zima 2013

UNIX jesień/zima 2013. Wykład 7 Programowanie systemowe w Linux: Sygnały Kolejki komunikatów, Pamięć współdzielona, Semafory. dr inż. Wojciech Bieniecki Instytut Nauk Ekonomicznych i Informatyki http://wbieniec.kis.p.lodz.pl/pwsz. Metody komunikacji międzyprocesowej.

chul
Télécharger la présentation

UNIX jesień/zima 2013

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. UNIXjesień/zima 2013 Wykład 7 Programowanie systemowe w Linux: Sygnały Kolejki komunikatów, Pamięć współdzielona, Semafory dr inż. Wojciech Bieniecki Instytut Nauk Ekonomicznych i Informatyki http://wbieniec.kis.p.lodz.pl/pwsz

  2. Metody komunikacji międzyprocesowej Komunikacja międzyprocesowa (ang. Inter-ProcessCommunication — IPC) – sposoby komunikacji pomiędzy procesami systemu operacyjnego. Mechanizmy IPC opierają się na budowaniu w pamięci lub na dysku dynamicznych struktur, używanych do transmisji komunikatów pomiędzy procesami Lista metod IPC ta obejmuje: pliki i blokady – najprostsza i najstarsza forma IPC sygnały (ang. signals) – czasami znane jako przerwania programowe łącza nienazwane (ang. pipes) – znane też jako łącza komunikacyjne łącza nazwane (ang. namedpipes) – znane też jako nazwane łącza komunikacyjne kolejki komunikatów (ang. messagequeues) umożliwiają przekazywanie określonych porcji danych pamięć dzielona (ang. sharedmemory) umożliwiają współdzielenie kilku procesom tego samego fragmentu wirtualnej przestrzeni adresowej semafory (ang. semaphores) umożliwiają synchronizacje procesów w dostępie do współdzielonych zasobów (np. do pamięci współdzielonej) gniazda Uniksa (ang. Unix domainsockets) gniazda (ang. sockets) RPC (ang. RemoteProcedureCall) – zdalne wywoływanie procedur.

  3. Mechanizmy IPC Systemu V zdefiniowane w <sys/ipc.h>

  4. Mechanizmy IPC POSIX

  5. Wywołania systemowe …get Wywołania systemowe …get (msgget, shmget, semget) są stosowane do tworzenia nowych obiektów IPC, lub do uzyskania dostępu do obiektów istniejących. intmsgget(key_t key, intmsgflg) key_tkey – klucz (liczba long) umożliwiający generowanie identyfikatorów IPC. Procesy, poprzez podanie tej samej wartości klucza uzyskują dostęp do konkretnego mechanizmu IPC. Wartość klucza określamy samodzielnie, lub poprzez stałą IPC_PRIVATE, która spowoduje utworzenie obiektu IPC o niepowtarzalnej wartości identyfikatora (umożliwia komunikację jedynie procesów spokrewnionych) intmsgflg– znacznik komunikatu, określa prawa dostępu do tworzonego obiektu IPC. IPC_CREAT – zwraca identyfikator obiektu IPC, a jeżeli nie istniał – utworzenie obiektu. IPC_CREAT|IPC_EXCL spowoduje utworzenie obiektu, a jeżeli obiekt IPC dla danej wartości klucza już istnieje, wywołanie funkcji get zakończy się błędem. Dzięki połączeniu tych dwóch flag użytkownik posiada gwarancję, że jest on twórcą danego obiektu IPC. Funkcje …get zwracają wartości całkowitoliczbowe, nazywane identyfikatorami IPC, które identyfikują obiekty IPC. Od strony systemu operacyjnego identyfikator IPC jest indeksem w systemowej tablicy zawierającej struktury z danymi dotyczącymi uprawnień do obiektów IPC.

  6. Tworzenie obiektów IPC

  7. Wywołania systemowe …ctl Funkcje …ctl (msgctl, shmctl, semctl), używane są w celu przeprowadzenia operacji kontrolnych na obiektach IPC. Wywołania funkcji …ctl posiadają dwa argumenty wspólne: identyfikator obiektu IPC (otrzymany w wyniku wywołania odpowiedniej funkcji …get), oraz następujące stałe: • IPC_STAT – zwraca informację o stanie danego obiektu IPC • IPC_SET – zmienia właściciela, grupę i tryb obiektu IPC • IPC_RMID – usuwa obiekt IPC z systemu

  8. Dostęp do IPC z powłoki systemowej Na poziomie systemowym dane znajdujące się w obiekcie IPC pobiera się za pomocą polecenia ipcs. Informacje na temat obiektów: kolejek komunikatów, pamięci współdzielonej i semaforów otrzymamy stosując odpowiednio przełączniki -q, -m, -s. Dodatkowo przełącznik -b pozwala uzyskać informację nt. maksymalnego rozmiaru obiektów IPC, czyli ilości bajtów w kolejkach, rozmiarów segmentów pamięci współdzielonej i ilości semaforów w zestawach. ipcs -q <msgid> – informacja nt. kolejki komunikatów o identyfikatorze msgid ipcs -m <shmid> – informacja nt. segmentu pamięci współdzielonej o identyfikatorze shmid ipcs -s <semid> – informacja nt. zestawu semaforów o identyfikatorze semid obiekt IPC USUWAMY wykonując polecenie systemowe ipcrm: ipcrm -q <msgid> – usunięcie kolejki komunikatów o identyfikatorze msgid ipcrm -m <shmid> – usunięcie segmentu pamięci współdzielonej o identyfikatorze shmid ipcrm -s <semid> – usunięcie zestawu semaforów o identyfikatorze semid

  9. Sygnały Sygnałem nazywamy asynchroniczne zdarzenie skierowane do procesu. Otrzymanie sygnału przez proces oznacza, że pojawiło się jakieś zdarzenie wyjątkowe, wymagające natychmiastowej reakcji ze strony procesu. od innego procesu (np. SIGINT, SIGKILL) od jądra (np. SIGPIPE, SIGCHLD, SIGALRM) od jądra, ale przez wyjątek sprzętowy (np. SIGSEGV, SIGBUS) Reakcja procesu na otrzymany sygnał Wykonanie akcji domyślnej – najczęściej zakończenie procesu z ewentualnym zrzutem zawartości segmentów pamięci na dysk, czasami zignorowanie sygnału, Zignorowanie sygnału Przechwycenie sygnału tj. podjęcie akcji zdefiniowanej przez użytkownika

  10. Sygnały w systemie Unix Funkcje obsługujące sygnały zawarte są w bibliotece <signal.h>

  11. Sygnały czasu rzeczywistego W Posix dostępne są również sygnały czasu rzeczywistego (realtime, RT) W linuxie mają one wartości 34 – 64. Sygnały czasu rzeczywistego charakteryzują się tym, że: ● Sygnały są kolejkowane ● Wielokrotne wystąpienie tego samego sygnału nie powoduje „zlepiania” w jeden sygnał ● Gdy do kolejki trafiają wielokrotne, nieblokowane sygnały czasu rzeczywistego to sygnały z niższym numerem mają wyższy priorytet. ● Sygnały RT przekazują nie tylko nr sygnału ale także: strukturę siginfo_t oraz kontekst.

  12. Ważniejsze funkcje systemowe dla sygnałów

  13. Sygnały systemu Unix - funkcje Funkcja kill - wysłanie sygnału int kill(int pid, int signum) • pid – identyfikator procesu, do którego chcemy wysłać sygnał • pid > 0 oznacza proces o identyfikatorze pid, • pid == 0 oznacza grupę procesów do których należy proces wysyłający, • pid == -1 oznacza wszystkie procesy, których rzeczywisty identyfikator użytkownika • jest równy efektywnemu (obowiązującemu) identyfikatorowi procesu wysyłającego • sygnał, • pid < -1 oznacza procesy należące do grupy o identyfikatorze -pid. • signum – numer wysyłanego sygnału Wynik: -1 w przypadku błędu (szczegóły w errno) 0 OK.

  14. Sygnały systemu Unix - funkcje Funkcja signal - Określenie sposobu obsługi sygnału void (*signal(int signum, void (*f)()))() • signum – numer sygnału, którego obsługa ma zostać zmieniona • f – może obejmować jedną z trzech wartości: • SIG_DFL (wartość 0) – standardowa reakcja na sygnał • SIG_IGN (wartość 1) – ignorowanie sygnału • Wskaźnik do funkcji – wskaźnik na funkcję, która będzie uruchomiona w reakcji na sygnał Wynik: -1 w przypadku błędu (szczegóły w errno) Wskaźnik na poprzednią procedurę obsługi sygnału, lub SIG_IGN, SIG_DFL UWAGA 1: Nie można przechwytywać, ani ignorować sygnałów SIGKILL i SIGSTOP. UWAGA 2: Gdy w procesie macierzystym ustawiony jest tryb ignorowania sygnału SIGCLD to po wywołaniu funkcji exit przez proces potomny, proces zombi nie jest zachowywany i miejsce w tablicy procesów jest natychmiast zwalniane

  15. Sygnały systemu Unix - funkcje Funkcja pause – oczekiwanie na sygnał voidpause() Zawiesza wywołujący proces aż do chwili otrzymania dowolnego sygnału. Jeśli sygnał jest ignorowany przez proces, to funkcja pause też go ignoruje. Najczęściej sygnałem, którego oczekuje pause jest sygnał pobudki SIGALARM. Funkcja ALARM Funkcja wysyła sygnał SIGALARM po upływie czasu podanym przez użytkownika. unsigned alarm ( unsignedintsek ) Wynik: 0 w przypadku gdy alarm nie był wcześniej ustawiony liczba dodatnia – liczba sekund pozostała do wysłania sygnału gdy poprzednio ustawiono alarm, a sygnał nie został jeszcze wysłany

  16. Sygnały POSIX Procedura obsługi sygnału zdefiniowana jest następująco: voidfunc (intsigno, siginfo_t * info, void *context); gdzie signo numer sygnału info struktura: typedefstruct { intsi_signo; /*numer sygnału*/ intsi_errno; /* jeśli !=0 numer błędu związany z tym sygnałem*/ intsi_code; /*SI_{USER, QUEUE, TIMER, KERNEL, ASYNCIO, MESGQ, SIGIO, TKILL, ASYNCNL} */ union sigvalsi_value; } siginfo_t; contextkontekst zależny od implementacji.

  17. Generowanie sygnałów RT w POSIX Sygnały RT są generowane przez następujące zdarzenia i identyfikowane w polu si_code struktury siginfo_t: SI_ASYNCIO – sygnał został wygenerowany przez zakończenie asynchronicznego zlecenia wejścia wyjścia (funkcje z serjiaio_*) np drivery USB SI_MESGQ – sygnał wygenerowany przy umieszczeniu komunikatu w pustej kolejce komunikatów SI_QUEUE – sygnał wysłany przez funkcję sigqueue SI_TIMER – sygnał wygenerowany przez zakończenie pracy czasomierza ustawionego za pomocą timer_settim SI_USER – sygnał wysłany przez funkcję kill SI_TKILL – sygnał wysłany przez funkcję tkill SI_KERNEL – sygnał wysłany przez funkcję jądra SI_SIGIO – sygnał wysłany przez SIGIO

  18. Określanie procedury obsługi sygnału voidsigaction(intnumer_sygnalu, structsigaction* akcja, structsigaction* poprzednia_akcja); sa_handler – jeżeli sa_flags nie jest ustawione na SA_SIGINFO to jest to adres klasycznej funkcji obsługi sygnału. Możliwe też SIG_DFL, SIG_IGN dzięki którym przywracamy domyślną obsługę lub ignorujemy. structsigaction { void (*sa_handler)(int); sigset_tsa_mask; unsigned long sa_flags; void (*sa_sigaction)(int,siginfo_t, void*); void (*sa_restorer)(void); }; sa_mask – dodatkowe sygnały do zablokowania sa_flags – flagi sygnału SA_NOCLDSTOP – nie wysyłaj SIGCLD nawet gdy potomek się zakończy SA_NOCLDWAIT – nie twórz „zombie” po śmierci potomków. SA_SIGINFO – użyj trzyargumentowej funkcji obsługi sygnału sa_sigaction zamiast sa_handler SA_NOMASK – w czasie wykonywania sygnału nie jest on automatycznie blokowany SA_ONESHOT – Kiedy sygnał jest wysłany, obsługa sygnału jest zerowana do SIG_DFL SA_RESTART – Kiedy sygnał jest wysłany do procesu, w czasie, gdy ten wykonuje wolną funkcję systemową, funkcja systemowa jest wznawiana po powrocie z funkcji sa_sigaction– jeżeli SA_SIGINFO to określa adres funkcji obsługi sygnału

  19. Obsługa zbioru sygnałów sigset_t: Funkcje systemowe POSIX do obsługi sygnałów operują na zbiorach sygnałów. intsigemptyset(sigset_t *set); - zeruje zbiór sygnałów intsigfillset(sigset_t *set); - dodaje do zbioru wszystkie dostępne sygnały intsigaddset(sigset_t *set,intsig); - dodaje do zbioru sygnał int sigdelset(sigset_t *set,int sig); - usuwa ze zbioru sygnał int sigismeber(sigset_t *set,int sig); - Jeśli sygnał w jest w zbiorze zwróci != 0 intsigpending(sigset_t *set); - Uzyskuje informacje, które z sygnałów są aktualnie niezałatwione intsigsuspend(sigset_t *mask); - Wstrzymanie działania programu dopóki proces nie otrzyma sygnału. Na ten czas ustawia maskę sygnałów. dostępne sygnały voidpause(); - wstrzymuje działanie procesu do nadejścia jakiegokolwiek sygnału

  20. Maskowanie sygnałów W kodzie programu mogą istnieć sekcje które nie powinny być przerywane sygnałami. Stąd w systemie istnieją funkcje służące do blokowania sygnałów. Zablokowany sygnał jest pamiętany - może on być obsłużony gdy zostanie odblokowany. Standardowo tylko jeden nie obsłużony sygnał może być pamiętany, ale sygnały mogą być kolejkowane gdy ustawiona jest flaga SA_SIGINFO (funkcja sigaction()) Przykład: maska sygnałów blokuje dostarczanie sygnałów do procesu P1

  21. Maskowanie sygnałów w POSIX Do maskowania sygnałów wykorzystujemy zbiory sygnałów int sigprocmask(int how, sigset_t *set, sigset_t *oset) • how – sposób blokowania • SIG_SETMASK – blokowanie sygnałów ze zbioru set • SIG_UNBLOCK – odblokowanie sygnałów ze zbioru set • SIG_BLOCK – blokowane sygnały są unią bieżąco zablokowanych sygnałów i wyspecyfikowanych w zbiorze set set Zbiór sygnałów oset Poprzedni zbiór sygnałów Użycie w sekcji krytycznej

  22. Wysłanie sygnału POSIX intsigqueue(pid_tpid, intsig, const union sigvalvalue); pid – komu chcemy wysłać sygnał sig – nr sygnału value – dodatkowe informacje union sigval { intsival_int; void *sival_ptr; };

  23. Uwaga do używania sygnałów Sygnały z założenia są narzędziem działającym asynchronicznie. Nie można robić żadnych założeń co do stanu wykonywanego programu. Należy pamiętać by w procedurze obsługi sygnału nie zmieniać globalnych danych. Jeżeli już to czynimy to tylko wtedy gdy inna część naszego oprogramowania z nich nie korzysta. Należy np. na moment użycia zmiennych globalnych, czy innych współdzielonych z procedurą obsługi sygnału zasobów zablokować sygnał, który tą obsługę mógł by wywołać.

  24. Przykłady użycia sygnałów // Program obsługujący sygnał SIGINT #include <signal.h> #include <stdlib.h> #include <setjmp.h> intsigcnt = 0; intsig = 0; voidsighandler(int signum) { // Procedura obsługi sygnału sigcnt++; sig = signum; } main() { int i; i = 0; printf("Start programu \n"); signal(SIGINT, sighandler); do { printf(" %d %d %d \n", i, sigcnt, sig); sleep(1); i++; } while(1); }

  25. Przykłady użycia sygnałów // Sygnał ustala limit czasowy na podanie hasła #include <stdio.h> #include <signal.h> inttime_out; voidtime_sig(intsigno) { time_out = 1; } main() { char passwd[16]; signal(SIGALRM, time_sig); for(i=0;i<5;i++) { time_out = 0; printf("Podaj haslo:\n"); alarm(5); gets(passwd); alarm(0); if( time_out == 0) break; } // dalszy ciąg programu }

  26. Obsługa sygnałów przy pomocy funkcji sigaction #include <stdlib.h> #include <signal.h> #include <unistd.h> static int count, numer, kod, value = 0; void handler( intsigno, siginfo_t *info, void * o ) { count++; numer = signo; kod = info->si_code; value = info->si_value.sival_int; } intmain( intargc, char * argv[] ) { inti,pid,kod,wart; union sigvalsig; sigset_t set; structsigactionact; sigemptyset( &set ); sigaddset( &set, SIGUSR1 ); act.sa_flags = SA_SIGINFO; act.sa_mask = set; act.sa_handler = &handler; // VERTE

  27. Obsługa sygnałów przy pomocy funkcji sigaction sigaction( SIGUSR1, &act, NULL ); if((pid = fork()) == 0) { // Tu – proces potomny pid = getppid(); for(i=0;i<10;i++) { printf("Wysylam: sygnal= %d \n", i); // Wartość sygnalu sig.sival_int = i; sigqueue(pid, SIGUSR1, sig); // wrzucamy do kolejki sleep(1); } exit(0); } // Proces Macierzysty ----------- while(1) { printf("Odbior: licz= %d num= %d kod= %d val= %d\n", count, numer, kod, value); sleep(1); if(value == 9) break; } return EXIT_SUCCESS; }

  28. Sprawdzenie sygnałów dostarczonych do procesu ale zablokowanych #include <stdio.h> #include <signal.h> #include <unistd.h> voidmain() { sigset_t set, oset, pset; sigemptyset( &set ); sigaddset( &set, SIGINT ); // Ustawienie maski sigprocmask( SIG_BLOCK, &set, &oset ); printf( "stary zbiór %8.8ld.\n", oset ); // Sprawdzanie sygnałów oczekujących sigpending( &pset ); printf( "Zbiór sygnałów oczekujących %8.8ld.\n", pset ); kill( getpid(), SIGINT ); sigpending( &pset ); printf( "Pending set is %8.8ld.\n", pset ); sigprocmask( SIG_UNBLOCK, &set, &oset ); /* Program zatrzymuje się z SIGINT */ }

  29. Oczekiwanie na sygnał #include <stdio.h> #include <signal.h> #include <unistd.h> voidmain() { sigemptyset( &set ); sigaddset( &set, SIGINT ); printf("Program zawieszony i odporny na przerwanie.\n" ); printf("Sygnał SIGALRM zatrzyma program za 10 sekund\n" ); alarm( 10 ); sigsuspend( &set ); } Funkcja sigsuspend tymczasowo zastępuje bieżącą maskę sygnałów przez sigmask i oczekuje na sygnał. Następuje zablokowanie procesu. Gdy sygnał jest obsługiwany odblokowanie procesu następuje po zakończeniu obsługi sygnału i maska sygnału zostaje przywrócona do poprzedniej.

  30. Kolejka komunikatów Kolejki komunikatów to specjalne listy (kolejki) w jądrze, zawierające odpowiednio sformatowane dane i umożliwiające ich wymianę poprzez dowolne procesy w systemie. Istnieje możliwość umieszczania komunikatów w określonych kolejkach (z zachowaniem kolejności ich wysyłania przez procesy) oraz odbierania komunikatu na parę rożnych sposobów (zależnie od typu, czasu przybycia itp.).

  31. Kolejka komunikatów Za każdą kolejkę komunikatów odpowiada jedna struktura typu msqid_ds Komunikaty danej kolejki przechowywane są na liście, której elementami są struktury typu msg każda z nich posiada informacje o typie komunikatu, wskaźnik do następnej struktury msg oraz wskaźnik do miejsca w pamięci, gdzie przechowywana jest właściwa treść komunikatu Dodatkowo, każdej kolejce komunikatów przydziela sie dwie kolejki typu wait_queue, na których śpią procesy zawieszone podczas wykonywania operacji czytania bądź pisania do danej kolejki.

  32. Struktury danych kolejki Struktura msqid_ds (include/linux/msg.h): structmsqid_ds {/* jedna struktura msg dla kazdej kolejki w systemie */ structipc_permmsg_perm; Jest to instancja struktury ipc_perm (prawa dostępu) structmsg *msg_first; /* pierwszy komunikat w kolejce */ structmsg *msg_last; /* ostatni komunikat w kolejce */ __kernel_time_tmsg_stime; /* czas ostatniego msgsnd */ __kernel_time_tmsg_rtime; /* czas ostatniego msgrcv */ __kernel_time_tmsg_ctime; /* czas ostatniej zmiany */ structwait_queue *wwait;/* dwie kolejki typu wait_queue, na ktorychspia */ structwait_queue *rwait; /*procesy zawieszone podczas wykonywania operacji odpowiednio czytania oraz pisania w danej kolejce komunikatow*/ unsignedshortmsg_cbytes; /* liczba bajtow w kolejce */ unsignedshortmsg_qnum; /* liczba komunikatow w kolejce */ unsignedshortmsg_qbytes; /* maksymalna liczba bajtow w kolejce */ __kernel_ipc_pid_tmsg_lspid; /* pid ostatniego msgsnd */ __kernel_ipc_pid_tmsg_lrpid; /* pid ostatniego receive*/ };

  33. Struktury danych kolejki Struktura msg (include/linux/msg.h): /* jedna struktura msg dla każdego komunikatu */ struct msg { struct msg *msg_next; /* następny komunikat w kolejce */ long msg_type; char *msg_spot; time_t msg_stime; /* czas wysłania tego komunikatu */ short msg_ts; /* długość właściwej treści komunikatu */ }; Dodatkowe wyjaśnienia: msg_type Typ przechowywanego komunikatu. Wysyłanemu do kolejki komunikatowi nadawca przypisuje dodatnia liczbę naturalna, stająca sie jego typem. Przy odbiorze komunikatu można zażądać komunikatów określonego typu. msg_spot Wskaźnik do miejsca w pamięci, gdzie przechowywana jest właściwą tresc komunikatu. Na każdy komunikat przydzielane jest oddzielne miejsce w pamięci.

  34. Obsługa kolejki komunikatów Kolejki komunikatów umożliwiają przesyłanie pakietów danych, nazywanych komunikatami, pomiędzy różnymi procesami. Sam komunikat jest zbudowany jako struktura msgbuf, jego definicja znajduje się w pliku <sys/msg.h>: /* message buffer for msgsnd and msgrcv calls */ struct msgbuf { long mtype; /* typ komunikatu */ char mtext[1]; /* tresc komunikatu */ }; Każdy komunikat ma określony typ i długość. Typ komunikatu nadaje proces inicjujący komunikat. Komunikaty są umieszczane w kolejce w kolejności ich wysyłania. Nadawca może wysyłać komunikaty, nawet gdy żaden z potencjalnych odbiorców nie jest gotów do ich odbioru. Komunikaty są w takich przypadkach buforowane w kolejce oczekiwania na odebranie. Przy odbiorze komunikatu, odbiorca może oczekiwać na pierwszy przybyły komunikat lub na pierwszy komunikat określonego typu. Komunikaty w kolejce są przechowywane nawet po zakończeniu procesu nadawcy, tak długo, aż nie zostaną odebrane lub kolejka nie zostanie zlikwidowana.

  35. Obsługa kolejki komunikatów int msgsnd(int msgid, struct msgbuf *msgp, int msgs, int msgflg) służy do wysłania komunikatu do kolejki. Argumenty funkcji: msgid – identyfikator kolejki komunikatów msgp – wskaźnik na komunikat do wysłania msg – rozmiar właściwej treści komunikatu (w bajtach) msgflg – flagi specyfikujące zachowanie się funkcji w warunkach nietypowych. Wartość ta może być ustawiona na 0 lub IPC_NOWAIT Wartości zwracane: poprawne wykonanie funkcji: 0 zakończenie błędne: -1 Możliwe kody błędów (errno) w przypadku błędnego zakończenie funkcji: EACCES – kolejka skojarzona z wartością msgid, istnieje, ale proces wołający funkcję nie ma wystarczających praw dostępu do kolejki EAGAIN – kolejka jest pełna, a flaga IPC_NOWAIT była ustawiona EFAULT – niepoprawny wskaźnik msgp EIDRM – kolejka została przeznaczona do usunięcia EINTR – otrzymano sygnał podczas oczekiwania na operację zapisu EINVAL – zły identyfikator kolejki, lub ujemny typ wiadomości, lub zły rozmiar wiadomości

  36. Obsługa kolejki komunikatów Odbieranie komunikatów. Odebrany komunikat jest usuwany z kolejki int msgrcv (int msgid, struct msgbuf *msgp, int msgs, long msgtyp, int msgflg) Argumenty funkcji: msgid – identyfikator kolejki komunikatów msgp – wskaźnik do obszaru pamięci w którym ma zostać umieszczony pobrany komunikat msgs – rozmiar właściwej treści komunikatu msgtyp – określa typ komunikatu który ma być odebrany z kolejki. Możliwe są następujące wartości zmiennej msgtyp: • msgtyp > 0 – pobierany jest pierwszy komunikat typu msgtyp • msgtyp < 0 – pobierany jest pierwszy komunikat którgo wartość typu jest mniejsza lub równa wartości msgtyp • msgtyp = 0 – typ komunikatu nie jest brany, tzn. funkcja pobiera pierwszy komunikat dowolnego typu msgflg – flaga specyfikujące zachowanie się funkcji w warunkach nietypowych. Wartość ta może być ustawiona na 0, IPC_NOWAIT lub MSG_NOERROR Wartości zwracane: poprawne – liczba odebranych bajtów; błędne: -1 Możliwe kody błędów (errno) – analogicznie do funkcji msgsnd

  37. Przykład: producenci i konsumenci // Program producenta #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #define MAX 10 //zakładamy ograniczony bufor komunikatów struct buf_elem { //nasza struktura komunikatu long mtype; int mvalue; }; #define PUSTY 1 #define PELNY 2

  38. Przykład: producenci i konsumenci // Program producenta – c.d. main(){ int msgid, i; struct buf_elem elem; msgid = msgget(45281, IPC_CREAT|IPC_EXCL|0600); //tworzymy kolejke if (msgid == -1){ //jeżeli kolejka już istniała msgid = msgget(45281, IPC_CREAT|0600); // pobieramy jej id if (msgid == -1) exit(1); //błąd utworzenia lub pobrania kolejki }else{ elem.mtype = PUSTY; for (i=0; i<MAX; i++) if (msgsnd(msgid, &elem, sizeof(elem.mvalue), 0) == -1) exit(1); // Błąd wyslania pustego komunikatu } for (i=0; i<10000; i++){ if (msgrcv(msgid,&elem,sizeof(elem.mvalue),PUSTY, 0)== -1) exit(1); //Błąd odebrania pustego komunikatu elem.mvalue = i; elem.mtype = PELNY; if (msgsnd(msgid, &elem, sizeof(elem.mvalue), 0) == -1) exit(1); //Błąd wyslania elementu }}

  39. Przykład: producenci i konsumenci main(){ int msgid, i; struct buf_elem elem; msgid = msgget(45281, IPC_CREAT|IPC_EXCL|0600); if (msgid == -1){ msgid = msgget(45281, IPC_CREAT|0600); if (msgid == -1) exit(1); //błąd utworzenia kolejki komunikatów }else{ elem.mtype = PUSTY; for (i=0; i<MAX; i++) if (msgsnd(msgid, &elem, sizeof(elem.mvalue), 0) == -1) exit(1); //błąd wyslania pustego komunikatu } for (i=0; i<10000; i++){ if (msgrcv(msgid,&elem,sizeof(elem.mvalue),PELNY,0) == -1) exit(1); // błąd odebrania elementu printf("Numer: %5d Wartosc: %5d\n", elem.mvalue); elem.mtype = PUSTY; if (msgsnd(msgid, &elem, sizeof(elem.mvalue), 0) == -1) exit(1); // błąd wyslania pustego komunikatu„ } }

  40. Kolejki Komunikatów Posix Podstawowe różnice w stosunku do komunikatów SystemV – Odczyt przekazuje zawsze najstarszy komunikat o najwyższym priorytecie, z systemu V można odczytać najstarszą wiadomość dowolnego typu – Kolejki komunikatów w posiksie mogą generować sygnał lub tworzyć wątek w chwili umieszczenia komunikatu Każdy komunikat w kolejce ma następujące parametry – Priorytet – liczba całkowita bez znaku lub liczbą całkowitą typu long – Rozmiar obszaru danych w komunikacie >=0 – Dane (jeśli rozmiar > 0) Uwaga: Pisząc program w C wykorzystujemy plik nagłówkowy <mqueue.h> Podczas linkowania trzeba dodać przełącznik -lrt

  41. Tworzenie i zamykanie kolejki Posix Tworzenie kolejki komunikatów mqd_tmq_open(const char *name, intoflag,... /* mode_t mode, structmq_attr *atr */); – name – nazwa zaczynająca się od '/' – oflag – flagi, przy tworzeniu musi być ustawione O_CREATE) –mode – słowo trybu dostępu –atr - dodatkowe parametry, gdy NULL brana wartość domyślna Zamknięcie kolejki intmq_close(mqd_t *mqdes); mqdes – identyfikator kolejki Jeżeli proces się kończy, to wszystkie otwarte przez niego kolejki są zamykane, nie zostają one jednak usunięte!

  42. Usunięcie kolejki Posix intmq_unlink(const char *name); name – nazwa kolejki Funkcja ta usuwa nazwę name wtedy gdy ilość otwarć jest > 0 kolejka nie zostanie jednak usunięta z systemu do czasu ostatniego wywołania mq_close // mq_unlink.c #include <stdlib.h> #include <stdio.h> #include <mqueue.h> intmain (intargc, char **argv) { if (0 > mq_unlink("/kolejka")) { perror("blad usuwania kolejki"); exit(1); } exit(0); }

  43. Odczyt i ustawienie atrybutów kolejki Posix intmq_getattr(mqd_tmqdes, structmq_attr *attr); intmq_setattr(mqd_tmqdes, conststructmq_attr *attr, structmq_attr *old_attr); mqdes – identyfikator kolejki attr – atrybuty kolejki old_attr – jeśli != NULL zapisane zostaną stare atrybuty kolejki Atrybuty kolejki tworzą strukturę structmq_attr { long mq_flags; /*znaczniki kolejki np. O_NONBLOCK*/ long mq_maxmsg; /* maksymalna liczba komunikatów */ long mq_msgsize; /*maksymalny rozmiar komunikatu w bajtach*/ long mq_curmsgs; /* bieżaca liczba kokmunikatów */ }; Wskaźnik do tej struktury można przekazać jako czwarty parametr mq_open i tam ustawić mq_maxmsg i mq_msgsize. mq_setattr może jedynie ustawić mq_flags. Liczby komunikatów nie możemy zmienić za pośrednictwem tych funkcji.

  44. Przykład tworzenia kolejki Posix // mq_create.c #include <stdlib.h> #include <stdio.h> #include <mqueue.h> intmain (intargc, char **argv){ intflags; mqd_tmqd; structmq_attrmqat; flags = O_RDWR | O_CREAT ; * flags |= O_EXCL; */ mqat.mq_maxmsg = 8; mqat.mq_msgsize = 4096; if (0 > (mqd = mq_open("/kolejka",flags,0666,&mqat))) { perror("blad tworzenia kolejki"); exit(1); } mq_getattr(mqd,&mqat); printf("mq_maxmsg: %lu\nmq_msgsize: %lu\nmq_curmsgs: %lu\n", mqat.mq_maxmsg, mqat.mq_msgsize, mqat.mq_curmsgs); mq_close(mqd); exit(0); }

  45. Wysyłanie komunikatu do kolejki Posix intmq_send(mqd_tmqdes, const char *ptr, size_t len, unsignedintprio); mqdes – identyfikator kolejki ptr - początek danych len - rozmiar danych prio – priorytet, liczba całkowita bez znaku. Posix wymaga by można było ustawiać conajmniej 32 priorytety. Wartość ta to MQ_PRIO_MAX w pliku limits.h w przypadku linuxa 32768. Odbiór komunikatu intmq_receive(mqd_tmqdes, char *ptr, size_t len, unsignedint *prio); mqdes – identyfikator kolejki ptr – adres bufora len - rozmiar danych, musi być co najmniej o wielkości maksymalnej wielkości komunikatu. Dlatego często przed odbiorem komunikatu powinniśmy odczytać pole mq_msgsize struktury mq_attr. prio – priorytet. Z kolejki Posix zawsze odbierane są komunikaty o najwyższym priorytecie i najstarsze. Dopuszcza się by przekazać 0 bajtów.

  46. Przykład wysłania komunikatu do kolejki Posix #include <stdlib.h>/* Wysłanie komunikatu mq_send.c */ #include <stdio.h> #include <mqueue.h> #include <string.h> intmain (intargc, char **argv){ intflags; mqd_tmqd; structmq_attrmqat; char msg[256] = "Tekst komunikatu"); flags = O_RDWR | O_CREAT ; if (0 > (mqd = mq_open("/kolejka",flags,0666,NULL))){ perror("blad tworzenia kolejki"); exit(1); } if (0 > mq_send(mqd,msg,sizeof(msg),10)){ perror("bladwyslania komunikatu"); exit(1); } mq_getattr(mqd,&mqat); printf("mq_maxmsg: %lu\nmq_msgsize: %lu\nmq_curmsgs: %lu\n", mqat.mq_maxmsg, mqat.mq_msgsize, mqat.mq_curmsgs); mq_close(mqd); exit(0); }

  47. Przykład odebrania komunikatu z kolejki Posix #include <stdlib.h>/* Odbiór komunikatu mq_receive.c */ #include <stdio.h> #include <mqueue.h> #include <string.h> intmain (intargc, char **argv){ intflags; mqd_tmqd; structmq_attrmqat; char msg[4096]; unsignedintprio = 0; flags = O_RDWR | O_CREAT ; if (0 > (mqd = mq_open("/kolejka",flags,0666,NULL))){ perror("blad tworzenia kolejki"); exit(1);} if (0 > mq_receive(mqd,msg,4096,&prio)){ perror("blad odbioru komunikatu"); exit(1);} printf("Otrzymalem komunikat o priorytecie %u\n",prio); printf("[%s]\n",msg); mq_getattr(mqd,&mqat); printf("mq_maxmsg: %lu\nmq_msgsize: %lu\nmq_curmsgs: %lu\n", mqat.mq_maxmsg, mqat.mq_msgsize, mqat.mq_curmsgs); mq_close(mqd); }

  48. Powiadomienie o komunikacie intmq_notify(mqd_tmqdes, conststuctsigevent *n); n – struktura związana z sygnałami czasu rzeczywistego Posix.1 structsigevent { intsigev_notify; //SIGEV_{NONE, SIGNAL, THREAD,THREAD_ID} int sigev_signo; // jeżeli SIGEV_SIGNAL to nr sygnału union sigvalsigev_value; //przekazywane procedurze obsługi albo wątkowi void (*sigev_notify_function)(union sigval); //używane gdySIGEV_THREAD pthreaed_attr_t *sigev_notify_attributes; //używane gdySIGEV_THREAD } union sigval { intsival_int; void *sival_ptr; }; Gdy n!=0 to proces chce być powiadamiany kiedy w określonej kolejce pojawia się komunikat, a kolejka ta była pusta. Gdy n == 0 a proces był zarejestrowany do powiadamiania to informacja ta jest kasowana. Dla jednej kolejki, w danej chwili tylko jeden proces może być zarejestrowany do powiadomienia. Powiadomienie nie jest wysyłane gdy proces został zablokowany na mq_receive czekającna odbiór z danej kolejki komunikatów. Gdy zostanie wysłane powiadomienie to rejestracja jest kasowana i proces powinien znowu wywołać mq_notify. Informowanie po raz drugi nie wystąpi zanim kolejka nie będzie pusta dlatego ponowne mq_notify powinno być przed mq_receive.

  49. Przykład z powiadomieniem (1) #include <stdlib.h> #include <stdio.h> #include <mqueue.h> #include <string.h> #include <signal.h> mqd_tmqd; intflags; char *msg; structsigeventsigev; structmq_attrmqat; staticvoid sig_usr1(intsigno) { ssize_t n; mq_notify(mqd,&sigev); n = mq_receive(mqd, msg, mqat.mq_msgsize, NULL); printf("SIGUSR! odebrał %ld bajtów\n", (long) n); printf("[%s]\n",msg); } // . . . . .

  50. Przykład z powiadomieniem (1) // c. d. intmain (intargc, char **argv){ f = O_RDWR | O_CREAT ; if(0>(mqd= mq_open("/kolejka",f,0666,NULL))) { perror("blad tworzenia kolejki"); exit(1); } mq_getattr(mqd, &mqat); printf("mq_maxmsg: %lu\nmq_msgsize: %lu\nmq_curmsgs: %lu\n", mqat.mq_maxmsg, mqat.mq_msgsize, mqat.mq_curmsgs); msg = malloc(mqat.mq_msgsize); signal(SIGUSR1, sig_usr1); sigev.sigev_notify = SIGEV_SIGNAL; sigev.sigev_signo = SIGUSR1; mq_notify(mqd, &sigev); for (;;) { sleep(1); printf("czekam\n"); } mq_close(mqd); exit(0); }

More Related