500 likes | 705 Views
Sistemsko programiranje. III. poglavlje Upravljanje memorijom, Memorijski mapirane datoteke i DLLovi. Ciljevi. Nakon svladavanja ovog poglavlja trebali bi znati: Opisati arhitekturu upravljanja memorijom kod Windowsa i ulogu heapova i memorijski mapiranih datoteka
E N D
Sistemsko programiranje III. poglavlje Upravljanje memorijom, Memorijski mapirane datoteke i DLLovi
Ciljevi • Nakon svladavanja ovog poglavlja trebali bi znati: • Opisati arhitekturu upravljanja memorijom kod Windowsa i ulogu heapova i memorijski mapiranih datoteka • Koristiti višestruke neovisne heapove u aplikacijama koje traže dinamičko upravljanje memorijom • Odgovarati na greške prilikom zauzimanja (alokacije) memorije • Koristiti memorijski mapirane datoteke • Odrediti kada treba koristiti neovisne heapove a kada memorijski mapirane datoteke, i opisati prednosti i nedostatke jednog i drugog • Opisati Windows dinamičke biblioteke - dynamic link libraries (DLL) • Opisati razliku između statičkog, implicitnog i eksplicitnog linkanja (povezivanja), te opisati prednosti i nedostatke svakog od njih • Koristiti DLLove za učitavanje različitih implementacija iste funkcije
Pregled poglavlja (1/2) 32-bitni operacijski sustav, pokazivači su 4-byte-ni objekti Win64 nam omogućavaju 64-bitne pokazivače Procesi imaju privatni 4GB virtualni adresni prostor • Pola (2GB) pripada procesu • Ostatak je zauzet za podatke koji se dijele i za sam kod • Win64 uvećava VA prostor; mnoge aplikacije to traže Programi mogu kreirati neovisne memorijske “heap”-ove Procesi mogu mapirati datoteke u memoriju • Procesi mogu dijeliti memoriju preko mapirane datoteke • Brzo i efikasno za različite načine procesiranja datoteka
Pregled poglavlja (2/2) • DLLovi i monolitni programi • Skupljanje cjelokupnog izvornog koda, uključujući često korištene skupine kao što su korisničke funkcije • Stavljanje cijelog izvornog koda u jedan projekt • Neučinkovitost • Prevođenje istog koda u svim projektima • Sve datoteke za izvođenje uključuju isti objektni kod • Nepotrebni gubitak prostora na disku i fizičke memorije prilikom izvođenja • Složenost održavanja ako se zajednički/djeljeni kod promjeni
Dnevni red • 1. dio Upravljanje memorijom i heapovi • 2. dio Memorijski mapirane datoteke • 3. dio Dynamic Link Libraries (DLL)
1. dio Upravljanje memorijom i heapovi
Arhitektura upravljanja memorijom Windows Program C library: malloc, free Heap API: HeapCreate, HeapDestroy, HeapAlloc, HeapFree MMF API: CreateFileMapping, CreateViewOfFile Virtual Memory API Windows Jezgra (Kernel) zajedno s Upravljanjem virtualnom memorijom (Virtual Memory Manager) Disk & Datotečni sustav (File system) Fizička memorija (RAM)
Heapovi (1/2) • Memorijska polja (pools of memory) unutar virtualnog adresnog prostora procesa (heap = hrv. hrpa) • Svaki proces ima svoj preddefinirani (default) procesni heap • Proces može imati više od jednog heapa. Prednosti razdvajanja heapova uključuju: • Pravednost (unutar niti i samog korištenja) • Učinkovitost zauzimanja (blokovi fiksne veličine u svakom heapu) • Učinkovitost oslobađanja (može se osloboditi čitava struktura podataka unutar jednog funkcijskog poziva) • Lokalnost kod adresiranja
Heapovi (2/2) Svaki proces ima svoj procesni heap Svaki heap ima svoj handle Programer može koristiti procesni heap kako bi kreirao nove heapove HANDLE GetProcessHeap (VOID) Vraća: Handle od procesnog heapa; NULL u slučaju greške
Upravljanje memorijom kod višestrukih heapova Program Virtualni adresni prostor nije zauzeto ProcHeap = GetProcessHeap ( ); pRoot = HeapAlloc (ProcHeap); Proces Heap · · · nije zauzeto RecHeap = HeapCreate ( ); NodeHeap = HeapCreate ( ); Record · · · RecHeap Record while ( ) { pRec = HeapAlloc (RecHeap); pNode = HeapAlloc (NodeHeap); · · · } Record nije zauzeto Node · · · NodeHeap Node HeapFree (RecHeap, 0, pRec); HeapFree (NodeHeap, 0, pNode); HeapDestroy (RecHeap); HeapDestroy (NodeHeap); Node nije zauzeto
Upravljanje heapom (1/3) HANDLE HeapCreate (DWORD flOptions, DWORD dwInitialSize, DWORD dwMaximumSize) Vraća: handle heapa ili NULL kod greške dwMaximumSize — Koliko veliki heap može postati • 0 — “rastući heap”; nema fiksiranog ograničenja • nije nula — “ne-rastući heap” • Cijeli blok je zauzet iz virtualnog adresnog prostora • Ali samo inicijalna veličina je predana straničnoj datoteci
Upravljanje heapom (2/3) flOptions jest kombinacija dvije zastavice: • HEAP_GENERATE_EXCEPTIONS • HEAP_NO_SERIALIZE Kod generiranja iznimki možemo izbjeći ekspicitno testiranje nakon svakog poziva funkcije koja upravlja heapom
Upravljanje heapom (3/3) BOOL HeapDestroy (HANDLE hHeap) • hHeap — heap napravljen koristeći HeapCreate • Nikad ne uništavati procesni heap (dobiven koristeći GetProcessHeap) • Prednosti korištenja HeapDestroy: • Nije potrebno obilaziti kroz sve veze nekih struktura podataka • Nije potrebno oslobađati memoriju za svaki pojedinačni član strukture podataka, što nekad može biti vremenski zahtjevno
Upravljanje memorijom heapa (1/4) LPVOID HeapAlloc (HANDLE hHeap, DWORD dwFlags, DWORD dwBytes) Vraća: Pokazivač na blok zauzete memorije (veličine dwBytes) ili NULL ako ne uspije (osim ako nije specifirano generiranje iznimki) hHeap — Handle od procesnog heapa dobivenog s GetProcessHeap ili našeg kreiranog heapa dobivenog s HeapCreate dwFlags — Kombinacija sljedećih zastavica: • HEAP_GENERATE_EXCEPTIONS • HEAP_NO_SERIALIZE • HEAP_ZERO_MEMORY — Zauzeta memorija postavlja se na nulu (ako stavimo ovo, onda izvođenje funkcije traje “malo” duže)
Upravljanje memorijom heapa (2/4) BOOL HeapFree (HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) dwFlags — Treba biti nula (ili HEAP_NO_SERIALIZE) lpMem — Treba imati vrijednost koju vraća funkcija HeapAlloc ili HeapReAlloc hHeap — Treba biti heap iz kojeg je zauzet lpMem
Upravljanje memorijom heapa (3/4) LPVOID HeapReAlloc (HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, DWORD dwBytes) Vraća: Pokazivač na blok memorije koji će biti nanovo zauzet. Kod greške vraća se NULL ili se pojavljuje iznimka. dwFlags — Neke osnovne kontrolne opcije: • HEAP_GENERATE_EXCEPTIONS i HEAP_NO_SERIALIZE • HEAP_ZERO_MEMORY — Samo novo-zauzeta memorija je incijalizirana • HEAP_REALLOC_IN_PLACE_ONLY — Ne treba micati blok lpMem — Postojeći blok u heapu hHeap za koji treba zauzeti memoriju ponovno dwByte — Nova veličina bloka
Upravljanje memorijom heapa (4/4) DWORD HeapSize (HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) Vraća: Veličina bloka ili nula prilikom greške.
Zastavice kod heapova HEAP_NO_SERIALIZE • Postoji u HeapCreate, HeapAlloc, i drugim funkcijama • Dobitak pri izvedbi (testovi pokazuju oko 15%) jer funkcija ne omogućava međusobno isključivanje niti koje pristupaju heapu • Može se sigurno koristiti ako (ALI, TREBA BITI OPREZAN): • Proces koristi samo jednu nit • Svaka nit ima svoj heap(ove) kojem(kojima) druge niti ne mogu pristupati • Se napravi sopstveni mehanizam za međusobno isključivanje koji će onemogućiti konkurentni pristup heapu od strane pojedinih niti. • Se koristi HeapLock i HeapUnlock HEAP_GENERATE_EXCEPTIONS • Omogućava da se izbjegne testiranje grešaka nakon svakog pokušaja zauzimanja (alokacije) memorije
Druge funkcije za heap • HeapValidate • Određuje da li je heap oštećen • HeapCompact • Sažima povezane slobodne blokove; vraća veličinu u byte-ima • HeapWalk • Pobrojava sve blokove zauzete unutar heapa
2. dio Memorijski mapirane datoteke
O mapiranju (prslikavanju) datoteka • Mapiranje datoteka jest povezivanje sadržaja datoteke s dijelom virtalnog adresnog prostora procesa • Prikaz datoteke (file view) jeste dio virtualnog adresnog prostora koji proces koristi da pristupi sadržaju datoteke • Objekt koji je mapirana datoteka se može sastojati od cijele ili samo dijela datoteke • Mapiranje datoteka omogućava dvije važne prednosti: - Dijeljenje memorije - Brži i lakši pristup datoteci
Memorijski mapirane datoteke Prednosti mapiranja (preslikavanja) prostora virtualne memorije direktno u obične datoteke, radije nego u datoteku za straničenje: • Nikada ne treba raditi direktni datotečni I/O • Strukture podataka koje kreirate sačuvane unutar datoteke • Mogu se koristiti algoritmi koji se koriste unutar memorije (procesiranje stringova, sortiranja, pretraživanja) za procesiranje podataka iako datoteka može biti puno veća nego što je ukupna fizička memorija • Nema potrebe za upravljanjem spremnicima (bufferima) i podacima datoteke koje spremnici sadrže • Višestruki procesi mogu dijeliti memoriju (to je jedini način), a prikazi datoteke bit će koherentni (sukladni) • Nema potrebe da se zauzima prostor u datoteci za straničenje
Adresni prostor procesa mapiran u datoteku Program Datoteka fH = CreateFile ( ); mH = CreateFileMapping (fH); · · · while ( ) { pRecA = MapViewOfFile (mH); pRecB = MapViewOfFile (mH); pRecB -> Data = pRecA -> Data; · · · UnmapViewOfFile (pRecA); UnmapViewOfFile (pRecB); } Adresni prostor procesa CloseHandle (mH); CloseHandle (fH);
Objekti mapirane datoteke (1/4) HANDLE CreateFileMapping (HANDLE hFile, LPSECURITY_ATTRIBUTES lpsa, DWORD dwProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCTSTR lpMapName) Vraća: Handle mapirane (preslikane) datoetke ili NULL
Objekti mapirane datoteke (2/4) Parametri • hFile — handle otvorene datoteke; zastavice zaštite kompatibilne s dwProtect • LPSECURITY_ATTRIBUTES — NULL za sada • dwProtect — Kako se može pristupiti mapiranoj datoteci: • PAGE_READONLY — Stranice u mapiranom području su samo za čitanje • PAGE_READWRITE — Puni pristup ako i hFile ima obadva GENERIC_READ i GENERIC_WRITE pristup • PAGE_WRITECOPY — Kada se napravi neka promjena u mapiranoj memoriji, kopija će biti zapisana u datoteku za straničenje
Objekti mapirane datoteke (3/4) • dwMaximumSizeHigh i dwMaximumSizeLow — Određuju veličinu mapiranog objekta; 0 za trenutnu veličinu datoteke. Datoteka će biti proširena ako je tekuća veličina datoteke manja od veličine mapiranog objekta (tj. preslika). • lpMapName — Imenuje mapriani objekt, s time dopuštajući drugim procesima da dijele isti taj objekt
Objekti mapirane datoteke (4/4) Handle na mapiranu datoteku može se dobiti isto tako ako zadamo već postojeće ime mapiranog objekta HANDLE OpenFileMapping (DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpNameP) Vraća: Handle mapirane datoteke ili NULL CloseHandle uništava handle mapriane datoeke (kao i handle-ove druge vrste)
Mapiranje adresnog prostora procesa (1/3) LPVOID MapViewOfFile (HANDLE hMapObject, DWORD dwAccess, DWORD dwOffsetHigh, DWORD dwOffsetLow, DWORD cbMap) Vraća: Početnu adresu bloka (file view) ili NULL kod greške hMapObject — Identificira objekt mapirane datoteke (handle) dwAccess — Mora biti kompatibilan s pristpom mapiranom objektu: • FILE_MAP_WRITE • FILE_MAP_READ • FILE_MAP_ALL_ACCESS
Mapiranje adresnog prostora procesa (2/3) dwOffsetHigh i dwOffsetLow • Početna lokacija područja mapirane datoteke • Mora biti višekratnik od 64K • Nula ako se mapira od početka datoteke cbMap — Veličina u byte-ovima mapiranog područja • Nula upućuje na cijelu datotekuPrimjedba: Veličina mape (preslika) je ograničena na 32-bitne adrese (Win64??)
Mapiranje adresnog prostora procesa (3/3) MapViewOfFileEx je slična, ali se može “predložiti” adresa BOOL UnmapViewOfFile (LPVOID lpBaseAddress) • Da oslobodimo pregled datoteke (file views)
Ograničenja mapiranja datoteka • Nepodudaranje s Windows 64-bitnim datotečnim sustavom i 32-bitnim memorijskim adresiranjem (Win64 više nema ovakvih problema) • Sa velikim datotekama (većim od 4GB) ne može se mapirati sve unutar virtualnog memorijskog prostora (Win64 više nema ovakvih problema) • Podatkovni prostor procesa je ograničen na 2GB (Win64 više nema ovakvih problema) • Ne može se koristiti svih 2GB; kontinuirani blokovi u tom prostoru bit će manji (Ako imamo dovoljno memorije, Win64 više nema ovakvih problema) • Kada radimo s velikim datotekama, moramo napisati programski kod koji će se brinuti o mapiranju i oslobađanju (unmap) područja datoteke kako koje zatrebamo (Ako imamo dovoljno memorije, Win64 više nema ovakvih problema)
Bazirani pokazivači (1/2) Bazirani pokazivači (based pointers) Ako se koriste pokazivači unutar područja mapirane datoteke onda oni moraju biti tipa _based • Konvencionalni pokazivači imaju vrijednost virtualne adrese • Ova bazna adresa će biti zasigurno drugačija kada naredni put mapiramo datoteku, ili formiramo novi pregled datoteke jednog te istog područja • Pokazivač mora biti “baziran” na osnovu adrese pregleda datoteke (file view-a)
Bazirani pokazivači (2/2) int *pi; int __based(pi) *bpi, i; ... pi = MapViewOfFile (...); *pi = 3; bpi = pi; i = *bpi; ...
3. dio Dynamic Link Libraries (DLL)
Statičke biblioteke • Prednosti • Pojednostavljuje izgradnju projekta • Mane • Stvari vezane za disk i memoriju • Održavanje zahtjeva ponovno linkanje i redistribuciju • Različiti programi mogu koristiti različite verzije biblioteke • Programi ne mogu koristiti alternativne korisničke implementacije za različite situacije
Dynamic link libraries (1/4) • Dinamički povezive biblioteke - DLL-ovi rješavaju mnoge probleme jako jednostavno i spretno • Funkcije biblioteke se povezuju (linkaju) pri: • Učitavanju programa — implicitno povezivanje • Izvođenju programa — eksplicitno povezivanje • Slika programa može biti puno manja • Ne uključuje funkcije iz biblioteke • Više programa može koristiti jedan DLL • Samo jedna kopija će biti učitana u memoriju • Svi programi preslikavaju svoj adresni prostor za DLL kod • Svaka nit će imati svoju kopiju ne dijeljenog prostora na stogu
Dynamic link libraries (2/4) • Nove verzije ili alternativne implementacije: • Omogućavaju se nove verzije DLL-ova • Svi programi mogu koristiti novu verziju bez modifikacija • Eksplicitno povezivanje: • Program odlučuje dok se izvršava koju verziju biblioteke će koristiti • Različite biblioteke mogu biti alternativne implementacije jedne te iste funkcije • Mogu se izvoditi totalno različite zadaće • Isto kao što rade različiti programi • Biblioteka će se izvoditi u istom procesu i niti kao i program koji ju poziva
Dynamic link libraries (3/4) • DLL-ovi se koriste u skoro svim operacijskim sustavima • Uključujući UNIX i prastare Windows 3.1 • Windows (sve verzije) koriste DLL-ove da implementiraju OS međusklopovlje (interface), pored ostalih stvari • Windows 3.1 DLL-ovi se izvode u istom adresnom prostoru za sve procese • Windows DLL-ovi se izvode u virtualnom adresnom prostoru svakog procesa
Dynamic link libraries (4/4) • Više Windows procesa mogu dijeliti DLL kod • Kod, kada se pozove, izvodi se kao dio pozivajućeg procesa i niti • Biblioteka može koristiti resurse pozivajućeg procesa (datotečne handle-ove, ...) • Koristi se stog pozivajuće niti • DLL-ovi moraju biti zaštićeni unutar niti “thread-safe” • DLL-ovi mogu eksportirati varijable kao i funkcijske ulazne točke
Implicitno povezivanje (1/2) • Implicitno, ili prilikom učitavanja, povezivanje je lakša od dviju tehnika • Koraci: • Prikupiti i napraviti izvorni kod funkcija kao DLL • Proces pravljenja (Build) projekta stvara .LIB datoteku • Staviti .LIB u projektni ‘library’ direktorij • Proces pravljenja također stvara .DLL datoteku
Implicitno povezivanje (2/2) • Sadrži aktuelnu izvršnu sliku • Stavljena je u isti direktorij kao i aplikacija koja ju koristi • Tekući radni direktorij je sekundarna lokacija • Nakon toga sistemski direktorij, Windows direktorij, PATH • Program učitava DLL tijekom svoje incijalizacije • Mora se “eksportirati” funkcijsko povezivanje unutar DLL izvornog koda
Eksportiranje i importiranje (1/3) DLL ulazna točka mora biti deklarirana • Microsoft C, koristi _declspec (dllexport) modifikator smještaja: _declspec (dllexport) DWORD MyFunction (...); Program koji poziva funkciju, deklarira ju kao funkciju koju treba importirati • Koristi se _declspec (dllimport) modifikator smještaja
Eksportiranje i importiranje (2/3) • Standardna tehnika unutar ‘include’ datoteke • Treba koristiti preprocesorsku varijablu kao što je primjerice “MYPROJ_EXPORTS“ • “MYPROJ” je ime vašeg projekta #ifdef MYPROJ_EXPORTS #define LIBSPEC _declspec (dllexport) #else #define LIBSPEC _declspec (dllimport) #endif LIBSPEC DWORD MyFunction (...);
Eksportiranje i importiranje (3/3) • Unutar DLL projekta definiramo MYPROJ_EXPORTS • Aplikacija koja poziva ostavlja MYPROJ_EXPORTS nedefiniran • Mogu se eksportirati i importirati i varijable isto kao funkcijske ulazne točke
Eksplicitno povezivanje (1/4) • Eksplicitno (prilikom izvođenja) povezivanje zahtjeva: • Program može učitati DLL — LoadLibrary • Traženje adrese ulazne točke funkcije — GetProcAddress • Prevođenje (cast) adresnog pokazivača na tip funkcije • Pozivanje funkcije koristeći pokazivač • Opcionalno oslobađanje biblioteke — FreeLibrary • Primjedba: Funkcija nije deklarirana u programu koji poziva DLL; zato se definira varijabla kao pokazivač na funkciju • Zbog toga, nije potrebna .LIB datoteka prilikom povezivanja (znači linkanja programa koji koristi DLL)
Eksplicitno povezivanje (2/4) HINSTANCE LoadLibrary (LPCTSTR lpLibFileName) Vraćeni handle je NULL ako dođe do greške HINSTANCE, radije nego konvencionalni HANDLE • Sadrži drugačiju informaciju
Eksplicitno povezivanje (3/4) BOOL FreeLibrary (HINSTANCE hLibModule) Gotovi smo s upotrebom biblioteke ili želimo neku drugu verziju LoadLibraryEx je slična funkcija • Nekoliko zastavica koje određuju alternativne putanje traženja biblioteke te učitavanje biblioteke kao datoteke s podacima
Eksplicitno povezivanje (4/4) Za dobivanje ulazne točke funkcije • FARPROC GetProcAddress (HMODULE hModule, LPCSTR lpProcName) hModule jest primjerak (instance) dobiven od LoadLibrary • Ili GetModuleHandle (ovu funkciju ovdje ne opisujemo) lpProcName jest ime ulazne točke funkcije • Ne može biti Unicode NULL se vraća u slučaju greške FARPROC, nešto kao “dugački pokazivač” - zastarjelo (vuče korjene još od 16-bitnih pokazivača)
Učitavanje DLL-a BOOL (*MojaFunk)(LPCTSTR, LPCTSTR, BOOL); FARPROC pMF; /* Učitaj dinamičku biblioteku imena moja.dll */ hDLL = LoadLibrary (“moja.dll”); /* Nađi adresu ulazne točke funkcije */ pMF = GetProcAddress (hDLL, ”MojaFunk"); /* Prevedi u pokazivač na funkciju */ MojaFunk = (BOOL (*)(LPCTSTR, LPCTSTR, BOOL)) pMF; /* Pozovi funkciju */ MojaFunk(“prvi string”, “drugi string”, FALSE); /* Oslobodi biblioteku (nakon ovog više ne smijemo pozivati MojaFunk(..) */ FreeLibrary( hDLL );