Jeśli podasz klucz, to fukcja msgget() utworzy
nową kolejkę,
lub znajdzie istniejącą (o podanym kluczu). Zwracany przez tę funkcję id_kolejki
to identyfikator, którego używamy podczas dalszych odwołań do kolejki.
Analogia z systemem plików:
Istnieją prawa dostępu do kolejki, takie same jak prawa do plików. Z każdą kolejką są związane 2 osoby: twórca kolejki i użytkownik kolejki, a także ich grupy. Standardowo twórca i użytkownik to ta sama osoba (można to zmienić przy pomocy msgctl()). Spośród 3 praw unixowych, liczą się tylko "r" i "w", którch interpretacja jest oczywista. Niektóre operacje (te wykonywane funkcją msgctl()) może wykonywać tylko twórca.
Usuwanie kolejki, odczytywanie rozmaitych informacji dotyczących kolejki, a także zmianę pewnych parametrów kolejki, wykonuje się przy pomocy funkcji msgctl().
UWAGA: Obiekty IPC (kolejki komunikatów i inne) mogą istnieć także wtedy gdy nie używa ich żaden proces !. Dlatego należy je starannie usuwać gdy nie są już potrzebne. Istnieje polecenia pozwalające zobaczyć istniejące obiekty, i usunąć je:
ipcsFunkcje systemowe do obslugi kolejek komunikatow:
ipcrm
Przykłady:
Otwieranie istniejącej kolejki o podanym kluczu:
#define KLUCZ 12345Otwieranie istniejącej kolejki o podanym kluczu, lub tworzenie nowej z prawami dostępu 0600:
int id_kolejki= msgget(KLUCZ, 0);
// jeśli nie ma takiej kolejki to wystąpi błąd !
#define KLUCZ 12345Tworzenie nowej kolejki z prawami dostępu 0600:
int id_kolejki= msgget(KLUCZ, IPC_CREAT|0600);
// jeśli robią to równocześnie 2 procesy, to jeden
// z nich utworzy kolejkę, a drugi tylko ją otworzy !
#define KLUCZ 12345
int id_kolejki= msgget(KLUCZ, IPC_CREAT|IPC_EXCL|0600);
// jeśli jest kolejka o podanym kluczu to wystąpi błąd !
Przykłady:
struct {
long mtype; // typ komunikatu
char mtext[50]; // dane komunikatu (dowolna długość i interpretacja)
} buf;
buf.mtype=123;
sprintf(buf.mtext,"Tra la la !!!");
j=msgsnd(id_kolejki, &buf, 50, 0);
/*
ostatni parametr to "opcje";
gdyby podac IPC_NOWAIT, to w razie gdyby wysłanie
komunikatu było niemożliwe, funkcje nie będzie blokować
tylko od razu zwróci błąd
*/
if(j==-1) perror("msgsnd");
Interpretacja parametru oczekiwanyTyp:
Przykłady:
struct {
long mtype; // typ komunikatu
char mtext[50]; // dane komunikatu
} buf;
j=msgrcv(id_kolejki, &buf, 50, 123, 0);
/*
oczekujemy na komunikat typu 123;
trzeci parametr to rozmiar bufora NIE LICZĄC zmiennej mtype !;
ostatni parametr to "opcje":
gdyby podac IPC_NOWAIT, to w razie gdyby odebranie
komunikatu było niemożliwe, funkcje nie będzie blokować
tylko od razu zwróci błąd
*/
if(j==-1) perror("msgrcv");
sprintf("otrzymalem komunikat: %s\n", buf.mtext);
Przykłady:
j=msgctl(id_kolejki, IPC_RMID, NULL);
//
// usuwanie kolejki
//
if(j==-1) perror("msgctl");
Prosty przykład znajduje się w pliku msg01.cc (+unix.h).
Drugi przykład to dwa programy msg02k.cc, msg02p.cc
(+unix.h).
Tutaj kolejka jest używana przez procesy niespokrewnione. Są to: producent
i konsument. Można zaobserwować, że jeśli
uruchomimy tylko jeden z programów, to zasoby kolejki prędko zostaną
wyczerpane
(lub kolejka będzie pusta). W obu przypadkach proces wykonujący
operacje
na kolejce zostanie uśpiony. Jądro obudzi go dopiero gdy stanie się
możliwe
kontynuowanie pracy z kolejką. W przykładzie tym jest dodatkowa
trudność:
klient musi wiedzieć, że producent zakończył definitywnie pracę, stąd
potrzeba
specjalnego "protokołu zakończenia" !.
klient; pid=123; usługa 1; wysyłam sfdgsfg
klient; pid=123; usługa 1; odebrałem s.f.d.g.s.f.g.
klient; pid=123; usługa 2; wysyłam sfdgsfg
klient; pid=123; usługa 2; odebrałem SFDGSFG
serwer1 &
serwer2 &
klient &
klient &
klient &
#define KLUCZ 12345Jeśli "otwieramy" istniejący segment pamięci to wystarczy:
int id= shmget(KLUCZ, 100, IPC_CREAT|0600);
// tworzenie segmentu pamięci o rozmiarze 100 bajtów
if(id==-1) perror("shmget");
int id= shmget(KLUCZ, 0, 0);Segment dołącza się do wirtualnej przestrzeni adresowej procesu, przy pomocy funkcji shmat():
if(id==-1) perror("shmget");
char *c= (char*)shmat(id, 0, 0);Ten sam segment może być dołączony do różnych procesów jak i kilka razy do tego samego procesu !.
if(c==(char*)-1) perror("shmat");
Segment odłącza się przy pomocy funkcji shmdt():
i= shmdt(c); // UWAGA: podaje się "adres" a nie "id" !!!Po odłączeniu segmentu (nawet od wszystkich procesów), segment ten NADAL istnieje !. Musi być jawnie usunięty funkcją shmctl():
if(i==-1) perror("shmdt");
i=shmctl(id, IPC_RMID, NULL);Prosty przykład znajduje się w pliku "shm01.cc"(+"unix.h").
if(i==-1) perror("shmctl");
Drugi przykład znajduje się w pliku "shm02.cc"(+"unix.h").
Jest to znany przykład z dwoma kontami, konto1 i konto2,
między którymi procesy dokonują losowych przelewów. Suma obu kont się
zmienia
(co świadczy o błędzie), gdyż nie używamy tutaj żadnych mechanizmów do
synchronizacji procesów, takich jak semafory.
Zbiór semaforów tworzymy przy pomocy funkcji semget(). KLUCZ identyfikuje zbiór semaforów.
#define KLUCZ 12345
int id_sem= semget(KLUCZ, 5, IPC_CREAT|IPC_EXCL|0666);
// drugi parametr to liczba semaforów w zbiorze.
if(id_sem==-1) perror("semget");
Istniejący zbiór semaforów "otwieramy" tak:
int id_sem= semget(KLUCZ, 0, 0);
Wykonywanie operacji na semaforach (takich jak podnoszenie i opuszczanie) :
struct sembuf buf;Inicjowanie semafora przy pomocy semctl():
buf.sem_num=0;
// nr semafora w zbiorze, na którym chcemy wykonać operację
buf.sem_op=-1;
// opuszczanie semafora (wait)
buf.sem_flg=0;
/* można podać flagi IPC_NOWAIT, SEM_UNDO;
ta ostatnia powoduje, że przed "błędnym"(?) zakończeniem procesu
zostaną anulowane modyfikacje semafora
*/
i=semop(id_sem, &buf, 1);
// ostatni parametr to rozmiar tablicy struktur "sembuf";
// można wykonywać kilka operacji na różnych semaforach
// jednocześnie (tzw "operacja zbiorowa")
if(i==-1) perror("semop(-1)");
buf.sem_num=0;
buf.sem_op=+1;
// podnoszenie semafora (signal)
buf.sem_flg=0;
i=semop(id_sem, &buf, 1);
if(i==-1) perror("semop(+1)");
union semun {Usuwanie zbioru semaforów przy pomocy semctl():
// UWAGA: te unię trzeba zdefiniować samodzielnie (?!?!)
// to dotyczy tylko niektórych wersji Linuxa ...
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
union semun arg;
arg.val=0;
// początkowa wartość semafora
j=semctl(id_sem, 0, SETVAL, arg);
// drugi argument to nr semafora, który chcemy zainicjować
if(j==-1) perror("semctl(SETVAL)");
i=semctl(id_sem, 0, IPC_RMID, NULL);Odczytywanie ilości procesów oczekujących na podniesienie semafora:
if(i==-1) perror("semctl(IPC_RMID)");
i=semctl(id_sem, 0, GETNCNT, NULL);UWAGA: Oprócz podnoszenia i opuszczania, na semaforach można wykonywać także operacje:
if(i==-1) perror("semctl(GETNCNT)");
printf("ilości procesów = %i\n", i);
W pliku "sem01.cc"(+"unix.h")
znajduje sie to samo co w pliku "shm02.cc" (przelewy między kontami),
jednak
tym razem operacje przelewu są chronione semaforami.
id = lacze_open(klucz)
lacze_read(id, ilosc_elementow, bufor);
lacze_write(id, ilosc_elementow, bufor);
lacze_close(id); // ???
Dodatkowo, jeśli użyliśmy flagi MAP_SHARED, to modyfikacje są natychmiast(?) widoczne dla innych procesów, które otwarły ten plik (i być możę odwzorowały go w pamięć).
UWAGA: Ten mechanizm może być używany do komunikacji między procesami. Rolę "kluczy" pełnią w nim pliki. Jest to nawet bardziej zgodne z filozofią UNIX-a, w której "wszystko jest plikiem" !.
Przykłady:
int d=open("plik.txt", O_RDWR); // otwieramy plik
if(d==-1) perror("open");
struct DanePersonalne *c=mmap(0, sizeof(struct DanePersonalne),
PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, d, 0);
/* -> funkcja mmap() dokonuje "odwzorowania" fragmentu pliku w pamięć
-> wartość 0 pierwszego parametru oznacza, że system ma sam wybrać
adres tworzonego segmentu pamięci (?)
-> drugi parametr to długość segmentu pamięci i fragmentu pliku
-> ostatni parametr to "offset" w pliku
*/
if(c==(char *)-1) perror("mmap");
close(d);
// możemy modyfikować fragment pliku
// za pośrednictwem wskazania "c"
//
// inne procesy będą widziały nasze modyfikacje
// (dzięki MAP_FILE|MAP_SHARED !)
//
sprintf(c->Imie, "Jan");
sprintf(c->Nazwisko, "Kowalski");
printf("Adres=%s\n", c->Adres);
man munmapUWAGA: funkcja mmap() Linuxa jest nieco uboższa od opisanej w pliku "mmap.txt" !.
man msync
Prosty przykład znajduje się w pliku mmap01.cc(+unix.h). PRZED jego uruchomieniem utwórz plik "mmap01.txt" przy pomocy polecenia:
echo "0123456789" > mmap01.txtW przykładzie tym jeden proces zapisuje fragment pliku odwzorowany w pamięć, a drugi go odczytuje (oba procesy modyfikują pamięć - nie wykonują żadnych operacji na plikach !).