Table of Contents
1. Memorie partajata (File Mapping)1.1. Crearea unei zone de memorie partajata2. Cozi de mesaje (Mailslots)
1.2. Accesul la o zone de memorie partajata deja creata
1.3. Exemplu de folosire a memoriei partajate
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:CreateFileMapping()
- CreateFileMapping() - este o functie pregatitoare care creeaza un obiect de tipul File Mapping, reprezentat de un HANDLE.
- MapViewOfFile() - pentru a mapa efectiv zona de memorie. Functia intoarce un pointer la zona de memorie partajata.
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:MapViewOfFile()
- 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.
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):OpenFileMapping()
- OpenFileMapping() - o functie pregatitoare care acceseaza (deschide) un obiect de tipul File Mapping.
- MapViewOfFile() - pentru a mapa efectiv zona de memorie.
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:
- programul server, cel care creeaza zona de memorie partajata
- 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 serverHANDLE hMapFile;Programul client
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.
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