Sunteți pe pagina 1din 10

Sisteme de Operare Laborator nr.

11

Laborator nr. 11

Comunicarea inter-proces prin cozi de mesaje.

Începând cu UNIX System V, s-a pus la dispozitia utilizatorilor, în cadrul pachetului


IPC System V, un mecanism eficient de comunicare între procese. Coada de mesaje este
organizata circular si gestionata de nucleul sitemului de operare.

1. Cozi de mesaje
Sincronizarea între procesele care transmit mesaje si cele care receptioneazã se
face pe principiul producator/consumator, controlat de nucleul sistemului de operare. Acest
principiu precizeaza ca procesul producator se blocheaza daca coada de mesaje este
plina, pâna când un proces consumator preia un mesaj. La fel, procesul consumator se
blocheaza daca coada de mesaje este goala sau daca nu are în coada un mesaj transmis
lui, pâna când un proces producator îi transmite un mesaj.

O coada de mesaje este o lista înlantuita de mesaje stocata de nucleu si


identificata printr-un identificator al cozii de mesaje. Coada de mesaje se va numi pe scurt
coada si identificatorul ei ID-ul cozii. O coada de mesaje este identificata de un proces
printr-o cheie.

Procesul care doreste sa transmita un mesaj obtine pe baza cheii numerice ID-ul
cozii prin apelul funcþiei msgget, iar apoi scrie mesajul în coada, apelând functia
msgsnd. Mesajele sunt adaugate la sfârsitul cozii.
Procesul care doreste sa receptioneze un mesaj din coada obtine pe baza cheii
numerice ID-ul cozii prin acelasi apel al functiei msgget, iar apoi citeste mesajul din coada
prin apelul functiei msgrcv. Receptionarea mesajelor din coada se poate face dupa
câmpul mtype nu neaparat dupa regula primul intrat primul iesit.
Structura unui mesaj este definitã în fisierul msg.h sub forma:
struct msgbuf {
long mtype;
char mtext[1];
};
Primul câmp al structurii, mtype, contine un numar întreg lung care precizeaza tipul
mesajului. Din acest motiv s-a lasat neutilizat acest câmp la exemplele prezentate la
fisierele FIFO. Dupa acest câmp urmeaza mesajul propriu-zis, care este redus la un singur
caracter. Daca mesajul care se doreste a fi trimis se presupune a fi de 256 octeti se poate
defini o alta structura sub forma:
struct msg {
long tip;
char continut_mesaj[256];
};
Structura mesajului cu care lucreaza o aplicatie nu este impusa. Ea trebuie sa
contina doar tipul mesajului si mesajul propriu-zis, a carui dimensiune este functie de
aplicatie.
Fiecare coada are asociata urmatoarea structura msqid_ds:
struct msqid_ds {
struct ipc_perm msg_perm; /* vezi mai jos */
struct msg *msg_first; /* pointer la primul mesaj */
struct msg *msg_last; /* pointer la ultimul mesaj */
ulong msg_cbytes; /* octeþi în coadã */
ulong msg_qnum; /* nr. mesaje în coadã */
ulong msg_qbytes; /* nr.max.de octeþi din coadã*/
pid_t msg_lspid; /* pid-ul ultimului msgsnd() */
Sisteme de Operare Laborator nr. 11
pid_t msg_lrpid; /* pid-ul ultimului msgrcv() */
time_t msg_stime; /* timpul ultimului msgsnd() */
time_t msg_rtime; /* timpul ultimului msgrcv() */
time_t msg_ctime; /* timpul ultimei modificãri a structurii
prin apelul funcþiei msgctl */
};
Cei doi pointeri nu sunt utili programelor utilizator, ei refera adresele unde sunt
stocate mesajele în nucleu.
Un proces poate avea acces la o coada de mesaje daca:
• procesul este al superuser-ului;
• ID utilizatorului, uid, coincide cu msg_perm.cuid sau cu msg_perm.uid si este
prevazut în msg_perm.mode dreptul de acces dorit;
• utilizatorul are ID de grup, gid, identic cu msg_perm.cgid sau cu
msg_perm.gid si este prevazut în msg_perm.mode dreptul de acces dorit;
• utilizatorul face parte din categoria "alti utilizatori" si este prevazut în
msg_perm.mode dreptul de acces dorit.

2. Structura de date care precizeaza permisiunile


Fiecarei structuri IPC i se asociazã o structura ipc_perm.
Ea defineste permisiunile si proprietarul.
struct ipc_perm {
uid_t uid; /* ID utilizatorului efectiv */
gid_t gid; /* ID grupului efectiv */ */
uid_t cuid; /* ID utilizatorului efectiv creator */
gid_t cgid; /* ID grupului efectiv creator */
mode_t mode; /* drepturile de acces */
ulong seq; /* numãr de secventã utilizare slot */
key_t key; /* cheie */
};
Toate campurile structurii, în afara campului seq, sunt initializate la crearea structurii
IPC.
Câmpurile uid, gid si mode se pot modifica prin functia msgctl. Pentru a putea
modifica aceste câmpuri, procesul apelant trebuie sa fie procesul care a creat structurile
IPC sau superuser.
Valorile din câmpul mode sunt similare cu cele de la lucrul cu fisiere, dar nu exista
dreptul x pentru structurile IPC. Cozile de mesaje si memoria comuna folosesc termenii
'read' si 'write' iar semafoarele 'read' si 'alter'.

3. Functii de sistem pentru cozile de mesaje


3.1. Functia de sistem msgget
Functia de sistem msgget permite unui proces sa creeze o coada de mesaje
folosind o cheie. Interfata functiei msgget este urmatoarea:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget ( key_t cheie, int ind);
Functia msgget returneaza ID cozii noi sau vechi (daca coada exista) în caz de
succes, (-1) în caz de eroare.
Argumente:
cheie - cheia atasata cozii de mesaje;
ind - indica actiunea de creare si drepturile de acces la coada de mesaje.
La crearea unei cozi de mesaje, trebuie data atât cheia asociata, pe care fiecare
proces care acceseaza coada de mesaje trebuie sa o cunoasca, cât si indicatorii. Pentru
crearea unei cozi de mesaje noi, câmpul ind va fi dat sub forma:
IPC_CREAT | IPC_EXCL | drepturi_de_acces.
Exemplul 1:
Sisteme de Operare Laborator nr. 11
Se creaza o coada de mesaje având cheia atasata 1995 si drepturile de acces de
scriere si citire de mesaje pentru toti utilizatorii:
#define KEY 1995
int msgid;
msgid=msgget( (key_t) KEY, IPC_CREAT | 0666);
Daca coada de mesaje exista si se doreste numai obtinerea ID-ului ei, apelul se
face dând cheia si valoarea zero pentru indicatori:
msgid=msgget( (key_t) KEY, 0);
La creare, câmpurile structurii de date asociate sunt completate cu urmatoarele
informatii:
 msg_perm.cuid, msg_perm.uid – contin ID utilizatorului atasat procesului care a
lansat apelul msgget;
 msg_perm.cgid, msg_perm.uid – contin ID grupului de utilizatori atasat
procesului care a lansat apelul msgget;
 msg_perm.mode – contine drepturile de acces prevazute în apelul functiei msgget
de argumentul ind;
 msg_qnum, msg_lspid, msg_lrpid, msg_stime si msg_rtime – au valoarea zero;
 msg_ctime – este completat cu valoarea curenta a timpului.
 msg_qbytes – este completat cu valoarea maxima admisa, parametru fixat la
generarea sistemului.
ID-ul cozii de mesaje returnat dupa apelul functiei msgget este folosit de apelurile
care lucreaza cu cozi de mesaje.

3.2. Functia de sistem msgctl


Functia de sistem msgctl are rolul de a efectua asupra structurii asociate unei cozi
de mesaje o serie de operatii: obtinerea informatiilor din câmpurile structurii, modificarea
informatiilor din anumite câmpuri ale structurii si stergerea structurii (deci implicit a cozii de
mesaje).
Interfata functiei de sistem msgctl este urmatoarea:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl( int msgid, int cmd, struct msqid_ds *buf);
Functia msgctl returneaza 0 în caz de succes, (-1) în caz de eroare.
Argumente:
msgid – este identificatorul cozii de mesaje obtinut prin msgget
cmd – indica operatia ceruta având urmatoarele valori:
 IPC_STAT - Continutul structurii asociate cozii de mesaje este
transferat la adresa referita de buf.
 IPC_SET - Structura de date asociatã cozii de mesaje este
actualizata conform informatiilor de le adresa referita de buf.
Actualizarea poate fi facuta de un proces care apartine superuser-ului
sau unui utilizator ce are ID utilizatorului efectiv egal cu msg_perm.uid
sau msg_perm.cuid. Pot fi modificate câmpurile msg_perm.uid,
msg_perm.gid, msg_perm.mode (ultimii 9 biti care indica drepturile de
acces). Câmpul msg_qbytes poate fi modificat numai de un proces al
superuser-ului.
 IPC_RMID Structura de date asociata cu coada de mesaje este
stearsa. Operatia este imediata si toate procesele ce folosesc coada
vor primi eroare cu errno=EIDRM. Operatia poate fi facuta de un
proces al superuser-ului sau de un proces care are ID utilizatorului
efectiv egal cu msg_perm.uid sau msg_perm.cuid. În acest caz
argumentul buf din functia msgctl va avea valoarea NULL.
Sisteme de Operare Laborator nr. 11
Exemplu de utilizare a functiei msgctl:
#define KEY 1995
...
msgid=msgget ((key_t) KEY, 0);
msgctl( msgid, IPC_RMID, NULL);
...

3.3. Functia de sistem msgsnd


Rolul functiei de sistem msgsnd este de a scrie un mesaj în coada de mesaje
anterior creata. Interfata functiei de sistem msgsnd este urmatoarea:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd( int msgid,const void *p,size_t nbytes, int ind);
Functia msgsnd returneaza 0 în caz de succes, (-1) în caz de eroare.
Argumente:
msgid – este identificatorul cozii de mesaje obtinut prin apelul functiei
msgget;
p – este un pointer la tipul void sau la o structura de tipul msgbuf (în
versiunile anterioare), care contine mesajul. Antetul mesajului conþine
tipul sau - un numãr întreg - pe baza caruia un proces poate sa
receptioneze un mesaj de un anumit tip, asa cum s-a prezentat anterior.
Noile versiuni folosesc tipul void pentru a putea transmite orice
structura de mesaj fara cast.
nbytes – este dimensiunea continutului mesajului, nu si a antetului;
ind – arata actiunea nucleului daca mesajul nu poate fi scris în coada de
mesaje.
Astfel, daca coada de mesaje este plina si este selectat indicatorul IPC_NOWAIT,
procesul nu se blocheaza în asteptarea scrierii, ci are loc revenirea din functie cu eroare
iar variabila errno va avea valoarea EAGAIN. Daca indicatorul IPC_NOWAIT nu este
setat, atunci daca coada de mesaje este plina, procesul este blocat pâna când un proces
receptor preia un mesaj sau coada este distrusa (errno=EIDRM) sau procesul primeste un
semnal (errno=EINTR).
Secventa urmatoare prezinta un exemplu de scriere a unui mesaj într-o coada:
#define KEY 1995
struct msgbuf mesaj;
char *mes="Mesaj de test";
...
msgid=msgget( (key_t) KEY, 0);
mesaj.mtype = 100; /* tipul mesajului */
strcpy( mesaj.mtext, mes); /* conþinutul mesajului */
msgsnd( msgid, &mesaj, strlen(mes), IPC_NOWAIT);
...

3.4. Functia de sistem msgrcv


Rolul functiei de sistem msgrcv este de a citi un mesaj din coada de mesaje. În
functie de parametrii de apel ai functiei, se poate prelua numai mesajul de un anumit tip.
Interfata functiei de sistem msgrcv este:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msgid,void *p,size_t nbytes,long tip,int ind);
Functia msgrcv returneaza dimensiunea zonei de date din mesaj în caz de succes,
(-1) în caz de eroare.
Argumente:
msgid – este identificatorul cozii de mesaje obtinut prin apelul funcþiei de
Sisteme de Operare Laborator nr. 11
sistem msgget,
p – este un pointer la mesajul care se receptioneaza
nbytes – este dimensiunea maxima a mesajului care se citeste (în octeti).
Dimensiunea efectiva a mesajului poate fi diferita de cea indicata de
catre procesul receptor. Daca mesajul are o dimensiune mai mica
decât nbytes, atunci nu este nici o problema. În schimb daca mesajul are o
dimensiune mai mare decât nbytes, atunci exista 2 cazuri:
a) indicatorul MSG_NOERROR este setat. În acest caz mesajul
este trunchiat la nbytes iar procesul continua fara a fi avertizat;
b) indicatorul MSG_NOERROR nu este setat. În acest caz, nu
se citeste mesajul, functia returneaza eroare iar variabila errno va
avea valoarea E2BIG.
tip – indica tipul mesajului dorit a fi receptionat. Exista 3 cazuri:
a) tip = 0. În acest caz este citit primul mesaj din coada.
b) tip > 0. În acest caz este citit primul mesaj din coada care are tipul
dat de tip.
c) tip < 0. În acest caz este citit primul mesaj din coada care are tipul
cel mai mic, dar mai mare sau egal cu abs(tip).
ind – arata modul de actiune în cazul în care nu sunt îndeplinite conditiile din
apelul functiei. Anterior s-a prezentat indicatorul MSG_NOERROR. În
continuare se prezinta indicatorul IPC_NOWAIT. În cazul în care în
coada de mesaje nu exista mesajul cu tipul curent, exista doua cazuri:
a) Indicatorul IPC_NOWAIT este setat. În acest caz procesul nu
este blocat în asteptarea depunerii în coada unui mesaj dorit,
functia returneaza eroare si variabila errno va avea valoarea
ENOMSG.
b) Indicatorul IPC_NOWAIT nu este setat. În acest caz procesul
este blocat pâna când un proces depune un mesaj cu tipul
dorit, sau pâna când coada este distrusa (errno=EIDRM) sau pîna
când se primeste un semnal (errno=EINTR).

Secventa urmatoare prezinta un exemplu de citire a unui mesaj:


#define KEY 1995
struct msgbuf mes;
...
msgid=msgget( (key_t) KEY, 0);
msgrcv( msgid, &mes, 25, long(100), IPC_NOWAIT | MSG_NOERROR);
/* prelucrarea mesajului mesaj.mtext */
...

4. Probleme rezolvate
4.1. Pentru a exemplifica folosirea cozilor de mesaje se va compila si rula exemplul
prezentat in continuare. Functiile send si receive se vor rescrie folosind functiile msgsnd
si msgrcv de la mesaje.
Functia care gestioneaza cozile de mesaje este openqueue care, în acest caz,
este mult mai simpla, deoarece cozile de mesaje nu sunt o resursa limitata ca descriptorii
de fisier.
Toate mesajele trimise sunt de tipul 1 si procesul care transmite, respectiv
receptioneaza un mesaj este blocat daca coada este plina, respectiv goala. Acest lucru
este posibil deoarece, spre deosebire de fisierele FIFO, procesul care transmite mesajul
poate sa-l depuna în coada fara a astepta procesul receptor.
Codul fisierului mesaj.c este ilustrat în cele ce urmeaza:
/******************** MESAJ.C ********************/
#include <sys/ipc.h>
Sisteme de Operare Laborator nr. 11
#include <sys/msg.h>
#include "hdr.h"

#define MAXOPEN 20 /* nr. cozi mesaje deschise */


#define MAXMSG 4 /* nr. max. de mesaje */
typedef enum { FALSE, TRUE} BOOLEAN;
static int openqueue( int key)
{
static struct { /* inf. despre mesaj */
int key; /* cheia fisierului */
int qid; /* descriptorul asociat cozii */
} queues[ MAXOPEN];
int i, avail, qid;
extern int errno;
avail = -1; /* nu s-a gasit descriptor valabil */
for( i=0; i < MAXOPEN; i++) { /* poate e deschis deja */
if( queues[i].key == key) /* s-a gasit */
return( queues[i].qid);
if( queues[i].key == 0 && avail == -1 )
avail = i;
}
if( avail == -1 ) {
errno = 0;
return(-1);
}
if( (qid=msgget(key, 0666 | IPC_CREAT)) < 0)
return(-1);
queues[ avail].key = key;
queues[ avail].qid = qid;
return qid;
}

BOOLEAN send( int dstkey, struct msgbuf *buf, int nbytes)


{
int qid;
if( (qid = openqueue( dstkey)) < 0) {
fprintf( stderr, "Send - Eroare openqueue\n");
return FALSE;
}
buf->mtype=1;
return( msgsnd(qid,buf,nbytes-sizeof(buf->mtype),0) != -1);
}

BOOLEAN receive( int srckey, struct msgbuf *buf, int nbytes)


{
int qid;
if( ( qid = openqueue( srckey)) < 0) {
fprintf( stderr, "Receive - Eroare openqueue\n");
return FALSE;
}
return(msgrcv(qid,buf,nbytes-sizeof(buf->mtype),0L,0) !=-1);
}
void rmqueue( int key)
{
int qid;
if( ( qid=openqueue( key)) < 0 ||msgctl( qid, IPC_RMID, NULL) < 0)
err_sys("Eroare rmqueue");
}

4.2. Un proces server ofera câteva servicii pe care un proces client le poate solicita
printr-un mesaj.
Structura mesajului de cerere, cheia cozii si codurile ce identifica un anumit serviciu
sunt declarate în fisierul mes.h, care este inclus de programele clientului si serverului.
Sisteme de Operare Laborator nr. 11

/********************** MES.H *********************/


#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define KEY 13
/* coduri de operatie */
#define READ 1
#define WRITE 2
typedef struct {
long mtip;
int pid; /* pid-ul procesului client */
int cod_op; /* codul serviciului solicitat */
char mtext[13]; /* spaþiu suplimentar */
}Message;
Codul programului client este:
/*
A se compila cu: gcc -o c client.c err.o
A se lansa in executie cu: c [ 1 | 2 ]
**************** CLIENT.c **************
*/
#include "mes.h"
#include "hdr.h"
int msgid;
main( int argc, char **argv)
{
pid_t pid;
Message mesp;
extern int sterg();
if ( argc < 2)
err_quit("Utilizare: c <cod_op>\n");
if ( ( msgid=msgget( (key_t)KEY, 0666)) < 0)
err_sys("Eroare la crearea cozii de mesaje");
mesp.mtip=1;
mesp.cod_op=atoi(argv[1]);
pid=mesp.pid=getpid();
printf( "Clientul %d a trimis cererea\n", mesp.pid);
if(msgsnd(msgid,(struct msgbuf *)&mesp,sizeof(mesp)-sizeof(long),0) < 0)
err_sys("Eroare client: msgsnd");
if(msgrcv(msgid,(struct msgbuf *)&mesp,sizeof(mesp)-sizeof(long),pid,0)<
0)
err_sys("Eroare client: msgrcv");
mesp.mtext[12]='\0';
printf("Server:%d->Clientul %d a facut o cerere: %s\n",pid, mesp.pid,
mesp.mtext);
}

Programul client se va lansa cu un argument în linia de comanda si anume codul


operatiei solicitate. Se foloseste o singura coada de mesaje în care clientii îsi depun
cererile si în care serverul depune raspunsurile. Pentru ca un client sa-si primeasca
raspunsul, el va receptiona doar mesajele care au tipul egal cu pid-ul lor. În acest fel
fiecare client îsi va primi raspunsul.
Codul programului server este:
/******************** SERVER.C ******************/
#include "mes.h"
#include "hdr.h"
int msgid;
static void do_it( Message *);
static void sterg( void);
main( void)
{
Sisteme de Operare Laborator nr. 11
int i, *pint;
Message mesp;
if ( ( msgid=msgget( (key_t)KEY, IPC_CREAT)) < 0)
err_sys("Eroare la creare cozii de mesaje");
for ever {
if(msgrcv(msgid,(struct msgbuf*) &mesp,sizeof(mesp)-sizeof(long),1L,0)< 0)
err_sys("Eroare server: msgrcv");
/* tratare cerere */
do_it( &mesp);
mesp.mtip=mesp.pid;
mesp.pid=getpid();
if(msgsnd(msgid,(struct msgbuf*) &mesp,sizeof(mesp)-sizeof( long), 0) < 0)
err_sys("Eroare server: msgsnd");
}
}
void sterg( void)
{
if ( msgctl( msgid, IPC_RMID, 0) < 0)
err_sys("Eroare msgctl");
exit(0);
}
static void do_it( Message *mesp)
{
switch ( mesp->cod_op) {
case READ: strcpy( mesp->mtext, "Read\n");
break;
case WRITE: strcpy( mesp->mtext, "Write\n");
break;
default: strcpy( mesp->mtext, "Necunoscuta\n");
break;
}
}

Programul server primeste de la client un mesaj cu tipul 1 si construieste un mesaj


de raspuns cu tipul egal cu pid-ul clientului care a facut cererea. Servicile oferite de server
sunt reduse în acest exemplu la doua: READ si WRITE. Acestea sunt doar simulate,
mesajul de raspuns continând în câmpul mtext un sir care precizeaza daca operatia
ceruta a fost identificata corect de server.
În cazul în care un proces client solicita o operatie inexistenta mesajul va contine în
câmpul text sirul "Necunoscuta".
Comportarea acestor programe la o executie de test este de forma:
$s&
$c 1 & c 4
Clientul 995 a trimis cererea
Server:768->Clientul 990 a facut o cerere: Read

Clientul 999 a trimis cererea


Server:768->Clientul 990 a facut o cerere: Necunoscuta

5. Probleme propuse pentru rezolvare


5.1. Implementati problema producator consumator prin transmitere de mesaje
folosind pseudocodul de mai jos. Pentru producator si consumator se vor scrie programe
separate.

const int capacity = /* buffering capacity */ ;


null = /* empty message */ ;
int i;
void producer()
{
message pmsg;
Sisteme de Operare Laborator nr. 11
while (true)
{
receive (mayproduce, pmsg);
pmsg = produce();
send (mayconsume, pmsg);
}
}

void consumer()
{
message cmsg;
while (true)
{
receive (mayconsume, cmsg);
consume (cmsg);
send (mayproduce, null);
}
}

void main()
{
create_mailbox (mayproduce);
create_mailbox (mayconsume);
for (int i = 1; i <= capacity; i++)
send (mayproduce, null);
parbegin (producer, consumer);
}

5.2. Implementati problema cititori-scriitori varianta cu prioritatea scriitorilor folosind


mesaje. Se va scrie cate un program pentru fiecare tip de proces. Lansarea in executie se
va face astfel:
$./controller
$ for i in 1 2 3 4; do ./writer ; done
$ for I in 1 2 3; do ./reader ; done

Se va folosi un proces controller pentru managementul datelor partajate:


- Dacă Count > 0 nici un scriitor nu aşteaptă; putem avea mai mulţi cititori;
- Dacă Count = 0 avem un scriitor care aşteaptă;
- Dacă Count < 0 avem cereri de scriere care aşteaptă să se termine citirile.
Reader(i){
rmsg = i;
send( readrequest, rmsg );
receive( mbox[i], rmsg );
reader C.S.
rmsg = i;
send( finished, rmsg );
}

Writer(j){
rmsg = j;
send( writerequest, rmsg );
receive( mbox[j], rmsg );
writer C.S.
rmsg = j;
send( finished, rmsg );
}

Controller () {
do forever
if ( count > 0 )
if ( !empty( finished ) )
Sisteme de Operare Laborator nr. 11
receive( fininshed, msg );
count++;
else if ( !empty( writerequest ) )
receive( writerequest, msg );
writer_id = msg.id
count -= 100;
else if ( !empty( readrequest ) )
receive( readrequest, msg );
count--;
send( msg.id, “OK” );
if ( count == 0 )
send( writer_id, “OK” );
receive( finished, msg );
count = 100;
while ( count < 0 )
receive( finished, msg );
count++;
}

6. Bibliografie:
• Iosif Ignat, Adrian Kacso, UNIX Gestionarea Proceselor, Ed. Albastră, 2006
• W. Richard Stevens, Stephen A. Rago, Advanced Programming in the UNIX Envi-
ronment: Second Edition, Addison Wesley, 2005
• Kurt Wall, Mark Watson, and Mark Whitis, Linux Programming Unleashed, Sams
Publishing, 1999
• A. D. Marshall, Programming in C. UNIX System Calls and Subroutines using C,
http://www.cs.cf.ac.uk/Dave/C/
• Guide to Unix IPC http://beej.us/guide/bgipc/output/html/singlepage/bgipc.html
• Mark Mitchell, Jeffrey Oldham, and Alex Samuel, Advanced Linux Programming,
New Riders Publishing, 2001, http://www.advancedlinuxprogramming.com/
• Compiler, assembler, linker and loader: a brief story
http://tenouk.com/ModuleW.html

S-ar putea să vă placă și