Ü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 nincs beá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.
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>
#define KEY
13
/* az
üzenetsor kulcsa */
#define READ
1
/* tevékenységkódok
elnevezései */
#define WRITE 2
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 hdr állomány */
#include "hdr.h"
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 */
err_quit("hasznalat: c <cop_op>");
if ((msgid =
msgget((key_t) KEY, 0666)) < 0)
err_sys("msgget
hiba");
/* üzenetsor
ID-jánek lekérdezése */
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)
err_quit("msgsnd
hiba");
/* tev.kód
elküldése a szervernek */
if (msgrcv(msgid, (struct
msgbuf *)&mesp, sizeof(mesp)-sizeof(long), pid, 0) < 0)
err_sys("msgrcv
hiba");
/* tev.név
fogadása a szervertől */
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 hdr állomány */
#include "hdr.h"
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)) < 0)
err_sys("msgget
hiba");
/* üzenetsor
létrehozása */
for
ever
/* végtelenciklus
*/
{
if (msgrcv(msgid, (struct msgbuf *)&mesp,
sizeof(mesp)-sizeof(long), 1L, 0) < 0)
err_sys("msgrcv
hiba");
/* tev.kód
kiolvasása a sorból */
do_it(&mesp);
/* átalakítás
*/
mesp.mtip =
mesp.pid;
/* kliens
kódja = üzenet típusa */
mesp.pid = getpid();
if
(msgsnd(msgid, (struct msgbuf *)&mesp, sizeof(mesp)-sizeof(long), 0) <
0)
err_sys("msgsnd
hiba");
/* tev.
elnevezés küldése */
}
}
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
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
<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