Laboratoare:Old:IPC (2)

= Linux =

Mesaje
Cozile de mesaje oferă posibilitatea mai multor procese de a scrie și citi mesaje. Coada de mesaje poate fi accesată de orice proces care îi cunoaște cheia. Un mesaj e format dintr-un tip de mesaj și din date. Datorită faptului că mesajele pot fi alese din coadă pentru citire în functie de tip, citirea nu trebuie să se facă numai FIFO, ca în cazul pipe-urilor.

O coadă de mesaje este descrisă de o structură msqid_ds, alocată și inițializată în momentul creării resursei. Unele câmpuri ale structurii pot fi alterate de apeluri msgctl; memoria ocupată de o coadă de mesaje este eliberată tot cu un apel msgctl (comanda IPC_RMID).

Pentru trimiterea sau recepționarea unui mesaj, se alocă o structură de tipul msgbuf, dar în care câmpul mtext are dimensiunea necesară. Mesajele au un tip întreg pozitiv, astfel încât un user poate alege să primească un anumit tip de mesaje.

ATENȚIE! Câmpul mtext al structurii msgbuf nu trebuie să fie neapărat un vector de caractere, poate avea practic orice tip (de exemplu poate fi o structură), însă nu poate fi un pointer (întrucât mesajele se pot trimite între procese diferite, iar în acest caz pointerii își pierd semnificația).

Procesul user trebuie să aibă permisiuni de write ca să trimită mesaje și de read ca să primească mesaje într-o coadă. La trimitere (apelul este msgsnd), mesajul este copiat într-o structură internă msg</tt> și adăugat cozii. Un apel msgrcv</tt> de recepție va citi acest mesaj și va elibera structura asociată msg</tt>.

msgget
O coadă de mesaje e alocată printr-un apel de sistem msgget</tt> :

Parametrii au următoarea semnificație :


 * key</tt> - un întreg obținut de obicei prin ftok</tt> sau IPC_PRIVATE</tt>
 * msgflg</tt>
 * IPC_CREAT</tt> - creează o resursă daca ea nu există deja
 * IPC_CREAT | IPC_EXCL</tt> - dacă resursa există deja, apelul eșuează
 * rwxrwxrwx</tt> - permisiuni de acces specificate ca în cazul fișierelor

Apelul întoarce un întreg folosit pentru accesările următoare sau -1 în caz de insucces.

O coadă de mesaje nouă este alocată dacă nu există o resursă asociată cheii date. În acest caz permisiunile de acces sunt copiate într-o structură ipc_perm</tt> și câmpurile structurii msqid_ds</tt> sunt inițializate. Pentru a se asigura că se alocă o nouă instanță, procesul user trebuie să folosească flagul IPC_CREAT</tt> sau cheia IPC_PRIVATE</tt>. Daca există o resursă asociată cheii, se verifică permisiunile de acces.

Erori :


 * EACCES</tt> - (procure) Do not have permission for requested access.
 * <tt>EEXIST</tt> - (allocate) <tt>IPC_CREAT | IPC_EXCL</tt> specified and resource exists.
 * <tt>EIDRM</tt> - (procure) The resource was removed.
 * <tt>ENOSPC</tt> - All id's are taken (max of <tt>MSGMNI</tt> id's system-wide).
 * <tt>ENOENT</tt> - Resource does not exist and <tt>IPC_CREAT</tt> not specified.
 * <tt>ENOMEM</tt> - A new <tt>msqid_ds</tt> was to be created but ... nomem.

msgsnd
Parametri :


 * <tt>msqid</tt> - un ID obținut în urma unui apel msgget
 * <tt>msgp</tt> - mesajul de trimis
 * <tt>msgsz</tt> - dimensiunea mtext, în octeți (pozitivă)
 * <tt>msgflg</tt> - poate fi <tt>IPC_NOWAIT</tt>

Întoarce <tt>msgsz</tt> în caz de succes și -1 în caz de eroare. Textul mesajului și tipul său sunt memorate în structura internă <tt>msg</tt>. Sunt actualizate câmpurile <tt>msg_cbytes</tt>, <tt>msg_qnum</tt>, <tt>msg_lspid</tt> și <tt>msg_stime</tt>, iar procesele reader care așteaptă la coada de mesaje sunt trezite.

Erori :


 * <tt>EACCES</tt> - Do not have write permission on queue.
 * <tt>EAGAIN</tt> - <tt>IPC_NOWAIT</tt> specified and queue is full.
 * <tt>EFAULT</tt> - msgp not accessible.
 * <tt>EIDRM</tt> - The message queue was removed.
 * <tt>EINTR</tt> - Full queue ... would have slept but ... was interrupted.
 * <tt>EINVAL</tt> - <tt>mtype</tt> < 1, <tt>msgsz</tt> > <tt>MSGMAX</tt>, <tt>msgsz</tt> < 0, <tt>msqid</tt> < 0 or unused.
 * <tt>ENOMEM</tt> - Could not allocate space for header and text.

msgrcv
Parametri :


 * <tt>msqid</tt> - un ID obținut în urma unui apel <tt>msgget</tt>
 * <tt>msgp</tt> - spațiu alocat de user pentru stocarea mesajului
 * <tt>msgsz</tt> - dimensiunea maximă a mesajului de primit
 * <tt>msgtyp</tt> - un întreg cu valorile :
 * = 0 - ia primul mesaj din coadă
 * > 0 - ia primul mesaj din coadă din respectivul tip
 * < 0 - ia mesajul din cel mai mic tip <= abs(msgtyp), căutând în toata coada
 * <tt>msgflg</tt>
 * <tt>IPC_NOWAIT</tt> - apelul se termină imediat dacă nu este găsit un mesaj
 * <tt>MSG_NOERROR</tt> - mesajul este trunchiat dacă depășește msgsz
 * <tt>MSG_EXCEPT</tt> - cu <tt>msgtyp</tt> > 0, se primesc toate mesajele cu excepția celui specificat

Întoarce lungimea mesajului primit sau -1 în caz de eroare. Dacă lungimea mesajului este mai mică decât <tt>msgsz</tt> sau dacă flagul <tt>MSG_NOERROR</tt> este setat, mesajul și tipul său sunt copiate în <tt>msgp->mtext</tt> și <tt>msgp->mtype</tt>, după care mesajul este scos din coadă. Sunt actualizate câmpurile <tt>msg_cbytes</tt>, <tt>msg_qnum</tt>, <tt>msg_lrpid</tt> și <tt>msg_rtime</tt>, iar procesele writer care așteaptă la coada de mesaje sunt trezite.

Erori :


 * <tt>E2BIG</tt> - msg bigger than msgsz and <tt>MSG_NOERROR</tt> not specified.
 * <tt>EACCES</tt> - Do not have permission for reading the queue.
 * <tt>EFAULT</tt> - <tt>msgp</tt> not accessible.
 * <tt>EIDRM</tt> - msg queue was removed.
 * <tt>EINTR</tt> - msg not found ... would have slept but ... was interrupted.
 * <tt>EINVAL</tt> - <tt>msgsz</tt> > <tt>msgmax</tt> or <tt>msgsz</tt> < 0, <tt>msqid</tt> < 0 or unused.
 * <tt>ENOMSG</tt> - msg of requested type not found and <tt>IPC_NOWAIT</tt> specified.

msgctl
Parametri :


 * <tt>msqid</tt> - un ID obtinut în urma unui apel <tt>msgget</tt>
 * <tt>cmd</tt> - <tt>IPC_STAT</tt>, <tt>IPC_SET</tt>, <tt>IPC_RMID</tt>
 * <tt>buf</tt> - spațiu alocat de user pentru citire sau scriere de informație

În cazul comenzii <tt>IPC_STAT</tt>, se copiază structura de date asociată cozii în buffer. În cazul <tt>IPC_SET</tt>, câmpurile <tt>msg_qbytes</tt>, <tt>uid</tt>, <tt>gid</tt> și <tt>mode</tt> ale structurii <tt>msg_perm</tt> sunt setate de către user, iar câmpul <tt>msg_ctime</tt> este actualizat.

ATENȚIE! numai super-userul poate crește dimensiunea cozii de mesaje peste <tt>MSGMNB</tt>.

Când coada de mesaje este distrusă (<tt>IPC_RMID</tt>), toate procesele reader și writer sunt trezite cu errno egal cu <tt>EIDRM</tt>.

Erori :


 * <tt>EPERM</tt> - Insufficient privilege to increase the size of the queue (<tt>IPC_SET</tt>) or remove it (<tt>IPC_RMID</tt>).
 * <tt>EACCES</tt> - Do not have permission for reading the queue (<tt>IPC_STAT</tt>).
 * <tt>EFAULT</tt> - buf not accessible (<tt>IPC_STAT</tt>, <tt>IPC_SET</tt>).
 * <tt>EIDRM</tt> - msg queue was removed.
 * <tt>EINVAL</tt> - invalid cmd, <tt>msqid</tt> < 0 or unused.

Limitări
Dimensiunile structurilor :


 * <tt>msgid_ds</tt> : 52; o structură pentru fiecare coadă de mesaje
 * <tt>msg</tt> : 16; o structură pentru fiecare mesaj
 * <tt>msgbuf</tt> : 8; alocată de user

Limite :


 * <tt>MSGMNI</tt> - numărul identificatorilor de cozi de mesaje; depinde de sistem
 * <tt>MSGMAX</tt> - dimensiunea maximă a unui mesaj (headerul și spațiul mesajului sunt alocate într-o pagină); <tt>MSGMAX = PAGE_SIZE - sizeof(struct msg)</tt>. Din implementare <tt>MSGMAX = 4080</tt>.
 * <tt>MSGMNB</tt> - dimensiunea maximă a cozii de mesaje; depinde de sistem. Super-userul poate mări dimensiunea unei cozi peste <tt>MSGMNB</tt> printr-un apel <tt>msgctl</tt>.

Exemplu folosire cozi de mesaje Linux
'''Exemplu 01-a. ipc2-qcreate.c''' '''Exemplu 01-b. ipc2-qopen.c'''

Memorie partajată
O memorie partajată este descrisă de structura :

Un apel <tt>shmget</tt> alocă o asemenea structură <tt>shmid_ds</tt> și o tabelă de pagini internă. Un apel <tt>shmat</tt> mapează segmentul în spațiul de adrese al procesului, la un pointer din tabela internă de pagini. Memoria asociată segmentului partajat trebuie dealocată explicit printr-un apel <tt>shmctl</tt> cu <tt>IPC_RMID</tt>.

shmget
Un segment de memorie partajată e alocat de un apel de sistem <tt>shmget</tt>:

Parametrii au următoarea semnificație :


 * <tt>key</tt> - un întreg obținut de obicei prin <tt>ftok</tt> sau <tt>IPC_PRIVATE</tt>
 * <tt>size</tt> - dimensiunea segmentului în octeți (<tt>SHMMIN <= size <= SHMMAX</tt>)
 * <tt>shmflg</tt>
 * <tt>IPC_CREAT</tt> - creează o resursă dacă ea nu există deja
 * <tt>IPC_CREAT | IPC_EXCL</tt> - dacă resursa exista deja, apelul eșuează
 * <tt>rwxrwxrwx</tt> - permisiuni de acces specificate ca în cazul fișierelor

Întoarce <tt>shmid</tt> în caz de succes, sau -1 în caz de eroare.

Se alocă un descriptor de segment cu dimensiunea egală cu un multiplu întreg de PAGE_SIZE (dimensiunea unei pagini) dacă una din următoarele condiţii este îndeplinită: Dacă alocarea a reuşit, permisiunile de acces specificate sunt apoi copiate în structura <tt>shm_perm</tt> împreună cu <tt>user_id</tt> etc. Dacă nici una din condiţii nu este îndeplinită şi dacă segmentul de memorie există deja, se verifică permisiunile de acces și dacă segmentul nu e marcat pentru ștergere.
 * <tt>key</tt> are valoarea <tt>IPC_PRIVATE</tt>
 * <tt>key</tt> nu are valoarea <tt>IPC_PRIVATE</tt>, nici un alt descriptor nu este asociat cu cheia <tt>key</tt> şi este specificat flag-ul <tt>IPC_CREAT</tt>

Erori :


 * <tt>EINVAL</tt> - (allocate) Size not in range specified above. (procure) Size greater than size of segment.
 * <tt>EEXIST</tt> - (allocate) <tt>IPC_CREAT | IPC_EXCL</tt> specified and resource exists.
 * <tt>EIDRM</tt> - (procure) The resource is marked destroyed or was removed.
 * <tt>ENOSPC</tt> - (allocate) All id's are taken (max of <tt>SHMMNI</tt> id's system-wide). Allocating a segment of the requested size would exceed the system wide limit on total shared memory (<tt>SHMALL</tt>).
 * <tt>ENOENT</tt> - (procure) Resource does not exist and <tt>IPC_CREAT</tt> not specified.
 * <tt>EACCES</tt> - (procure) Do not have permission for specified access.
 * <tt>ENOMEM</tt> - (allocate) Could not allocate memory for <tt>shmid_ds</tt> or <tt>pg_table</tt>.

shmat
Apelul mapează un segment partajat în spațiul de adrese al procesului.

Parametri :


 * <tt>shmid</tt> - un ID obtinut în urma unui apel <tt>shmget</tt>
 * <tt>shmaddr</tt> - adresa la care se cere maparea. Dacă este 0, sistemul alege o regiune liberă; altfel, valoarea trebuie să fie aliniată la pagină sau userul trebuie să specifice flagul <tt>SHM_RND</tt>.
 * <tt>shmflg</tt>
 * <tt>SHM_RDONLY</tt> - se cere o mapare read-only
 * <tt>SHM_RND</tt> - adresa la care se cere maparea e rotunjită la un multiplu de <tt>SHMLBA</tt> mai mic decât <tt>shmaddr</tt>

Întoarce adresa din spațiul virtual a segmentului mapat, sau -1 în caz de eroare. Segmentul e detașat automat când procesul se termină. Același segment poate fi mapat read-only sau read-write și de mai multe ori în cadrul spațiului de adrese al procesului. Un apel <tt>shmat</tt> se poate termina cu succes pentru un segment marcat pentru ștergere. Modul de mapare se specifică prin flagul <tt>SHM_RDONLY</tt>; nu există mapare write-only. Permisiunile de mapare cerute trebuie să fie cuprinse în cele memorate în <tt>shm_perm.mode</tt>.

Erori :


 * <tt>EACCES</tt> - Do not have permission for requested access.
 * <tt>EINVAL</tt> - <tt>shmid < 0</tt> or unused, <tt>shmaddr</tt> not aligned, attach at <tt>brk</tt> failed.
 * <tt>EIDRM</tt> - resource was removed.
 * <tt>ENOMEM</tt> - Could not allocate memory for descriptor or page tables.

shmdt
unde <tt>shmaddr</tt> este adresa mapată a segmentului, returnată de un apel <tt>shmat</tt>.

Întoarce 0 în caz de succes, sau -1 în caz de eroare.

Un segment mapat este detașat și câmpul shm_nattch decrementat. Regiunea ocupată în user space este eliberată; segmentul este distrus dacă este marcat pentru ștergere și dacă <tt>shm_nattch</tt> este 0. Câmpurile <tt>shm_lpid</tt> și <tt>shm_dtime</tt> sunt actualizate.

Erori :


 * <tt>EINVAL</tt> - No shared memory segment attached at <tt>shmaddr</tt>.

shmctl
Apelul poate elibera segmentele alocate și poate scrie sau citi structurile de control.

Parametri :


 * <tt>shmid</tt> - un ID obtinut în urma unui apel <tt>shmget</tt>
 * <tt>cmd</tt>
 * <tt>IPC_SET</tt> - setează <tt>uid/gid</tt> pentru proprietar și câmpul <tt>shm_perms.mode</tt>
 * <tt>IPC_RMID</tt> - segmentul e marcat pentru ștergere, dar e șters numai la ultimul detach
 * <tt>IPC_STAT</tt> - structura <tt>shmid_ds</tt> e copiată în bufferul alocat de user
 * <tt>buf</tt> - folosit pentru citirea (cu <tt>IPC_STAT</tt>) sau scrierea (cu <tt>IPC_SET</tt>) a informației

Întoarce 0 în caz de succes, sau -1 în caz de insucces.

Userul trebuie să apeleze <tt>shmctl</tt> cu <tt>IPC_RMID</tt> pentru a elibera memoria alocată de segmentul partajat, altfel toate paginile din memorie sau swap vor continua să existe.

Erori :


 * <tt>EACCES</tt> - Do not have permission for requested access.
 * <tt>EFAULT</tt> - buf is not accessible.
 * <tt>EINVAL</tt> - shmid < 0 or unused.
 * <tt>EIDRM</tt> - identifier destroyed.
 * <tt>EPERM</tt> - not creator, owner or super-user (<tt>IPC_SET</tt>, <tt>IPC_RMID</tt>).

Limitări

 * <tt>SHMMNI</tt> : 4096; numărul maxim de segmente partajate în întregul sistem
 * <tt>SHMMAX</tt> : 4M; dimensiunea maximă a unui segment de memorie partajată, în octeți
 * <tt>SHMMIN</tt> : 1 octet; dimensiunea minimă a unui segment de memorie partajată; practic este egală cu <tt>PAGE_SIZE</tt>
 * <tt>SHMALL</tt> : dimensiunea maximă a memoriilor partajate în sistem
 * <tt>SHMLBA</tt> : <tt>PAGE_SIZE</tt>; segment low boundary address multiple; aliniat la pagină

Exemplu folosire memorie partajată Linux
În exemplul de mai jos este prezentată crearea, folosirea și distrugerea unei zone de memorie partajată.

'''Exemplu 02. ipc2-shmem.c'''

Note suplimentare
Dupa un apel <tt>fork</tt>, procesul fiu moștenește segmentele de memorie partajată mapate. La un apel <tt>exec</tt>, segmentele partajate mapate sunt detasate (nu eliminate). La <tt>exit</tt>, toate segmentele sunt detașate (nu eliminate).

= Windows =

Cozi de mesaje (Mailslots)
Cozile de mesaje sunt folosite de procese pentru a comunica între ele prin mesaje. Aceste mesaje își păstrează ordinea în interiorul cozii de mesaje.

Mailslots sunt un fel de pseudo-fisiere care rezidă în memorie, și deci pot fi folosite prin intermediul funcțiilor standard de acces la fisiere. Datele dintr-un astfel de mailslot pot avea orice formă, atât timp cât nu depășesc limita de 64 Kbytes. Fiind păstrate în memorie, toate aceste date au un caracter volatil, spre deosebire de fișiere, iar când toate handle-urile la un mailslot sunt distruse, acesta la rândul său, este distrus împreună cu datele, iar memoria este eliberată.

Sistemul de cozi de mesaje bazate pe mailslots este de tipul client-server. Astfel :

Este procesul care creează și deține coada de mesaje, obținând astfel un handle. Numai cu ajutorul acestui handle se poate citi din (dar nu se poate scrie în) coada de mesaje. Există și posibilitatea ca handle-ul să fie moștenit. Este un proces care pune mesaje în coadă. Pot exista mai mulți clienți simultan.
 * serverul mailslot
 * clientul mailslot

Limitări :


 * Mesajele de tip broadcast sunt limitate la maximum 424 bytes, iar încercarea de a trimite un mesaj broadcast mai mare va eșua, iar funcția va întoarce eroare.
 * NU pot fi trimise mesaje de lungime 425 bytes sau 426 bytes.
 * Lungimea maximă a unui mesaj este 64 Kbytes.

Denumirea Mailslot-urilor
Când un proces creează un mailslot, trebuie să-i atribuie o denumire, care are următoarea formă :

\\.\mailslot\[path]

Atenție! Prefixul "\\.\mailslot\" trebuie sa existe exact în această formă, el fiind urmat de un nume, care eventual va fi precedat de o cale. Calea este asemănătoare cu cea a fișierelor. Un exemplu valid : "\\.\mailslot\test\commands".

Un proces client, pentru a scrie într-un mailslot, va folosi aceeași denumire.

Cozile de mesaje pot fi folosite și pentru a comunica cu procese care rulează pe alte calculatoare. În acest sens, clientul va folosi denumiri care au structura :

\\<ComputerName>\mailslot\[path]<Nume>

Pentru a trimite mesaje unui întreg domeniu, denumirea va avea structura :

\\<DomainName>\mailslot\[path]<Nume>

Pentru a trimite mesaje tuturor, denumirea va avea structura :

\\*\mailslot\[path]<Nume>

Crearea cozilor de mesaje
Pentru a crea un mailslot, se folosește funcția <tt>CreateMailslot</tt> care are următoarea sintaxă și întoarce un handle :

Parametrii au următoarea semnificație :


 * <tt>lpName</tt> - pointer la un șir de caractere care conține denumirea cozii de mesaje. Forma acceptată este cea descrisă în secțiunea anterioară.
 * <tt>nMaxMessageSize</tt> - dimensiunea maximă, în bytes, a unui mesaj care poate fi scris în coada de mesaje. Valoarea zero permite mesaje de orice dimensiune.
 * <tt>lReadTimeout</tt> - timpul, în milisecunde, după care o operație de citire expiră. Valoarea zero înseamnă că nu se va aștepta deloc, iar valoarea <tt>MAILSLOT_WAIT_FOREVER</tt> are ca rezultat o operație de citire care nu expiră.
 * <tt>lpSecurityAttributes</tt> - are semnificația cunoscută.

În cazul în care se încearcă crearea unui mailslot cu o denumire care deja există, se va întoarce INVALID_HANDLE_VALUE.

ATENȚIE! Handle-ul întors de această funcție poate fi folosit pentru a efectua doar operații de citire (nu și de scriere) cu mailslot-ul.

Deschiderea cozilor de mesaje existente
Pentru a deschide un mailslot pentru scriere, se folosește funcția <tt>CreateFile</tt> care va primi în loc de numele fișierului denumirea cozii de mesaje care se dorește a fi deschisă si flagul <tt>FILE_SHARE_READ</tt>. Pentru a permite accesul concomitent al mai multor clienţi, trebuie adăugat şi flagul <tt>FILE_SHARE_WRITE</tt>.

Scrierea și citirea în/din cozile de mesaje
Citirea și respectiv scrierea din/în cozile de mesaje sunt asemănătoare cu operațiile cu fișiere, folosindu-se aceleași funcții :


 * <tt>ReadFile</tt> și <tt>ReadFileEx</tt> - pentru citire
 * <tt>WriteFile</tt> și <tt>WriteFileEx</tt> - pentru scriere

Obținerea de informații despre cozile de mesaje
Pentru a obține informații despre o coadă de mesaje, se folosește funcția următoare :

care primește următorii parametrii :

Acest pointer poate fi <tt>NULL</tt> dacă nu se dorește această informație.
 * <tt>hMailslot</tt> - handle-ul cozii de mesaje
 * <tt>lpMaxMessageSize</tt> - dimensiunea maximă a unui mesaj
 * <tt>lpNextSize</tt> - pointer la o zonă în care va fi depusă dimensiunea următorului mesaj din coadă. Valoarea <tt>MAILSLOT_NO_MESSAGE</tt> specifică faptul că nu există nici un mesaj în coadă.
 * <tt>lpMessageCount</tt> - pointer la o zona în care va fi depus numărul de mesaje care așteaptă să fie citite. Acest pointer poate fi <tt>NULL</tt> dacă nu se dorește această informație.
 * <tt>lpReadTimeout</tt> - pointer la o zonă în care va fi depus timpul după care o operație de citire expiră. Acest pointer poate fi <tt>NULL</tt> dacă nu se dorește această informație.

Schimbarea timpului de expirare
Singura caracteristică a unei cozi de mesaje, care poate fi schimbată după ce coada a fost creată, este timpul de expirare. (Dimensiunea maximă a mesajelor acceptate de o coadă nu mai poate fi schimbată după ce aceasta a fost creată)

Funcția care setează această caracteristică este următoarea :

Parametrii au semnificațiile cunoscute.

Exemplu folosire cozi de mesaje Windows
'''Exemplu 04-a. Crearea unei cozi de mesaje'''

'''Exemplu 04-b. Deschiderea cozii de mesaje si trimiterea unui mesaj'''

'''Exemplu 04-c. Citirea mesajelor din coadă'''

Memorie partajata (File Mapping)
File Mapping în cazul general permite accesul mai multor procese la un fisier ca și cand fisierul ar fi o zonă de memorie. Astfel se pot folosi toate operațiile aplicabile asupra memoriei, inclusiv pointeri.

O facilitate specială a File Mapping este aceea de "named shared memory", sau memorie partajata identificată de un nume. Această facilitate specială este subiectul laboratorului de față.

ATENȚIE! Accesul la o zonă de memorie partajată trebuie reglementat folosind unul din mecanismele de sincronizare descrise în laboratorul precedent!

Crearea unei zone de memorie partajată
Pentru crearea unei zone de memorie partajată se folosesc două funcții care trebuie apelate în această ordine :


 * 1) <tt>CreateFileMapping</tt> - este o funcție pregătitoare care creează un obiect de tipul File Mapping, reprezentat de un <tt>HANDLE</tt>.
 * 2) <tt>MapViewOfFile</tt> - pentru a mapa efectiv zona de memorie. Funcția întoarce un pointer la zona de memorie partajată.

CreateFileMapping
Funcția creează o resursă (un obiect) de tipul FileMapping și are următoarea sintaxa :

Parametrii au umătoarea semnificație :

Dacă există deja un obiect File Mapping cu acest nume, funcția va încerca să acceseze/deschidă obiectul deja existent si va întoarce handle-ul către acesta, iar funcția <tt>GetLastError</tt> va întoarce ERROR_ALREADY_EXISTS. Dacă există un obiect cu același nume dar de alt tip, funcția va eșua și va întoarce NULL.
 * <tt>hFile</tt> - handle al fișierului care va fi mapat în memorie. În cazul nostru, valoarea <tt>INVALID_HANDLE_VALUE</tt> este folosită pentru a crea o zonă de memorie partajată (având ca fișier de suport fișierul de swap al sistemului).
 * <tt>lpAttributes</tt> - pointer la o structură <tt>SECURITY_ATTRIBUTES</tt>, poate fi <tt>NULL</tt>.
 * <tt>flProtect</tt> - tipul de acces care va fi disponibil :
 * <tt>PAGE_READONLY</tt> - doar acces de citire
 * <tt>PAGE_READWRITE</tt> - acces de scriere și citire
 * <tt>PAGE_WRITECOPY</tt> - acces copy-on-write
 * <tt>dwMaximumSizeHigh</tt> - dimensiunea zonei (partea superioară).
 * <tt>dwMaximumSizeLow</tt> - dimensiunea zonei (partea inferioară).
 * <tt>lpName</tt> - numele obiectului. Dacă este <tt>NULL</tt>, se va crea un obiect anonim.

MapViewOfFile
Funcția întoarce un pointer la zona de memorie partajată și are sintaxa :

Parametrii au umătoarea semnificație :


 * <tt>hFileMappingObject</tt> - obiectul care va fi mapat, întors de funcția <tt>CreateFileMapping</tt> sau <tt>OpenFileMapping</tt>.
 * <tt>dwDesiredAccess</tt> - accesul dorit :
 * <tt>FILE_MAP_WRITE</tt> - acces de citire și scriere
 * <tt>FILE_MAP_READ</tt> - acces doar de citire
 * <tt>FILE_MAP_ALL_ACCESS</tt> - similar <tt>FILE_MAP_WRITE</tt>
 * <tt>FILE_MAP_COPY</tt> - acces copy-on-write - modificările făcute NU sunt scrise și în fișierul original de pe disc
 * <tt>dwFileOffsetHigh</tt> - deplasamentul (de unde să înceapă maparea) - partea superioară.
 * <tt>dwFileOffsetLow</tt> - deplasamentul (de unde să înceapă maparea) - partea inferioară.
 * <tt>dwNumberOfBytesToMap</tt> - numărul de octeți care vor fi mapați. Dacă este zero, se mapează întreg File Mapping-ul.

Accesul la o zonă de memorie partajată deja creată
Pentru a accesa o zonă de memorie partajată, creată de alt proces, se utilizează următoarele funcții (în ordinea specificată) :


 * 1) <tt>OpenFileMapping</tt> - o funcție pregătitoare care accesează (deschide) un obiect de tipul File Mapping.
 * 2) <tt>MapViewOfFile</tt> - pentru a mapa efectiv zona de memorie.

OpenFileMapping
Accesează o resursă/obiect deja existent de tipul FileMapping și are sintaxa :

Semnificația parametrilor :


 * <tt>dwDesiredAccess</tt> - accesul dorit, vezi <tt>CreateFileMapping</tt>.
 * <tt>bInheritHandle</tt> - dacă este <tt>TRUE</tt> handle-ul va fi moștenit de copii.
 * <tt>lpName</tt> - numele obiectului/resursei FileMapping care se dorește accesat.

Demaparea unei zone de memorie partajată
Pentru a demapa o zonă de memorie partajată, care a fost anterior mapată folosind funcția <tt>MapViewOfFile</tt>, se folosește funcția <tt>UnmapViewOfFile</tt> care are următoarea sintaxă :

Parametri :


 * <tt>lpBaseAddress</tt> - pointer la adresa de bază a zonei de memorie partajată ce urmează să fie demapată. Această valoare trebuie sa coincidă cu valoarea întoarsă de apelul <tt>MapViewOfFile</tt>.

Exemplu folosire memorie partajată Windows
'''Exemplu 03-a. Secvența de program care creează o zonă de memorie partajată'''

'''Exemplu 03-b. Secvența de program care accesează zona de memorie partajată'''

= Exerciții =

Quiz
Pentru autoevaluare raspundeți la întrebările din acest quiz.

Exercitii pre-laborator
Folosiți [[media:Lab6-pre.zip|arhiva de pre-sarcini]] a laboratorului.

Linux
Folosiți directorul <tt>lin/</tt> din [[media:Lab6-pre.zip|arhiva de pre-sarcini]] a laboratorului.


 * 1) Intraţi in directorul <tt>msg/</tt>.
 * 2) * Studiaţi fişierele <tt>send.c</tt> şi <tt>recv.c</tt>. Sender-ul creează o coadă de mesaje şi îi trimite receptorului un mesaj, pe care acesta il afişează.
 * 3) * Completaţi în <tt>send.c</tt> zona <tt>TODO</tt>, astfel încât să împachetaţi in membrul <tt>mtype</tt> al structurii <tt>msgbuf</tt> întregul <tt>number</tt>. Nu trebuie să convertiţi întregul la un şir de caractere, ci trebuie să depuneţi direct octeţii ce compun întregul.

Windows
Folosiți directorul <tt>win/</tt> din [[media:Lab6-pre.zip|arhiva de pre-sarcini]] a laboratorului.


 * 1) Intraţi in directorul <tt>conc/</tt>.
 * 2) * Studiaţi fişierele <tt>server.c</tt> şi <tt>client.c</tt>. Server-ul creează o coadă de mesaje pe care clientul o deschide. Compilaţi.
 * 3) * Rulaţi scriptul <tt>run.bat</tt>, care creează o instanţă a server-ului şi două instanţe ale clientului. De ce unul din clienţi iese cu eroare? Corectaţi problema.
 * 4) ** Hint: Cercetaţi parametrii <tt>CreateFile</tt>.

Exerciţii de laborator
Folosiți [[media:lab6-tasks.zip|arhiva de sarcini]] a laboratorului. Intraţi in directoarele include/ si libs/ si analizati continutul fisierelor respective.


 * 1) Linux (2p)/ Windows (2p): Implementați un protocol simplu de comunicație între un client și un server folosind cozi de mesaje. Clientul se va conecta la server și va trimite acestuia numărul 1337 pe care server-ul îl va afișa. Apoi clientul va trimite server-ului un mesaj de închidere.
 * 2) * Hints:
 * 3) ** Fişierul <tt>common.h</tt> conţine structurile necesare protocolului, iar <tt>types.h</tt> conţine definiţii de tipuri şi constante. Aceste fişiere vor fi folosite şi la exerciţiile următoare
 * 4) ** Entitatea care creează coada de mesaje va trebui să o și distrugă
 * 5) ** Pe Windows nu puteți selecta din coadă tipul mesajului pe care doriți să-l citiți, deci asigurați-vă că mesajele vor putea fi trimise și recepționate în mod asemănător pe Windows și Linux
 * 6) ** Citiți și exercițiul 2
 * 7) Linux (2p)/ Windows (2p): Folosind protocolul anterior, modificați clientul și server-ul astfel încât clientul să trimită numerele de la 0 la n-1 către server (n citit de la tastatură), server-ul le recepționează, le incrementează cu 1 și le trimite înapoi clientului, care le afișează. Comunicarea se incheie în mod asemănător ca la exerciţiul precedent.
 * 8) * Hints:
 * 9) ** Pe Windows cozile de mesaje nu sunt bidirecționale (entitatea care creează coada de mesaje, poate numai s-o folosească pentru a citi mesaje), deci va trebui să folosiți 2 cozi de mesaje
 * 10) Linux (2p)/ Windows (2p): Folosind memoria partajată, realizați un transfer simplu de informație între două procese astfel: server-ul va crea o zona de 4k de memorie și va pune numărul 1337 începând cu primul octet, clientul va citi și afișa acest număr.
 * 11) * Hints:
 * 12) ** Deoarece clientul trebuie ruleze după ce serverul a creat, mapat și scris în zona partajată și înainte ca acesta să o elibereze, folosiți o functie de sleep pentru server (implementaţi funcţia <tt>Dream</tt> din <tt>utils.c</tt>)
 * 13) Linux (2p)/ Windows (2p): Înlocuiți funcția de sleep de la exercițiul precedent cu o metodă de sincronizare din laboratorul trecut. De asemenea, protejați zona partajată, astfel încât să evitați "race condition"-urile.
 * 14) * Hints:
 * 15) ** Server-ul trebuie să aștepte până ce clientul a citit valoarea, pentru a nu elibera zona prea devreme. După ce clientul citește valoarea, îi va semnala acest lucru server-ului (puteți folosi un semafor inițializat la 0)

= Soluții =


 * [[Media:lab6-sol.zip|Soluții exerciții laborator 6]]

= Resurse utile =


 * 1) MSDN: Exemplu de utilizare a memoriei partajate (Named Shared Memory) in Windows
 * 2) MSDN: Crearea unui Mailslot
 * 3) MSDN: Scrierea intr-un Mailslot
 * 4) MSDN: Citirea dintr-un Mailslot
 * 5) IPC în Linux
 * 6) Cozi de mesaje în Linux
 * 7) Memorie partajată în Linux