Kolejki wiadomości
Po co nam kolejki wiadomości, skoro mamy już pamięć współdzieloną? Byłoby to z wielu powodów, spróbujmy podzielić to na kilka punktów dla uproszczenia -
Jak rozumiem, po odebraniu wiadomości przez proces nie będzie ona już dostępna dla żadnego innego procesu. Podczas gdy w pamięci współdzielonej dane są dostępne dla wielu procesów.
Jeśli chcemy komunikować się za pomocą małych formatów wiadomości.
Dane w pamięci współdzielonej muszą być chronione przez synchronizację, gdy wiele procesów komunikuje się w tym samym czasie.
Częstotliwość zapisywania i odczytywania danych przy użyciu pamięci współdzielonej jest wysoka, wówczas wdrożenie tej funkcjonalności byłoby bardzo skomplikowane. Nie warto ze względu na wykorzystanie w tego typu przypadkach.
Co by było, gdyby wszystkie procesy nie miały dostępu do pamięci współdzielonej, ale bardzo niewiele procesów tego potrzebowało, lepiej byłoby wdrożyć je z kolejkami komunikatów.
Jeśli chcemy komunikować się z różnymi pakietami danych, powiedzmy, że proces A wysyła komunikat typu 1 do procesu B, komunikat typu 10 do procesu C i komunikat typu 20 do przetwarzania D. W tym przypadku łatwiej jest zaimplementować z kolejkami komunikatów. Aby uprościć podany typ wiadomości jako 1, 10, 20, może to być 0 lub + ve lub –ve, jak omówiono poniżej.
Oczywiście kolejność w kolejce komunikatów to FIFO (First In First Out). Pierwsza wiadomość wstawiona do kolejki jest pierwszą odebraną.
Korzystanie z pamięci współdzielonej lub kolejek wiadomości zależy od potrzeb aplikacji i tego, jak efektywnie można ją wykorzystać.
Komunikacja przy użyciu kolejek wiadomości może odbywać się na następujące sposoby -
Zapisywanie do pamięci współdzielonej przez jeden proces i czytanie z pamięci współdzielonej przez inny proces. Jak wiemy, czytanie może odbywać się również za pomocą wielu procesów.
Zapis do pamięci współdzielonej przez jeden proces z różnymi pakietami danych i odczyt z niej przez wiele procesów, tj. Zgodnie z typem wiadomości.
Po zapoznaniu się z pewnymi informacjami na temat kolejek komunikatów nadszedł czas, aby sprawdzić wywołanie systemowe (System V), które obsługuje kolejki komunikatów.
Aby wykonać komunikację przy użyciu kolejek wiadomości, wykonaj następujące czynności:
Step 1 - Utwórz kolejkę wiadomości lub połącz się z już istniejącą kolejką wiadomości (msgget ())
Step 2 - Zapisz do kolejki wiadomości (msgsnd ())
Step 3 - Odczyt z kolejki wiadomości (msgrcv ())
Step 4 - Wykonaj operacje kontrolne na kolejce komunikatów (msgctl ())
Sprawdźmy teraz składnię i pewne informacje na temat powyższych wywołań.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg)
To wywołanie systemowe tworzy lub przydziela kolejkę komunikatów System V. Należy przekazać następujące argumenty -
Pierwszy argument, klucz, rozpoznaje kolejkę komunikatów. Klucz może być dowolną wartością lub taką, która może pochodzić z funkcji bibliotecznej ftok ().
Drugi argument, shmflg, określa wymagane flagi kolejki komunikatów, takie jak IPC_CREAT (tworzenie kolejki komunikatów, jeśli nie istnieje) lub IPC_EXCL (używany z IPC_CREAT do tworzenia kolejki komunikatów i wywołanie kończy się niepowodzeniem, jeśli kolejka komunikatów już istnieje). Musisz również przekazać uprawnienia.
Note - Zapoznaj się z wcześniejszymi sekcjami, aby uzyskać szczegółowe informacje na temat uprawnień.
To wywołanie zwróci prawidłowy identyfikator kolejki komunikatów (używany do dalszych wywołań kolejki komunikatów) po pomyślnym zakończeniu i -1 w przypadku niepowodzenia. Aby poznać przyczynę niepowodzenia, sprawdź zmienną errno lub funkcją perror ().
Różne błędy związane z tym wywołaniem to EACCESS (odmowa uprawnień), EEXIST (kolejka już istnieje, nie można utworzyć), ENOENT (kolejka nie istnieje), ENOMEM (za mało pamięci do utworzenia kolejki) itp.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg)
To wywołanie systemowe wysyła / dołącza komunikat do kolejki komunikatów (System V). Należy przekazać następujące argumenty -
Pierwszy argument, msgid, rozpoznaje kolejkę komunikatów, tj. Identyfikator kolejki komunikatów. Wartość identyfikatora jest otrzymywana po pomyślnym wykonaniu msgget ()
Drugi argument, msgp, jest wskaźnikiem do wiadomości wysyłanej do dzwoniącego, zdefiniowanym w strukturze następującej postaci -
struct msgbuf {
long mtype;
char mtext[1];
};
Zmienna mtype jest używana do komunikacji z różnymi typami wiadomości, szczegółowo wyjaśnionymi w wywołaniu msgrcv (). Zmienna mtext jest tablicą lub inną strukturą, której rozmiar jest określony przez msgsz (wartość dodatnia). Jeśli pole mtext nie jest wymienione, jest to traktowane jako komunikat o rozmiarze zerowym, co jest dozwolone.
Trzeci argument, msgsz, to rozmiar wiadomości (wiadomość powinna kończyć się znakiem null)
Czwarty argument, msgflg, wskazuje pewne flagi, takie jak IPC_NOWAIT (zwraca natychmiast, gdy w kolejce nie zostanie znaleziona żadna wiadomość lub MSG_NOERROR (obcina tekst wiadomości, jeśli jest więcej niż msgsz bajtów)
To wywołanie zwróci 0 w przypadku sukcesu i -1 w przypadku niepowodzenia. Aby poznać przyczynę niepowodzenia, sprawdź zmienną errno lub funkcją perror ().
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msgid, const void *msgp, size_t msgsz, long msgtype, int msgflg)
To wywołanie systemowe pobiera komunikat z kolejki komunikatów (System V). Należy przekazać następujące argumenty -
Pierwszy argument, msgid, rozpoznaje kolejkę komunikatów, tj. Identyfikator kolejki komunikatów. Wartość identyfikatora jest otrzymywana po pomyślnym wykonaniu msgget ()
Drugi argument, msgp, jest wskaźnikiem wiadomości otrzymanej od dzwoniącego. Jest zdefiniowany w strukturze następującej postaci -
struct msgbuf {
long mtype;
char mtext[1];
};
Zmienna mtype służy do komunikacji z różnymi typami wiadomości. Zmienna mtext jest tablicą lub inną strukturą, której rozmiar jest określony przez msgsz (wartość dodatnia). Jeśli pole mtext nie jest wymienione, jest to traktowane jako komunikat o rozmiarze zerowym, co jest dozwolone.
Trzeci argument, msgsz, to rozmiar otrzymanej wiadomości (wiadomość powinna kończyć się znakiem null)
Czwarty argument, msgtype, wskazuje typ wiadomości -
If msgtype is 0 - Odczytuje pierwszą otrzymaną wiadomość w kolejce
If msgtype is +ve - Odczytuje pierwszą wiadomość w kolejce typu msgtype (jeśli msgtype ma wartość 10, to odczytuje tylko pierwszą wiadomość typu 10, mimo że inne typy mogą znajdować się w kolejce na początku)
If msgtype is –ve - Odczytuje pierwszą wiadomość najniższego typu mniejszą lub równą bezwzględnej wartości typu wiadomości (powiedzmy, jeśli msgtype wynosi -5, to czyta pierwszą wiadomość typu mniejszego niż 5, tj. Typ wiadomości od 1 do 5)
Piąty argument, msgflg, wskazuje pewne flagi, takie jak IPC_NOWAIT (zwraca natychmiast, gdy w kolejce nie zostanie znaleziona żadna wiadomość lub MSG_NOERROR (obcina tekst wiadomości, jeśli jest więcej niż msgsz bajtów)
To wywołanie zwróci liczbę faktycznie otrzymanych bajtów w tablicy mtext w przypadku sukcesu i -1 w przypadku niepowodzenia. Aby poznać przyczynę niepowodzenia, sprawdź zmienną errno lub funkcją perror ().
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msgid, int cmd, struct msqid_ds *buf)
To wywołanie systemowe wykonuje operacje sterujące kolejką komunikatów (System V). Należy przekazać następujące argumenty -
Pierwszy argument, msgid, rozpoznaje kolejkę komunikatów, tj. Identyfikator kolejki komunikatów. Wartość identyfikatora jest otrzymywana po pomyślnym wykonaniu msgget ()
Drugi argument, cmd, jest poleceniem wykonującym wymaganą operację sterującą w kolejce komunikatów. Prawidłowe wartości cmd to -
IPC_STAT- Kopiuje informacje o bieżących wartościach każdego elementu struktury msqid_ds do przekazanej struktury wskazywanej przez buf. To polecenie wymaga uprawnień do odczytu w kolejce komunikatów.
IPC_SET - Ustawia identyfikator użytkownika, identyfikator grupy właściciela, uprawnienia itp. Wskazywane przez strukturę buf.
IPC_RMID - Natychmiast usuwa kolejkę komunikatów.
IPC_INFO - Zwraca informacje o limitach i parametrach kolejki wiadomości w strukturze wskazywanej przez buf, która jest typu struct msginfo
MSG_INFO - Zwraca strukturę msginfo zawierającą informacje o zużytych zasobach systemowych przez kolejkę komunikatów.
Trzeci argument, buf, jest wskaźnikiem do struktury kolejki komunikatów o nazwie struct msqid_ds. Wartości tej struktury będą używane do ustawiania lub pobierania zgodnie z cmd.
To wywołanie zwróci wartość w zależności od przekazanego polecenia. Sukces IPC_INFO i MSG_INFO lub MSG_STAT zwraca indeks lub identyfikator kolejki komunikatów lub 0 dla innych operacji i -1 w przypadku niepowodzenia. Aby poznać przyczynę niepowodzenia, sprawdź zmienną errno lub funkcją perror ().
Po zapoznaniu się z podstawowymi informacjami i wywołaniami systemowymi dotyczącymi kolejek komunikatów, przyszedł czas na sprawdzenie w programie.
Zobaczmy opis, zanim spojrzymy na program -
Step 1 - Utwórz dwa procesy, jeden służy do wysyłania do kolejki komunikatów (msgq_send.c), a drugi do pobierania z kolejki komunikatów (msgq_recv.c)
Step 2- Tworzenie klucza za pomocą funkcji ftok (). W tym celu początkowo tworzony jest plik msgq.txt, aby uzyskać unikalny klucz.
Step 3 - Proces wysyłania wykonuje następujące czynności.
Odczytuje ciąg znaków wejściowych od użytkownika
Usuwa nową linię, jeśli istnieje
Wysyła do kolejki wiadomości
Powtarza proces do końca wprowadzenia (CTRL + D)
Po odebraniu końca danych wejściowych wysyła komunikat „koniec” oznaczający koniec procesu
Step 4 - W trakcie odbierania wykonuje następujące czynności.
- Odczytuje wiadomość z kolejki
- Wyświetla dane wyjściowe
- Jeśli otrzymana wiadomość to „koniec”, kończy proces i wychodzi
Aby uprościć, w tym przykładzie nie używamy typu wiadomości. Ponadto jeden proces zapisuje w kolejce, a inny czyta z kolejki. Można to w razie potrzeby rozszerzyć, tj. Idealnie byłoby, gdyby jeden proces zapisywał do kolejki, a wiele procesów odczytywało z kolejki.
Teraz sprawdźmy proces (wysyłanie wiadomości do kolejki) - Plik: msgq_send.c
/* Filename: msgq_send.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define PERMS 0644
struct my_msgbuf {
long mtype;
char mtext[200];
};
int main(void) {
struct my_msgbuf buf;
int msqid;
int len;
key_t key;
system("touch msgq.txt");
if ((key = ftok("msgq.txt", 'B')) == -1) {
perror("ftok");
exit(1);
}
if ((msqid = msgget(key, PERMS | IPC_CREAT)) == -1) {
perror("msgget");
exit(1);
}
printf("message queue: ready to send messages.\n");
printf("Enter lines of text, ^D to quit:\n");
buf.mtype = 1; /* we don't really care in this case */
while(fgets(buf.mtext, sizeof buf.mtext, stdin) != NULL) {
len = strlen(buf.mtext);
/* remove newline at end, if it exists */
if (buf.mtext[len-1] == '\n') buf.mtext[len-1] = '\0';
if (msgsnd(msqid, &buf, len+1, 0) == -1) /* +1 for '\0' */
perror("msgsnd");
}
strcpy(buf.mtext, "end");
len = strlen(buf.mtext);
if (msgsnd(msqid, &buf, len+1, 0) == -1) /* +1 for '\0' */
perror("msgsnd");
if (msgctl(msqid, IPC_RMID, NULL) == -1) {
perror("msgctl");
exit(1);
}
printf("message queue: done sending messages.\n");
return 0;
}
Kroki kompilacji i wykonywania
message queue: ready to send messages.
Enter lines of text, ^D to quit:
this is line 1
this is line 2
message queue: done sending messages.
Poniżej znajduje się kod z procesu odbierania wiadomości (pobierania wiadomości z kolejki) - Plik: msgq_recv.c
/* Filename: msgq_recv.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define PERMS 0644
struct my_msgbuf {
long mtype;
char mtext[200];
};
int main(void) {
struct my_msgbuf buf;
int msqid;
int toend;
key_t key;
if ((key = ftok("msgq.txt", 'B')) == -1) {
perror("ftok");
exit(1);
}
if ((msqid = msgget(key, PERMS)) == -1) { /* connect to the queue */
perror("msgget");
exit(1);
}
printf("message queue: ready to receive messages.\n");
for(;;) { /* normally receiving never ends but just to make conclusion
/* this program ends wuth string of end */
if (msgrcv(msqid, &buf, sizeof(buf.mtext), 0, 0) == -1) {
perror("msgrcv");
exit(1);
}
printf("recvd: \"%s\"\n", buf.mtext);
toend = strcmp(buf.mtext,"end");
if (toend == 0)
break;
}
printf("message queue: done receiving messages.\n");
system("rm msgq.txt");
return 0;
}
Kroki kompilacji i wykonywania
message queue: ready to receive messages.
recvd: "this is line 1"
recvd: "this is line 2"
recvd: "end"
message queue: done receiving messages.