Üzenetsorok (message queues)
Folyamatok közti kommunikáció üzenetek (kis adatcsomagok) küldözgetésével...
Az
üzenetsor adatainak lekérdezése, módosítása és törlése (msgctl)
Posix
műveletek üzenetsorokkal
Lecsatlakozás az üzenetsorról
(mq_unlink)
Üzenetsor tulajdonságainak
lekérdezése (mq_getattr)
Üzenetsor tulajdonságainak
beállítása (mq_setattr)
Névvel ellátott csővezeték (FIFO)
A folyamatok közti kommunikáció egyik leghatékonyabb módja az üzenetsorok használata. Az üzenetsorokat a rendszer
magja felügyeli. Egy ilyen sorban az adatok (vagyis az üzenetek) cirkulárisan
közlekednek, a folyamatok közti szinkronizáció pedig a előállító/fogyasztó
elv alapján valósul meg. Magyarul: ha az üzenetsor megtelt, az előállító
leáll, ameddig egy fogyasztó ki nem olvas egy üzenetet, illetve ha az üzenetsor
kiürült, a fogyasztónak kell várnia az első (neki címzett) üzenet
érkezéséig.
Tehát az üzenetsor egy, a rendszer által tárol
láncolt lista, amelynek jól meghatározott azonosítója van. Valamely folyamat
egy üzenetsort egy kulcs segítségével
azonosít. (Vigyázat! Nem tévesztendő össze az azonosító és a kulcs. A
kulcsot mi választjuk, az azonosítót pedig a rendszer minden üzenetsorhoz
automatikusan rendeli hozzá.)
Az a folyamat, amely üzenetet szeretne küldeni, előbb az adott kulcs
alapján az msgget függvénnyel lekéri az üzenetsor
azonosítóját, majd az msgsnd függvény
segítségével elküldi az üzenetet. Az üzenet a sor végére kerül.
Az a folyamat, amely üzenetet szeretne fogadni, ugyanúgy lekérdezi a kulcs
és az msgget függvény segítségével az üzenetsor
azonosítóját, majd az msgrcv függvénnyel
kiolvassa az üzenetet. Az üzenetsorból való olvasás nem feltétlenül a FIFO
módszer szerint történik, ugyanis az mtype
mező értékétől függően az üzeneteket tetszőleges sorrendben is
kiolvashatjuk.
Egy üzenet szerkezete az msg.h
állományban a következőképpen van definiálva:
struct
msgbuf
{
long mtype;
char mtext[1];
};
Az mtype mező egy hosszú egész számot
tartalmaz, amely az üzenet típusát jelképezi. Ezután következik a
tulajdonképpeni üzenet (mtext), amely jelen
esetben egyetlen karakterből áll. Amennyiben ennél hosszabb üzenetet
szeretnénk egyszerre küldeni, mi is definiálhatunk hasonló szerkezetű
struktúrákat. Például:
struct
msg
{
long tip;
char uzenet[256];
};
Amint látjuk, az üzenetek szerkezete nem rögzített, de kötelezően
tartalmazniuk kell a típust és magát az üzenetet. Ez utóbbi hossza mindig az
alkalmazástól függ.
Minden üzenetsorhoz a rendszer hozzárendel egy msqid_ds típusú struktúrát, amelynek szerkezete:
struct
msqid_ds
{
struct ipc_perm msg_perm; /* definiálja a jogokat és a
tulajdonost */
struct msg *msg_first;
/* mutató az első üzenetre */
struct msg *msg_last; /* mutató az
utolsó üzenetre */
ulong
msg_cbytes; /* sor
bájtjainak száma */
ulong
msg_qnum; /* üzenetek
száma a sorban */
ulong msg_qbytes;
/* maximális
bájtok száma sorban */
pid_t
msg_lspid; /* utolsó
msgsnd() pid-je */
pid_t msg_lrpid;
/* utolsó
msgrcv() pid-je */
time_t msg_stime;
/* utolsó
msgsnd() ideje */
time_t msg_rtime;
/* utolsó
msgrcv() ideje */
time_t msg_ctime;
/* utolsó
struktúramódosítás ideje */
};
ahol az ipc_perm struktúra a következőképpen
definiált:
struct
ipc_perm
{
uid_t
uid;
/* effektív
felhasználó ID-je */
gid_t
gid;
/* effektív
csoport ID-je */
uid_t
cuid;
/* effektív
létrehozó felhasználó ID-je */
gid_t
cgid;
/* effektív
létrehozó csoport ID-je */
mode_t
mode;
/* hozzáférési
jogok */
ulong
seq;
/* numǎr de secvenţǎ utilizare
slot */
key_t
key;
/* kulcs
*/
};
Egy folyamat csak akkor férhet hozzá egy üzenetsorhoz ha:
- a folyamat a
superuser-é,
- a felhasználó
ID-je (uid) megegyezik az msg_perm.cuid-vel vagy az msg_perm.uid-vel
és az msg_perm.mode tartalmazza a kívánt
jogokat,
- a felhasználó
csoportazonosítója (gid) megegyezik az msg_perm.cgid vagy msg_perm.gid
értékek egyikével és az msg_perm.mode
tartalmazza a kívánt jogokat,
- a felhasználó
beleesik a "többi felhsználó" kategóriába és az msg_perm.mode tartalmazza a megfelelő jogokat.
Létrehozás (msgget)
Az msgget rendszerfüggvény engedélyezi a
folyamatnak, hogy létrehozzon egy üzenetsort felhasználva egy bizonyos kulcsot.
Alakja:
#include
<sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int
msgget(key_t key, int flg);
A függvény visszatérési értéke az újonnan létrehozott vagy egy régi
üzenetsor azonosítója, ha a művelet sikerült, különben -1. A key változóban az üzenetsorhoz rendelt kulcsot kell
megadni, míg az flg-ben a létrehozási tevékenységet és a
hozzáférési jogokat. A kulcsot az üzenetsorhoz kapcsolódó összes folyamatnak
ismernie kell.
Egy új üzenetsor létrehozása esetén az flg mezőt a következő formában kell
megadni:
IPC_CREAT | IPC_EXCL | hozzáférési_jogok
Példaként hozzunk létre egy 2003 kulccsal rendelkező üzenetsort, és
adjunk hozzá írási és olvasási jogot minden felhasználónak.
#define
KEY 2003
int
msgid;
msgid = msgget((key_t) KEY, IPC_CREAT | 0666);
Ha az üzenetsor már létezik, de meg szeretnénk határozni az azonosítóját
(ID-ját), akkor az flg mezőbe 0-t írunk
– ez esetben a függvény nem fog létrehozni új sort.
msgid
= msgget((key_t) KEY, 0);
Létrehozáskor a társított adatstruktúra (msg_perm) mezői a következő információkkal
töltődnek fel:
- msg_perm.cuid,
msg_perm.uid – az msgget függvényt
meghívó folyamathoz hozzárendelt felhasználó ID-ja,
- msg_perm.cgid, msg_perm.uid
– az msgget függvényt meghívó folyamathoz hozzárendelt felhasználócsoport
ID-ja,
- msg_perm.mode – az msgget függvény
hívásakor megadott flg argumentum, amely a hozzáférési jogokat tartalmazza,
- msg_qnum, msg_lspid,
msg_lrpid, msg_stime,
msg_rtime – értéke 0,
- msg_ctime – az aktuális időt
tartalmazza,
- msg_qbytes – azt a legnagyobb
megengedett értéket tartalmazza, amely a rendszer létrehozásakor volt rögzítve.
Az msgget függvény hívásakor visszaadott azonosítót
az üzenetsorral dolgozó függvények használják.
Az üzenetsor adatainak lekérdezése, módosítása és
törlése (msgctl)
Az msgctl függvény az üzenetsorok szintjén az
információk lekérdezésére, módosítására és törlésére használható. Szintaxisa:
#include
<sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int
msgctl(int msgid, int cmd, struct msqid_ds *buf);
A függvény visszatérési értéke 0, ha a művelet sikeres volt, ellenkező
esetben -1. Az msgid paraméter az msgget függvény által meghatározott üzenetsor
azonosítója.
A cmd argumentum a kívánt műveletet
határozza meg és a következő értékeket veheti fel:
- IPC_STAT – az
üzenetsorhoz rendelt struktúra tartalma a buf
változóba kerül,
- IPC_SET – az
üzenetsorhoz rendelt struktúrát frissíti a buf
által megadott struktúrával,
- IPC_RMID
– az üzenetsorhoz rendelt struktúrát törli; a
művelet azonnali és minden folyamat, amely ezt a sort használja az errno=EIDRM üzenetet kapja; ez esetbe a buf argumentumnak a NULL értéket kell adni.
Példa:
#define
KEY 2003
msgid
= msgget((key_t) KEY, 0);
msgctl(msgid, IPC_RMID, NULL);
Az msgsnd függvény egy már létrehozott üzenetsorba
ír egy adott üzenetet. Szintaxisa:
#include
<sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int
msgsnd(int msgid, const void *addr, size_t nbytes, int flg);
A függvény visszatérési értéke 0, ha a művelet sikeres volt,
ellenkező esetben -1. Az msgid
az msgget által meghatározott üzenetsor azonosítója.
Az addr pointer típusú argumentum az üzenetre
mutat. Ez lehet void vagy msgbuf típusú. Az előbbi esetben az üzenet
szerkezete tetszőleges lehet. Az nbytes
megadja az üzenet tartalmának a hosszát (nem az egész üzenetét!). Az flg paraméterben megadhatjuk, hogy a rendszer hogyan
viselkedjen, ha az üzenetet nem lehet beírni a sorba. Például, ha a sor megtelt
és az IPC_NOWAIT opciót választottuk, akkor a folyamat nem áll le, hanem
visszatér a függvényből, és az errno
az EAGAIN hibaüzenetet fogja tartalmazni.
A következő példa egy üzenetnek a sorba való beírását mutatja be:
#define
KEY 2003
struct msgbuf uzenet;
char *uzen = "probauzenet";
msgid
= msgget((key_t) KEY, 0);
uzenet.mtype = 100;
strcpy(uzenet.mtext, uzen);
msgsnd(msgid, &uzenet, strlen(uzen), IPC_NOWAIT);
Az msgrcv
függvény feladata kiolvasni egy üzenetet az
üzenetsorból. A paraméterek segítségével megadhatjuk, hogy
milyen típusú üzeneteket fogadjon. Alakja:
#include
<sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int
msgrcv(int msgid, void *addr, size_t nbytes, long msgtype, int flg);
A függvény
sikeres olvasás esetén visszaadja a kiolvasott bájtok számát, különben a -1
értéket. Az msgid az msgget
által meghatározott üzenetsor azonosítója. Az addr pointer a kapott üzenetre mutat.
Az nbytes a lekérdezett üzenet maximális hossza
bájtokban. Az üzenet valós mérete különbözhet az nbytes-ban megadottól. Ha az
üzenet hossza kisebb a megadott maximális méretnél, akkor minden OK. Ha viszont
átlépi az nbytes határt, akkor két eset lehetséges:
a. Ha az MSG_NOERROR be van állítva, az üzenet megcsonkul, de nem kapunk
hibaüzenetet.
b. Ha az MSG_NOERROR nincs beállítva, a függvény nem olvassa ki az
üzenetet, a függvény hibát ad, és az errno
változó értéke E2BIG lesz.
Az msgtype paraméter határozza meg a kiolvasandó
üzenet típusát. 3 eset lehetséges:
a. msgtype =
0 – a sor
első üzenetét olvassa ki,
b. msgtype
> 0 – a
sor első msgtype típusú üzenetét olvassa ki,
c. msgtype
< 0 – a
sor legkisebb, de az abs(msgtype)-nál nagyobb vagy azzal egyenlő
értékű üzenetét olvassa ki.
Az flg argumentummal megadhatjuk, hogy a
rendszer hogyan viselkedjen, ha a függvény meghívásának feltételei nem
teljesülnek. Az előbb bemutattuk az MSG_NOERROR használatát. Következzen
az IPC_NOWAIT-é. Szintén két eset lehetséges:
a. Ha az IPC_NOWAIT be van állítva, a folyamat nem várja meg az óhajtott
üzenetnek a sorban való elhelyezését. A függvény hibát ad, és az errno értéke ENOMSG lesz.
b. Ha az IPC_NOWAIT nincs beállítva, a folyamat addig várakozik, ameddig
kerül egy megfelelő üzenet, ameddig a sor megsemmisül (errno=EIDRM), vagy egy jelzés érkezik (errno=EINTR).
A következő programrészlet példa egy üzenet kiolvasására:
#define
KEY 2003
struct msgbuf uzenet;
msgid
= msgget((key_t) KEY, 0);
msgrcv(msgid, &uzenet, 25, long(100), IPC_NOWAIT | MSG_NOERROR);
Ha az üzenetsorba bekerülő adatok típusai megegyeznek, akkor az
üzenetsorok tulajdonképpen FIFO állományként működnek.
Példa(Letöltés)
Hozzunk létre egy kliens-szerver rendszert! A kliensfolyamat a parancssorból beolvas egy tevékenységkódot (pl.
kliens 2), majd ezt egy üzenetsoron keresztül elküldi a szervernek. A
visszakapott választ a képernyőre írja. A szerverfolyamat kiolvassa az üzenetsorból a tevékenységkódot,
elvégzi a tevékenységet, majd jelentést küld a kliensnek.
Az egyszerűség kedvéért a közös adatokat (így az üzenet struktúráját
is) egy külön fejléc állományban tároljuk, amelyet mind a kliens, mind a
szerver el tud majd érni.
mes.h
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define KEY 958320 /*
az üzenetsor kulcsa */
#define READ 1 /*
tevékenységkódok elnevezései */
#define WRITE 2
typedef struct{ /* az üzenet struktúrája */
long mtip; /* üzenet típusa */
int pid; /* küldő folyamat azonosítója */
int cod_op; /* kliens üzenete; tevékenységkód */
char mtext[13]; /* szerver üzenete; tev. elnevezése
*/
}
MESSAGE;
A kliens első lépésben a
parancssorból beolvas egy tevékenységkódot. Egyetlen üzenetsort
használunk, amelyen keresztül minden kliens elküldheti
a kéréseit a szerver felé, és amelyen a válasz is érkezik. Azért, hogy egy
kliens megkapja a neki címzett választ, csak azon üzeneteket fogja kiolvasni a
sorból, amelyek típusa éppen megegyezik a saját pid-jével. Éppen ezért a
kéréssel egyidőben minden kliensnek el kell
küldenie az azonosítóját is.
kliens.c
#include "mes.h" /* a közös mes állomány */
int msgid; /* üzenetsor azonosítója */
void main(int argc, char **argv)
{
pid_t pid; /* folyamatazonosító */
MESSAGE mesp; /*
üzenet */
if (argc != 2){ /* hibás argumentumok */
printf("hasznalat:
c <cop_op>\n");
exit(1);
}
if ((msgid =
msgget((key_t) KEY, 0666)) < 0){
perror("In
msgget()"); /* üzenetsor ID-jánek
lekérdezése */
exit(1);
}
mesp.mtip = 1; /* a kérés típusa mindig 1 */
mesp.cod_op =
atoi(argv[1]); /* tevékenység kódja
parancssorból */
pid = mesp.pid =
getpid();
printf("a %d
kliens elkuldte a kerest\n", mesp.pid);
if (msgsnd(msgid, (struct
msgbuf *)&mesp, sizeof(mesp)-sizeof(long), 0) < 0){
perror("In
msgsnd()"); /* tev.kód elküldése a
szervernek */
exit(1);
}
if (msgrcv(msgid, (struct
msgbuf *)&mesp, sizeof(mesp)-sizeof(long), pid, 0) < 0){
perror("In
msgrcv()"); /* tev.név fogadása a
szervertől */
exit(1);
}
mesp.mtext[12] =
'\0'; /* eredmény előkészítése,
kiíratás */
printf("a %d
szerver a %d kliensnek %s kerest kuldott\n", mesp.pid, pid, mesp.mtext);
}
A
szerverfolyamat kap a klienstől egy 1-es típusú üzenetet, majd egy olyan
választ készít, amelynek típusa megegyezik a kérést küldő kliens
pid-jével. Az alábbi példában a szerver által felkínált szolgáltatások a
következők: READ és WRITE. Ezek végrehajtását a szerver csak szimulálja. A
választ az mtext mező fogja tartalmazni, amely
megmutatja, hogy a szervernek sikerült-e azonosítania a tevékenységet vagy sem.
szerver.c
#include "mes.h" /* a közös mes állomány */
int msgid; /* üzenetsor azonosítója */
void do_it(MESSAGE *mesp) /* tev.kód -> tev.elnevezés */
{
switch
(mesp->cod_op) /* a kliens által
küldött kód */
{
case READ: /* READ = 1 tev. elnevezése
*/
strcpy(mesp->mtext,
"READ\n");
break;
case WRITE: /* WRITE = 2 tev. elnevezése
*/
strcpy(mesp->mtext,
"WRITE\n");
break;
default: /*
ismeretlen tev.kód */
strcpy(mesp->mtext, "Ismeretlen\n");
break;
}
}
void main(void)
{
MESSAGE mesp; /*
üzenet */
if ((msgid =
msgget((key_t) KEY, IPC_CREAT | 0666)) < 0){
perror("In
msgget()"); /* üzenetsor létrehozása
*/
exit(1);
}
while(1) /* végtelenciklus */
{
if
(msgrcv(msgid, &mesp, sizeof(mesp)-sizeof(long), 1L, 0) < 0){
perror("In
msgrcv()"); /* tev.kód kiolvasása a
sorból */
exit(1);
}
do_it(&mesp); /* átalakítás */
mesp.mtip =
mesp.pid; /* kliens kódja = üzenet
típusa */
mesp.pid =
getpid();
if
(msgsnd(msgid, &mesp, sizeof(mesp)-sizeof(long), 0) < 0){
perror("In
msgsnd()"); /* tev. elnevezés küldése */
exit(1);
}
}
}
Makefile(Fedora
disztribúció):
all: kliens szerver
kliens: kliens.c
gcc kliens.c -o kl
szerver: szerver.c
gcc szerver.c -o sz
clean: rm sz kl
.PHONY: all clean
A fenti példa
egy tesztesetre a következőképpen működik:
$ szerver
&
$ kliens 1 & kliens 4
a
995 kliens elkuldte a kerest
a 768 szerver a 995 kliensnek READ kerest kuldott
a
999 kliens elkuldte a kerest
a 768 szerver a 999 kliensnek ismeterlen kerest kuldott
A posix szabvány
szerint az üzenetsorok struktúrája az <mqueue.h> header állományban
található.
Típusa : mqd_t
Üzenetsor struktúrája(mq_attr):
Minden üzenetsorhoz van rendelve egy struktúra ami
tartalmazza a következő adatokat:
long mq_flags: Az
illető üzenetsor beállításait tartalmazza. Beállítása a mq_setattr() fg-el
lehetséges
long mq_maxmsg: Az
üzenetsorban tárolható üzenetek maximális száma. Ennek a mezőnek a beállítása
a létrehozás során történik
long mq_msgsize: Az üzenetek mérete
long mq_curmsgs: Megadja
az üzenetsorba található üzenetek számát
long mq_sendwait: Megadja
azon folyamatok számát amelyek üzenetküldésre várakoznak. Amennyiben az értéke
nem 0, azt jelenti, hogy az üzenetsor tele van
long mq_recvwait: Megadja
azon folyamatok számát amelyek üzenet érkezésére várakoznak. Amennyiben ez a
szám nem 0, az üzenetsor üres
Az mq_open() rendszerfüggvény megnyit vagy létrehoz
egy üzenetsort.
Szintaxis:
#include <mqueue.h>
mqd_t mq_open(
const char *name, int oflag, [mode_t mode], [mq_attr *attr] )
Megnyitja a name által jelölt üzenetsort a flagek
szerint majd visszatéríti az üzenetsor descriptorat
Paraméterek:
name: Az üzenetsor neve( /-el kezdődik).
oflag: Valamelyik az alábbiak közül:
O_RDONLY
(receive-only)
O_WRONLY
(send-only)
O_RDWR (send-receive)
További
flagek:
O_CREAT:
Akkor adjuk meg ha új üzenetsort szeretnénk létrehozni. Amennyiben meg van ez
adva, akkor az utolsó két paraméter is
számit. Csak akkor ad hibát ha a name által jelölt üzenetsor létre volt
hozva és minden folyamat unlink-elte (mq_unlink()) de nem zárták (mq_close())
be.
O_EXCL: Hogyha
az O_CREAT-al együtt megadjuk akkor létező üzenetsor esetén hibát ad.
Semmi értelme magába megadni
O_NONBLOCK: Nem blokkoló mód. Alapértelmezetten akkor blokkol hogyha az
üzenetsor tele van vagy üres.
mode: A
jogosultságok beállítása, pl. : 0666 (Megj.: végrehajtási jogosultság nincs
figyelembe véve).
attr: Ez
egy pointer az mq_attr struktúrára amely tartalmazza az üzenetsor
tulajdonságait. NULL pointer esetén az alapértelmezett beállításokat veszi
alapul:
Struct mq_attr{
mq_maxmsg
1024
//ennyi üzenetet tartalmazhat
mq_msgsize
4096
// egy üzenet mérete
mq_flags 0
// Posix implementációba nem szükséges megadni
}
Példa:
char* name = argv[1];
int flags = O_RDWR
| O_CREAT; //flagek beállítása
mode_t mode = 0666; //jogok
beállítása
struct mq_attr attr; //struktura
létrehozása és beállítása
attr.mq_maxmsg = 10;
attr.mq_msgsize = sizeof(Msg);
mqd_t qid = mq_open(name, flags, mode, &attr);//Üzenetsor
létrehozása
Eredmény:
Ha sikerült, visszaküldi az üzenetsor
deszkriptorát különben -1 es az errno megfelelően be lesz állítva.
A mq_close() rendszerfüggvény bezár egy létező üzenetsort
Szintaxis:
#include
<mqueue.h>
Int mq_close( mqd_t mqdes );
Példa:
mq_close(qid); //üzenetsor bezárása
Eredmény:
0 ha sikeresen bezárta különben -1 és az errno megfelelően be lesz állítva
A mq_send()
rendszerfüggvény betesz egy üzenetet egy létező üzenetsorba.
Szintaxis:
#include
<mqueue.h>
int
mq_send( mqd_t mqdes,
const
char *msg_ptr,
size_t
msg_len,
unsigned
int msg_prio );
Az mq_send()
beteszi a msg_len méretű msq_ptr által mutatott üzenetet msg_prio
prioritással az mqdes deszkriptorú üzenetsorba
Az üzenetek rendezve vannak a prioritásuk szerint (0-tól MQ_PRIO_MAX-ig). Ezen
belül pedig FIFO sorrendbe.
Ha az üzenetsor megtelt és az O_NONBLOCK nem volt megadva, az mq_send()
blokkolja a folyamatot egész addig amíg nem lesz hely.
Példa:
Msg
msg = {rand() % 100, rand() % 100, rand() % 100, 0};
int prio =
rand() % MQ_PRIO_MAX;
if
(mq_send(qid, (char*) &msg, sizeof(Msg), prio) == -1) //üzenet
küldése
{
perror("Failed to send msg");
}
Eredmeny:
0 ha sikeresen
bezárta különben -1 és az errno megfelelően be lesz állítva
Üzenet fogadása (mq_receive())
Az mq_receive() rendszerfüggvény kiolvas egy üzenetet egy létező üzenetsorból.
Szintaxis:
#include
<mqueue.h>
int
mq_receive( mqd_t mqdes,
char
*msg_ptr,
size_t
msg_len,
unsigned
int *msg_prio );
Az mq_receive() kiolvas egy msg_len
méretű üzenetet az mqdes deszkriptorú üzenetsorból az msg_ptr által
mutatott helyre. A msg_prio arra az egészre (integer) mutat amelybe a
prioritást fogjuk megkapni
mq_receive() abban az esetben blokkol ha:
-nincs üzenet
- O_NONBLOCK nem volt bealítva az
mq_open() hívásakor.
Ha nem érdekel az üzenet prioritása akkor az
msg_prio NULL.
Példa:
Msg
msgr = {0,0,0,0};
if
(mq_receive(qid, (char*) &msgr, sizeof(msgr), 0) != -1) //válasz fogadása
{
printf("szum
= %d\n", msgr.szum);
}
else
{
perror("mq_receive");
return(1);
}
Eredmény:
Kiolvasott
üzenet mérete ha sikerult.
-1 ha nem sikerült.
Lecsatlakozás az üzenetsorról
(mq_unlink ())
Az mq_unlink() rendszerfüggvény lecsatlakozik az üzenetsorról
Szintaxis:
#include
<mqueue.h>
int
mq_unlink( const char *name );
Megjegyzés:
mq_close()-al
ellentétben nem törli az üzenetsort, csak lecsatlakozik róla.
Példa:
mq_unlink(argv[1]); //üzenetsor torlése
Eredmény:
0:
sikeres
-1:
sikertelen és errno-t beállítja
Üzenetsor tulajdonságainak
lekérdezése (mq_getattr())
Az mq_getattr() rendszerfüggvény lekérdézi az üzenetsor
tulajdonságait.
Szintaxis:
#include
<mqueue.h>
int
mq_getattr( mqd_t mqdes,
struct
mq_attr *mqstat );
Lekéri az mqdes deszkriptorú üzenetsor tulajdonságait
és elmenti az mqstat pointer által mutatott struktúrába.
Példa:
struct
mq_attr temp;
if
(mq_getattr( qid, &temp) == -1 ) //Üzenetsor
tulajdonságainak lekérdezése
perror("getattr:");
else
printf("maxmsg
= %d\n", temp.mq_maxmsg);
Eredmény:
0:
sikeres
-1:
sikertelen és errno-t beállítja
Üzenetsor tulajdonságainak beállítása
(mq_setattr())
Az mq_setattr() rendszerfüggvény beállítja az üzenetsor
tulajdonságait.
Szintaxis:
#include
<mqueue.h>
int
mq_setattr( mqd_t mqdes,
const
struct mq_attr *mqstat,
struct
mq_attr *omqstat );
Beállítja
az mqdes tulajdonságait az mqstat pointer által mutatott struktúra szerint.
Amennyiben az omqstat nem NULL, az omqstat által mutatott struktúra megkapja a
régi beállítások.
Az mq_maxmsg és mq_msgsize mezők figyelmen kívül maradnak.
Példa:
struct
mq_attr newattr;
//adatok beálitása
if
(mq_setattr(qid,&newattr,NULL) == -1)
perror("setattr:")
Eredmény:
0:
sikeres
-1:
sikertelen és errno-t beállítja
Példa(Letöltés)
Feladat:
Sender.C
gombnyomásra küld három számot véletlen prioritással egészen addig amíg be nem
olvasunk egy q betűt. Miután elküldte a három számot, megvárja a választ
és kiírja.
Receiver.C:
kiolvassa az üzenetet(három szám) és visszaküldi az összeget. Ha a kapott
üzenet első eleme -1, leáll
Msg.h:
#ifndef
MSG_H
#define
MSG_H
typedef
struct
{
unsigned int x;
unsigned int y;
unsigned int z;
unsigned int szum;
} Msg;
#endif
Sender.C:
#include
"Msg.h"
#include <mqueue.h>
#include
<stdbool.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<time.h>
#include
<errno.h>
#include
<string.h>
#include
<limits.h> // MQ_PRIO_MAX
int
main(int argc, char** argv)
{
if (argc != 2)
{
printf("Usage: %s /qname\n", argv[0]);
return 1;
}
char* name =
argv[1];
int flags = O_RDWR | O_CREAT; //flagek
beállítása
mode_t mode = 0666; //jogok
beállítása
struct mq_attr attr; //struktura
létrehozása és beállítása
attr.mq_flags = 0;
attr.mq_maxmsg = 10;
attr.mq_msgsize =
sizeof(Msg);
attr.mq_curmsgs = 0;
mqd_t qid =
mq_open(name, flags, mode, &attr);//Üzenetsor létrehozása
if (qid == -1)
perror("mq_open():");
struct mq_attr temp;
if (mq_getattr( qid,
&temp) == -1 ) //Üzenetsor
tulajdonságainak lekérdezése
perror("getattr:");
else
printf("maxmsg
= %d\n", temp.mq_maxmsg);
srandom(time(0));
printf("Press
enter to send a message...");
getchar();
char c;
do
{
Msg msg = {random() %
100, random() % 100, random() % 100, 0};
int prio = random() %
MQ_PRIO_MAX; //Véletlenszerűen
generálja az üzenet tartalmát és prioritását
printf("sending msg = (%03d, %03d, %03d) with priority:
%d\n", msg.x, msg.y, msg.z,prio);
if (mq_send(qid, (char*) &msg, sizeof(Msg), prio) == -1)
//üzenet
küldése
{
perror("Failed to send msg");
}
else
{
Msg msgr = {0,0,0,0};
if
(mq_receive(qid, (char*) &msgr, sizeof(msgr), 0) != -1) //válasz fogadása
{
printf("szum
= %d\n", msgr.szum);
}
else
{
perror("mq_receive");
return(1);
}
printf("Press
enter to send another message...");
c = getchar();
}
} while (c != 'q');
Msg msg = {-1,0,0}; //-1 el
kezdődő üzenettel jellezuk, hogy készen vagyunk
if (mq_send(qid,
(char*) &msg, sizeof(Msg), 0) == -1)
{
perror("Failed
to send msg");
}
mq_close(qid); //üzenetsor
bezárása
mq_unlink(argv[1]); //üzenetsor
törlése
return 0;
}
Receiver.C:
#include
"Msg.h"
#define
_XOPEN_SOURCE 600
#include
<time.h>
#include
<mqueue.h>
#include
<signal.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<errno.h>
#include
<string.h>
#include
<stdbool.h>
int
main(int argc, char** argv)
{
if (argc != 2)
{
printf("Usage: %s /qname\n", argv[0]);
return 1;
}
char* name = argv[1];
int flags = O_RDWR; //flagek beállítása
mqd_t qid =
mq_open(name, flags); //Üzenetsor
megnyitása
if (qid <0)
{
perror("Failed
to open the message queue");
}
bool quit = false;
while (!quit)
{
Msg msg =
{0,0,0,0};
unsigned int
priority = 0;
if
(mq_receive(qid, (char*) &msg, sizeof(msg), &priority) != -1) //Üzenet fogadása
{
if (msg.x != -1)
{
printf("msg
= (%03d, %03d, %03d), priority: %d\n", msg.x, msg.y, msg.z, priority);
msg.szum =
msg.x + msg.y + msg.z;
if (mq_send(qid,
(char*) &msg, sizeof(Msg), 0) == -1) //válasz küldése
{
perror("Failed
to send msg");
}
}
else
{
quit=true;
}
}
else
{
perror("mq_receive");
return(1);
}
}
mq_close(qid); //üzenetsor bezárása
return 0;
}
Makefile(Fedora
disztribúció):
SRC1 = Sender.c
SRC2 = Receiver.c
OBJ1 = $(SRC1:.c=.o)
OBJ2 = $(SRC2:.c=.o)
CFLAGS = -Wall -pedantic -D_GNU_SOURCE -std=gnu99
-lrt -lmqueue
INCLUDES = -I./
LIBS = -lrt
LIBPATH =
DEP_FILE = .depend
SNDR = sndr
RCVR = rcvr
.PHONY:
all clean distclean
all:
depend $(SNDR) $(RCVR)
$(SNDR): $(OBJ1)
@echo Makefile -
linking $@
@$(CC) $(LIBPATH) $(LIBS) $^ -o $@
$(RCVR): $(OBJ2)
@echo Makefile -
linking $@
@$(CC) $(LIBPATH) $(LIBS) $^ -o $@
.c.o:
@echo Makefile -
compiling $<
@$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
clean:
$(RM) $(OBJ1) $(OBJ2)
distclean:
clean
$(RM) $(SNDR) $(RCVR)
$(RM) $(DEP_FILE)
depend:
$(DEP_FILE)
@touch $(DEP_FILE)
$(DEP_FILE):
@echo Makefile -
building dependencies in: $@
@$(CC) -E -MM $(CFLAGS) $(INCLUDES) $(SRC1)
>> $(DEP_FILE)
@$(CC) -E -MM $(CFLAGS) $(INCLUDES) $(SRC2)
>> $(DEP_FILE)
ifeq
(,$(findstring clean,$(MAKECMDGOALS)))
ifeq
(,$(findstring distclean,$(MAKECMDGOALS)))
-include
$(DEP_FILE)
endif
endif
Futtatás eredménye:
Sender.C:
$ ./sndr /uz
maxmsg = 10
Press enter to send a message...
sending msg = (059, 070, 013) with
priority: 5595
szum = 142
Press enter to send another message...
sending msg = (070, 034, 064) with
priority: 2915
szum = 168
Press enter to send another message...
sending msg = (012, 069, 085) with
priority: 8822
szum = 166
Press enter to send another message...
sending msg = (087, 091, 001) with
priority: 6281
szum = 179
Press enter to send another message...q
$
Receiver.C
$
./rcvr /uz
msg = (059, 070, 013), priority:
5595
msg = (070, 034, 064), priority:
2915
msg = (012, 069, 085), priority:
8822
msg = (087, 091, 001), priority:
6281
$
Copyright (C) Buzogány László, 2002