Table of Contents

1. Memorie partajata (File Mapping)
1.1. Crearea unei zone de memorie partajata
1.2. Accesul la o zone de memorie partajata deja creata
1.3. Exemplu de folosire a memoriei partajate
2. Cozi de mesaje (Mailslots)
2.1. Denumirea Mailslot-urilor
2.2. Crearea cozilor de mesaje
2.3. Deschiderea cozilor de mesaje existente
2.4. Scrierea si citirea in/din cozile de mesaje
2.5. Obtinerea de informatii despre cozile de mesaje
2.6. Schimbarea timpului de expirare
2.7. Exemple
 
 




1. Memorie partajata (File Mapping)


File Mapping in cazul general permite accesul mai multor procese la un fisier ca si cand fisierul ar fi o zona de memorie. Astfel se pot folosi toate operatiile aplicabile asupra memoriei, inclusiv pointeri.
O facilitate speciala a File Mapping este aceea de "named shared memory", sau memorie partajata identificata de un nume. Aceasta facilitate speciala este subiectul lucrarii de fata.
Atentie! accesul la o zona de memorie partajata trebuie reglementat folosind unul din mecanismele de sincronizare descrise in prima parte!

1.1. Crearea unei zone de memorie partajata


Pentru crearea unei zone de memorie partajata se folosesc doua functii care trebuie apelate in aceasta ordine:
  1. CreateFileMapping() - este o functie pregatitoare care creeaza un obiect de tipul File Mapping, reprezentat de un HANDLE.
  2. MapViewOfFile() - pentru a mapa efectiv zona de memorie. Functia intoarce un pointer la zona de memorie partajata.
CreateFileMapping()
Functia creeaza o resursa/obiect de tipul FileMapping si are urmatoarea sintaxa:

HANDLE CreateFileMapping(

HANDLE hFile,

LPSECURITY_ATTRIBUTES lpAttributes,

DWORD flProtect,

DWORD dwMaximumSizeHigh,

DWORD dwMaximumSizeLow,

LPCTSTR lpName

);

Parametrii au urmatoarea semnificatie:
  • hFile
    Handle al fisierului care va fi mapat in memorie. In cazul nostru, valoarea INVALID_HANDLE_VALUE este folosita pentru a crea o zona de memorie partajata (avand ca fisier de suport fiserul de swap al sistemului)
  • lpAttributes
    Pointer la o structura SECURITY_ATTRIBUTES, poate fi NULL
  • flProtect
    Tipul de acces care va fi disponibil:

    • PAGE_READONLY - doar acces de citire
    • PAGE_READWRITE - acces de scriere si citire
    • PAGE_WRITECOPY - acces copy-on-write
  • dwMaximumSizeHigh
    Dimensiunea zonei (partea superioara)
  • dwMaximumSizeLow
    Dimensiunea zonei (partea inferioara)
  • lpName
    Numele obiectului. Daca este NULL se va crea un obiect anonim.
    Daca exista deja un obiect File Mapping cu acest nume functia va incerca sa acceseze/deschida obiectul deja existent.
    Daca exista un obiect cu acelasi nume dar alt tip, functia va esua.
MapViewOfFile()
Functia intoarce un pointer la zona de memorie partajata si are sintaxa:

LPVOID MapViewOfFile(

HANDLE hFileMappingObject,

DWORD dwDesiredAccess,

DWORD dwFileOffsetHigh,

DWORD dwFileOffsetLow,

SIZE_T dwNumberOfBytesToMap

);

Parametrii:
  • hFileMappingObject
    Obiectul care va fi mapat, intors de functia CreateFileMapping() sau OpenFileMapping()
  • dwDesiredAccess
    Accesul dorit:

    • FILE_MAP_WRITE - acces de citire si scriere
    • FILE_MAP_READ - acces doar de citire
    • FILE_MAP_ALL_ACCESS - similar FILE_MAP_WRITE
    • FILE_MAP_COPY - acces copy-on-write - modificarile facute NU sunt scrise si in fisierul original de pe disc
  • dwFileOffsetHigh
    Deplasamentul (de unde sa inceapa maparea) - partea superioara
  • dwFileOffsetLow
    Deplasamentul (de unde sa inceapa maparea) - partea inferioara
  • dwNumberOfBytesToMap
    Numarul de octeti care vor fi mapati. Daca este zero se mapeaza intreg File Mapping-ul.

1.2. Accesul la o zone de memorie partajata deja creata


Pentru a accesa o zona de memorie partajata, creata de alt proces, se utilizeaza urmatoarele functii (in ordinea specificata):
  1. OpenFileMapping() - o functie pregatitoare care acceseaza (deschide) un obiect de tipul File Mapping.
  2. MapViewOfFile() - pentru a mapa efectiv zona de memorie.
OpenFileMapping()
Acceseaza o resursa/obiect deja existent de tipul FileMapping si are sintaxa:

HANDLE OpenFileMapping(

DWORD dwDesiredAccess,

BOOL bInheritHandle,

LPCTSTR lpName

);

Semnificatia parametrilor:
  • dwDesiredAccess - accesul dorit, vezi CreateFileMapping()
  • bInheritHandle - daca este TRUE handle-ul va fi mostenit de copii.
  • lpName - numele obiectului/resursei FileMapping care se doreste accesat.

1.3. Exemplu de folosire a memoriei partajate


Exemplul consta in doua programe distincte:
  1. programul server, cel care creeaza zona de memorie partajata
  2. programul client, cel care acceseaza zona de memorie partajata

In ambele programe functia DoError() nu este prezenta, ea avand rolul de a trata eroarea respectiva, de obicei afisarea unui mesaj, eventual eliberarea altor resurse si terminarea programului. Programul server

 HANDLE hMapFile;

hMapFile = CreateFileMapping(

INVALID_HANDLE_VALUE, // swap, nu mapam un fisier anume

NULL, // securitatea standard

PAGE_READWRITE, // access read/write

0, // dimensiunea - partea superioara

1024, // dimensiunea - partea inferioara

"sadyczone"); // numele obiectului



if (hMapFile == NULL) DoError("Nu pot crea obiectul FileMapping");



LPVOID lpMapAddress;

lpMapAddress = MapViewOfFile(

hMapFile, / // handle-ul obiectului FileMapping

FILE_MAP_ALL_ACCESS, // read/write permission

0, // deplasamentul

0, // deplasamentul

0); // dimensiunea; 0 = totul = 1024



if (lpMapAddress == NULL)

DoError("Nu pot mapa zona de memorie");



// acum la adresa pointerului lpMapAddress avem memoria partajata.


Programul client

 HANDLE hMapFile;

LPVOID lpMapAddress;

hMapFile = OpenFileMapping(

FILE_MAP_ALL_ACCESS, // access read/write

FALSE, // copii NU vor mosteneastenii handle-ul

"sadyczone"); // numele obiectului



if (hMapFile == NULL)

DoError("Nu pot accesa obiectul FileMapping");



lpMapAddress = MapViewOfFile(

hMapFile, // handle-ul obiectului FileMapping

FILE_MAP_ALL_ACCESS, // access read/write

0, // deplasamentul

0, // deplasamentul

0); // dimensiunea; 0 = mapam tot



if (lpMapAddress == NULL)

DoError("Nu pot mapa zona de memorie");



// acum la adresa pointerului lpMapAddress avem memoria partajata.



2. Cozi de mesaje (Mailslots)


Cozile de mesaje sunt folosite de procese pentru a comunica intre ele prin mesaje. Aceste mesaje isi pastreaza ordinea in interiorul cozii de mesaje.
Mailslots sunt un fel de pseudo-fisiere care rezida in memorie, si deci pot fi folosite prin intermediul functiilor standard de acces la fisiere. Datele dintr-un astfel de mailslot pot avea orice forma, atata timp cat nu depasesc limita de 64Kbytes. Fiind pastrate in memorie toate aceste date au un caracter volatil, spre deosebire de fisiere, iar cand toate handle-uri la un mailslot sunt distruse, acesta la randul sau este distrus impreuna cu datele, iar memoria este eliberata.
Sistemul de cozi de mesaje bazate pe mailslots este de tipul client-server. Astfel:
  • serverul mailslot
    Este procesul care creeaza si detine coada de mesaje, obtinand astfel un handle. Numai cu ajutorul acestui handle se poate citi din coada de mesaje. Exista si posibilitatea ca handle-ul sa fie mostenit.
  • clientul mailslot
    Este un proces care pune mesaje in coada. Se poate sa fie mai multi clienti.

Este posibil sa fie trimise mesaje de tipul broadcast in interiorul unui domeniu. Astfel daca mai multe procese din domeniu creeaza fiecare un mailslot cu acelasi nume, toate vor primi mesajele destinate aceului mailslot si trimise domeniului.

Limitari:
  • Mesajele de tip broadcast sunt limitate la maximum 424 bytes, iar incercarea de a trimite un mesaj broadcast mai mare va esua iar functia va intoarce eroare.
  • NU pot fi trimise mesaje de lungime 425 bytes sau 426 bytes.
  • Lungimea maxima a unui mesaj este 64 Kbytes.

2.1. Denumirea Mailslot-urilor


Cand un proces creeaza un mailslot trebuie sa-i atribuie o denumire, care are urmatoarea forma:

\\.\mailslot\[path]<nume>

Atentie! prefixul "\\.\mailslot\" trebuie sa existe exact in aceasta forma, el fiind urmat de un nume, care eventual va fi precedat de o cale. Calea este asemanatoare cu cea a fisierelor. Un exemplu valid fiind: "\\.mailslot\sadyc\commands"
Un proces client pentru a scrie intr-un mailslot va folosi aceeasi denumire.
Mailslots pot fi folosite si pentru a comunica cu procese care ruleaza pe alte calculatoare. In acest sens clientul va folosi denumiri care au structura:

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

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

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

Pentru a trimite mesaje tuturor, denumirea va avea structura:

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

2.2. Crearea cozilor de mesaje


Pentru a crea un mailslot se foloseste functia CreateMailslot care are urmatoare sintaxa si intoarce un handle:

HANDLE CreateMailslot(

LPCTSTR lpName,

DWORD nMaxMessageSize,

DWORD lReadTimeout,

LPSECURITY_ATTRIBUTES lpSecurityAttributes

);

Parametrii au urmatoarea semnificatie:
  • lpName
    Pointer la un sir de caractere care contine denumirea cozii de mesaje. Forma acceptata este cea descrisa in sectiunea anterioara.
  • nMaxMessageSize
    Dimensiunea maxima, in bytes, a unui mesaj care poate fi scris in coada de mesaje. Valoarea zero permite mesaje de orice dimensiune.
  • lReadTimeout
    Timpul, in milisecunde, dupa care o operatie de citire expira.
    Valoarea zero inseamna ca nu se va astepta deloc, iar valoare MAILSLOT_WAIT_FOREVER are ca rezultat o operatie de citire care nu expira.
  • lpSecurityAttributes - are semnificatia cunoscuta

In cazul in care se incearca crearea unui mailslot cu o denumire care deja exista se va intoarce eroare.

Handle-ul intors va fi folosit pentru a efectua operatii (citire, scriere) cu mailslot-ul.

2.3. Deschiderea cozilor de mesaje existente


Pentru a deschide un mailslot se foloseste functia CreateFile care va primi in loc de numele fisierului denumirea cozii de mesaje care se doreste a fi deschisa si flagul FILE_SHARE_READ (mai multe procese pot scire in aceeasi coada de mesaje).

2.4. Scrierea si citirea in/din cozile de mesaje


Citirea si respectiv scrierea din/in cozile de mesaje sunt asemanatoare cu operatiile cu fisere, folosindu-se aceleasi functii:
  • ReadFile() si ReadFileEx() - pentru citire
  • WriteFile() si WriteFileEx() - pentru scriere

O descriere amanuntita a acestor functii gasiti in MSDN.

2.5. Obtinerea de informatii despre cozile de mesaje


Pentru a obtine informatii despre o coada de mesaje se foloseste functia urmatoare:

BOOL GetMailslotInfo(

HANDLE hMailslot,

LPDWORD lpMaxMessageSize,

LPDWORD lpNextSize,

LPDWORD lpMessageCount,

LPDWORD lpReadTimeout

);

Care primeste urmatorii parametrii:
  • hMailslot
    Handle-ul cozii de mesaje
  • lpMaxMessageSize
    Dimensiunea maxima a unui mesaj
  • lpNextSize
    Pointer la o zona in care va fi depusa dimensiunea urmatorului mesaj din coada. Valoarea MAILSLOT_NO_MESSAGE specifica faptul ca nu exista nici un mesaj in coada.
    Acest pointer poate fi NULL daca nu se doreste aceasta informatie.
  • lpMessageCount
    Pointer la o zona in care va fi depus numarul de mesaje care asteapta sa fie citite.
    Acest pointer poate fi NULL daca nu se doreste aceasta informatie.
  • lpReadTimeout
    Pointer la o zona in care va fi depus timpul dupa care o operatie de citire expira.
    Acest pointer poate fi NULL daca nu se doreste aceasta informatie.

2.6. Schimbarea timpului de expirare


Singura caracteristica a unei cozi de mesaje care poate fi schimbata dupa ce coada a fost creata este timpul de expirare. (Dimensiunea maxima a mesajelor acceptate de o cada NU mai poate fi schimbata dupa ce aceasta a fost creata.)
Functia care seteaza aceasta caracteristica este urmatoarea:

BOOL SetMailslotInfo(

HANDLE hMailslot,

DWORD lReadTimeout

);

Parametrii au semnificatiile cunosctue.

2.7. Exemple


Pentru a crea o coada de mesaje (mailslot) se poate folosi secventa urmatoare de cod:

HANDLE hSlot;



hSlot = CreateMailslot("\\\\.\\mailslot\\test_mailslot",

0, // orice dimensiune

MAILSLOT_WAIT_FOREVER, // fara timp de expirare

NULL); // atribut de securitate



if (hSlot1 == INVALID_HANDLE_VALUE)

DoError("Nu pot crea coada de mesaje");

Exemplul urmator este pentru a accesa (deschide) o coada de mesaje in vederea trimiterii de mesaje:

HANDLE hFile;



hFile = CreateFile("\\\\.\\mailslot\\test_mailslot",

GENERIC_WRITE, // accesul dorit

FILE_SHARE_READ, // acest flag este absolut necesar

NULL, // securitate

OPEN_EXISTING, // coada deja exista

FILE_ATTRIBUTE_NORMAL, // atributele

NULL); // fisier template



if (hFile == INVALID_HANDLE_VALUE)

DoError("Nu pot deschide coada de mesaje");

In ambele exemple functia DoError() nu este prezenta, ea avand rolul de a trata eroarea respectiva, de obicei afisarea unui mesaj, eventual eliberarea altor resurse si terminarea programului.
  
 

Inapoi la prima parte

Inapoi la pagina principala