Hurmuzache Ciprian-Constantin, grupa 431A
Mardare Oana-Viorica, grupa 431A
Pruiu Ana-Maria, grupa 431A
Manole Ion-Laurentiu, grupa 432A
Cuprins
Partea I – Gestiunea memorii: lucruri generale si algoritmi de gestiune a memoriei
– Mardare Oana-Viorica, grupa 431A
1. Introducere: memoria, caracteristici, obiective, tipuri
2. Swapping
3. Gestiunea memoriei libere
3.1. Gestiunea memoriei prin intermediul hartii de biti
3.2. Gestiunea memoriei libere cu liste inlantuite: algoritmul fist fit,
next fit, best fit
4, Memoria virtuala: necesitatea memoriei virtuale, definitii, legatura dintre
memoria fizica si memoria virtuala
4.1. Paginarea: definitie, exemplu, gestiunea memoriei folosind
tabelul de pagini
4.2. Algoritmi de gestionare a memoriei prin paginare:
- Algorimul Second Chance(SC)
- Algoritmul Clock(C)
- Algoritmul Least Recently Used(LRU)
- Algoritmul Random(R)
Partea a-II-a – Algoritmi de gestiune a memoriei – Hurmuzache Ciprian-
Constantin, grupa 431A
1. Introducere
2. Paginarea
- Algoritmul optim Belady
- Algoritmul NRU
- Algoritmul FIFO
- Algoritmul NFU
- Algoritmul Aging(A)
3. Segmentarea
- Compactarea
- Algoritmul First fit
- Algoritmul Best fit
- Conceptul de Garbage Collector
- Algoritmul Mark-and-Sweep
- Algoritmul trenului
-
Partea a-III-a – Gestiunea memoriei in Linux – Pruiu Ana-Maria, grupa 431A
1.1. Introducere
1.2. Gestiunea memoriei fizice in Linux
1.3. Alocarea si dealocarea paginilor de memorie
1.3.1. Alocatorul de zona
1.3.2. Algoritmul buddy (algoritmul “prietenului”)
1.3.3. Alocatorul slab
Partea a-IV-a – Apeluri de sistem pentru gestiunea memoriei: comparative Linux
si Windows – Manole Ion-Laurentiu, grupa 432A
1. Introducere
2. Apeluri de sistem pentru gestiunea memoriei in Linux: brk, sbrk,
getrlimit, setrlimit, mnap, mremap, munmao, msync,
3. Apeluri de sistem pentru gestiunea memoriei in Windows: VirtualAlloc,
VirtualProtect, VirtualFree, GetProcessHeap, HeapAlloc, HeapReAlloc,
HeapFree, HeapDestroy
Partea I
Gestiunea memoriei
Mardare Oana-Viorica, grupa 431A
1. Introducere
Memoria reprezinta partea calculatorului in care se stocheaza programele si
datele care sunt preluate de catre processor, executate si apoi scrise tot in memorie.
Memoria are mai multe caracteristici, ca de exemplu:
1. Capacitatea de memorare = numarul celulelor de memorie continute de o
unitate de memorie.
2. Timpul de acces la o unitate de memorie = timpul in care o celula de
memorie trasfera date de/la locatia de memorie.
3. Rata de acces = inversul timpului de acces.
4. Timpul de ciclu la o unitate de memorie = timpul minim intre doua accesari
successive
5. Timp de revenire = diferenta dintre timpul de acces si timpul de ciclu.
6. Rata de transfer = inversa timpului de ciclu (cantitatea maxima de
informative).
7. Costul total al unitatii de memorie = costul circuitelor de acces la celulele de
memorie.
8. Costul unitar = costul de memorare a unui cuvant de informative
Obiectivele gestiunii memoriei sunt:
Contabilizarea zonelor libere/ocupate
Aloca memoria necesara rularii proceselor
Dealoca memoria de care procesele nu mai au nevoie
Ajuta la protejarea memoriei
Face transferal proceselor intre memoria principala si hard disk (proces care
poarta numele de swapping)
Ajuta la calculul de translatare a adresei
Memoriile pot fi de mai multe feluri:
- Memoria cache
- Memoria principal
- Memoria secundara
- Memoria suplimentara
In functie de aceste tipuri de memorie, rolul si locul lor din sistemul de
calcul putem realiza schema de ierarhizare a memoriei, pe care am reprezentat-o in
figura de mai jos [2]:
Memoria interna este alcatuita din memoria cache si memoria principal.
Memoria interna contine programele si datele pentru toate procesele care se
executa in momentul respectiv de catre sistemul de calcul. Memoria interna este de
tip Random Access Memory (RAM).
Memoria principala este memoria de lucru a calculatorului si este realizata
cu circuite integrate de tip DRAM (Dynamic Random Access Memory). Memoria
principala deserveste la pastrarea datelor active pentru a fi apelate de catre
procesor, chiar daca nu este imediat nevoie de ele. Datele se activeaza la
transferarea lor din memoria secundara in memoria principala. Pentru pastrarea
datelor atunci cand calculatorul este oprit se ocupa memoria nevolatila cu acces
aleatoriu (NVRAM) care pastreaza informatii esentiale cum ar fi: data,ora, optiuni
de pornire/oprire.
Memoria cache este o memorie specializata prin intermediul careia se scade
timpul de acces la informatiile stocate in memoria interna.
Memoria externa stocheaza programele care ajuta la prelucrarea datelor,
dar pentru a fi utilizate este necesar sa fie aduse in memoria principala, iar de aici
vor fi apelate de catre procesor. “Memoria externa este:
Nevolatila
Cu acces positional si timp de acces mare
Cu viteza de transfer mai mica
Cu cost redus
Cu capacitate mult mai mare
De tip read-write
Cu densitate de memorare variabila de la un echipament la altul si de la un
support la altul (optic, magnetic, semiconductor)”[2]
Memoria externa se clasifica astfel:
1. Memorie secundara – are rolul de a extinde memoria principala
2. Memorie suplimentara
-memoria expandata
-memoria extinsa
-memoria de arhivare – stocheaza datele primate din exterior pe o
perioada nedeterminata
2. Swapping
Swapping-ul se refera la transferul proceselor intre hard disk si memoria
principala. Cu timpul, noile programe pot consuma foarte multa memorie RAM,
lucru care a dus la concluzia ca memoria RAM trebuie extinsa intr-un fel sau altul.
Cu trecerea anilor s-au descoperit metode care pot combate neajunsul memoriei
RAM. Avem doua metode care vin in ajutorul nostru: cea mai simpla metoda
poarta numele de “swapping” si prin care se intelege: “aducerea in intregime a unui
proces, rularea lui pentru o perioada, apoi punerea lui inapoi pe disk. Procesele
inactive sunt in general stocate pe disk, deci ele nu consuma memorie cand nu
ruleaza (desi totusi o parte din ele se trezesc periodic sa isi faca treaba, iar apoi
devin iarasi inactive.”[1] A doua metoda este memoria virtuala care “permite
programelor sa ruleze chiar si atunci cand sunt partial in memoria principala.”[1]
In procesul de swapping memoria alocata se schimba in functie de procesele
care vin sau pleaca din memorie. Acest process este ilustrat in figura [1]:
In partea (a) procesul A este in memorie. In (b) si (c) sunt aduse in memorie
procesele B si C. In (d) procesul A este extras din memorie, iar in (e) este adus in
memorie D, apoi in (f) B este scos din memorie pentru ca in (g) sa fie readus A in
memorie, dar in alta locatie fata de unde era initial.
Dupa cum se observa si din figura, cand sunt extrase procese din memorie
raman gauri care pot fi cateodata prea mici pentru a fi ocupate de alte procese si
din aceasta cauza “este posibil sa fie combinate toate si sa formeze o gaura mai
mare prin mutarea proceselor pe cat posibil in jos. Aceasta tehnica este cunoscuta
drept compactarea memoriei. Ea de obicei nu este realizata deoarece are nevoie de
mult timp din partea CPU.”[1]
3.Gestionarea memoriei libere
Daca memorie este alocata dinamic, sistemul de operare trebuie sa o
gestioneze. Acest lucru se poate face in doua feluri: cu ajutorul listelor libere si cu
ajutorul hartii de biti.
3.1.Gestiunea memoriei prin intermediul hartii de biti
“Cu o harta de biti, memoria este divizata in unitati de alocare de
dimensiunea a cateva cuvinte si in jur de cativa kilobytes. Fiecare unitate de
alocare poate lua 2 valori binare: 0 daca unitatea este libera si 1 daca unitatea este
ocupata (sau invers). In figura de mai jos este reprezentata o parte din memories i
harta de biti corespunzatoare”[1]
Cu cat unitatea de alocare este mai mica cu atat harta de biti este mai mare.
De exemplu 32 de biti de memorie vor avea nevoie doar de un bit din harta de biti.
De aici putem deduce usor ca 32n biti de memorie vor avea nevoie de o harta de
memorie de ordinul n.
3.2.Gestionarea memoriei libere cu liste inlantuite
A doua metoda de a gestiona memoria libera consta in folosirea listelor
inlantuite care gestioneaza segmentele de memorie alocata si segmentele de
memorie libera. Exemplu de o lista inlantuita este reprezenta la punctual c) din
figura de mai sus. In figura gasim: procesul care este reprezentat prin litera P, iar
gaura din memorie prin litera H, adresa de la care incepe procesul, lungimea lui si
un pointer catre urmatorul proces. Lista proceselor si a gaurilor poate fi sortata
dupa adresa. In acest caz, alocarea memoriei se poate face prin anumiti algoritmi,
ca de exemplu, best fit, first fit, next fit, cel mai usor si mai rapid dintre acestia
fiind first fit care “parcurge circular lista de gauri si alege prima gaura suficient de
mare pentru a pastra segmentul”[2]. Cand se gaseste o gaura suficient de mare
pentru procesul respective, aceasta se imparte in doua: o parte pentru procesul
nostrum, iar cealalta parte devine la randul ei o alta gaura. Desi acest algoritm este
foarte rapid are si un dezavantaj: creeaza alte gauri in memorie.
Algoritmul next fit este asemanator cu algoritmul first fit, cu diferenta ca
acesta nu cauta o gaura de la inceput, ci din locul de unde a ramas la apelarea
anterioara.
Algoritmul best fit cauta in toata lista gaura cu dimensiunea cea mai
apropiata de dimensiunea procesului nostru. Comparativ cu first fit, best fit este
mult mai lent luand in considerare faptul ca trebuie sa caute in toata lista rezultand
si un consum mult mai mare de memorie. Desi la prima vedere algoritmul best fit
pare mai bun, dupa rularea acestuia memoria va fi umpluta cu gauri de dimensiuni
mici care vor fi mai greu de umplut.
Pentru folosirea avantajoasa a acestor algoritmi se pot folosi 2 liste: una
pentru procese si una pentru gauri, lista in care sunt memorate gaurilor poate fi
sortata in functie de dimensiune rezultand ca algoritmii best fit si first fit vor avea
acelasi timp, iar algoritmul next fit ar fi inutil in aceasta situatie.
4.Memoria virtuala
Cu evolutia sistemelor de programare si a programelor, memoria incepe nu
mai ajunga. Fiecare system de operare are un minim de cerinte in ceea ce priveste
memori RAM. De exemplu sistemul de operare are nevoie de un minim de 512
MB pentru a functiona, iar Windows 7 are nevoie de minin 1GB pentru o
functionare normal, dar daca se ruleaza aplicatii complexe, necesarul de memorie
este mai mare. Bineinteles, daca dispune de mai multa memorie este foarte bine.
Din ce in ce mai mult programele au inceput sa aiba nevoie de memorie mai
multa decat cea care este pusa la dispozitie. Acest lucru este o problema inca de la
inceputurile calculatoarelor.
Pentru a elimina aceste inconveniente in ceea ce priveste insuficienta
memoriei, a fost dezvoltata o noua metoda care poarta numele de memorie
virtuala. “Ideea de bază din spatele memoriei virtuale este faptul ca fiecare
program are propriul spatiu de adrese, care este rupt în bucati numite pagini.
Fiecare pagina este o gamă de adrese contiguu (invecinat). Aceste pagini sunt
mapate pe memoria fizica, dar nu toate paginile trebuie sa fie în memoria fizica
pentru a rula programul. In cazul in care programul face referire la o parte din
spatiul de adrese ale sale, care este in memoria fizica, hardware-ul necesar,
efectueaza maparea pe zbor. Atunci cand programul face referire la spatiul de
adrese care nu se afla în memoria fizica, sistemul de operare este alertat pentru a
obtine piesa lipsa şi a re-executa instructiuni care nu au reusit.”[1]
“Memoria virtuala lucreaza foarte bine intr-un sistem multiprogram, cu biti
si piese a multor programe in memorie in acelasi timp. Cat timp un program
asteapta ca o piesa a sa sa fie citita, CPU poate rula alt process.”[1]
“Memoria virtuala se foloseste cu doua acceptiuni:
- Spatiu virtual care poate fi accesat (initial)
- Zona de memorie plasata nu in memoria principala, ci pe un dispozitiv de
memorare secundar sau extern, cel mai adesea pe hard disk.”[2]
Legatura dintre memoria fizica si memoria virtuala este reprezentata in
figura[2]:
4.1.Paginarea
Paginarea inseamna generarea de adrese virtuale folosind instructiunea:
“MOV REG,1000”[1]
Spatiul adreselor virtuale este divizat in unitati cu diminensiune fixa numite
pagini.
“Exemplu. Fie un sistem cu 16 linii (64 KB) si o memorie instalata de 4
linii.
O singura pagina de 4 KB se poate afla la un moment dat in memoria
principala (MP) sau fizica. Daca programul contine mai multe pagini, restul
paginilor se afla undeva în memoria secundara (pe floppy disk sau pe hard disk).
Cand programul trebuie sa execute o instructiune din pagina urmatoare,
sistemul de gestiune al memoriei virtuale (care face parte din sistemul de operare)
executa urmatoarele operatii:
1. salveaza continutul memoriei periferice în memoria secundara
2. localizeaa pagina respectiva în memoria secundara
3. incarca aceasta pagina in MP
4. asociaza adreselor absolute adresa de memorie fizica de la 0 la 4095
5. continua executia programului.”[2]
Mai jos avem o figura in care este descrisa gestiunea memoriei fizice
folosind tabelul de pagini: [2]
4.2.Algoritmi de gestionare a memoriei prin paginare
Atunci cand pagina necesara sistemului de operare nu se gaseste in memoria
fizica, ci pe disk se ruleaza un algoritm de inlocuire a paginii. Mai jos am descries
o parte din algoritmii de inlocuire a paginilor (alte exemple in partea a-II-a,
subpunctul 2):
1. Algoritmul Second Chance(SC): functioneaza pe principiul algoritmului
FIFO, dar in comparatie cu acesta, algoritmul SC nu scoate imediat prima
pagina inserata in memorie, ci, precum spune si numele lui, ii mai acorda
inca o sansa. Aceasta sansa este acordata doar daca bitul de referinta este
setat. Daca aceasta conditie nu este indeplinita, pagina va fi scoasa din
memorie, iar daca este indeplinita conditia, pagina va fi din nou inserata in
coada.
2. Algoritmul Clock(C): reprezinta evoluatia algortimilor FIFO si SC,
constand in folosirea unui sistem circular in loc de coada, iar un pointer
indica locul un se afla pagina careia i se acorda a doua sansa.
3. Algoritmul Least Recently Used(LRU): este teoretic implementabil, dar
practice este destul de costisitor. Varianta software a acestui algoritm este
scumpa, fiind de tip coada si consta in realizarea unei liste cu toate paginile
din memorie ordonata in functie de cat de des este utilizata o pagina: astfel
pagina cea mai putin utilizata pagina recenta, iar in varful listei gasim cea
mai des utilizata pagina. O alta varianta acestui algoritm are nevoie si de o
implementare hardware reprezentata de un counter care numara de cate ori
este utilizata o pagina, iar cand este vorba de o mutare a unei pagini se
allege pagina cu counterul cel mai mic. In figura de mai jos avem descries
algoritmul LRU folosind o matrice a paginilor la care se face referire [1]:
4. Algoritmul Random(R): pur si simplu inlocuieste o pagina din memorie,
neafacand nici o selectie care. De multe ori se descurca mai bine decat
algoritmul FIFO si LRU.
Bibliografie
[1] Andrew Tanenbaum-“Modern Operating Systems ed 3”
[2] Radu Radescu –“Arhitectura Sistemelor de Calcul”
Partea a-II-a
Algoritmi de gestiune ai memoriei
Hurmuzache Ciprian-Constantin, grupa 431A
1.Introducere
Memoria unui calculator reprezinta in esenta acea parte a calculatorului in care
sunt stocate programele si datele.Din memorie,procesorul preia datele necesare
pentru prelucrare si tot aici poate depune si rezultatele.unitatea memoriei este o
cifra binara care poate lua doar 2 valori : 0 sau 1.
In mod ideal se doreste o memorie rapida,mare si dimensiuni reduse.Foarte
multe sisteme de calcul in zilele noastre folosesc o structura pe niveluri a
memoriei,acest lucru avand ca scop de a beneficia de avantajele oferite de diverse
tipuri de memorie.
Putem enumera doua tipuri de gestiune a memoriei si anume : “cele care muta
procesele intre memorie si disc in timpul executiei acestora (interschimbare si
paginare) si cele care nu fac aceste operatii.” [1]
2.Paginarea
O descriere sumara a algoritmilor folositi in cadrul procesului de paginare ar
putea fi astfel:
Un prim algoritm este binecunoscutul algoritm optim Belady care
functioneaza dupa urmatorul principiu:”cand o pagina trebuie
copiata de pe disc in memorie sistemul de operare muta,din
memorie pe disc constinutul paginii care va fi folosita in proces cel
mai tarziu,facundu-i astfel loc noii pagini.Acest algoritm este unul
teoretic,deoarece nu se poate stii exact cat va dura pana cand va fi
folosita o pagina din memorie”[2].
Un alt algoritm este NRU pentru care “sistemul de operare distinge
4 clase de pagini:clasa 0,1,2,3.Pagina din memorie apartinand clasei
nevide cu valoare cea mai mica este eliminata din memorie.”[2]
Un alt algoritm este FIFO care functioneaza dupa urmatorul
principiu:”se elimina prima pagina care a fost incarcata prima in
memoria fizica,indiferent de momentul in care aceasta pagina a fost
referita ultima data.Se creaza practic un fenomen de
imbatranire,cand va trebui mutata o pagina se va muta pagina cu
contorul cel mai mare”[2].
Un alt algoritm este NFU care “genereaza mai putine semnale de tip
eroare de pagina-page fault) decat algoritmul LRU.Acest algoritm
foloseste un counter de sine statator pentru fiecare pagina.La fiecare
interval de ceas toate paginile referite vor avea acel contor
incrementat cu 1.Pagina cu valoare cea mai mica a contorului(care
nu a fost referita)va fi inlocuita”.[2]
Un alt algoritm este algoritmul Aging (A) care functioneaza dupa
urmatorul principiu:”Fata de NFU acest algoritm tine cont si de
factorul interval de timp.In loc sa incrementeze contoarele paginilor
apelate(referite),nefacand nicio discriminare din punct de vedere al
intervalului de timp in care numarul de apeluri a contribuit la
contorul de frecventa,contorul de apel al unei pagini este intai
deplasat spre dreapta(impartit la 2 ),intainte ca bitul de referinta sa
fie adaugat in partea stanga a numarului binar.”[2]
Acesti tipi de algoritmi descrisi mai sus intervin in momentul in care sistemul
de operare primeste semnalul de page fault ,acest semnal reclama faptul ca o
pagina solicitata nu se gaseste in memoria instalata ,ci pe disc.Rolul acestor
algoritmi este de a hotara ce pagina din memoria fizica va fi mutata pentru a face
loc noii pagini.
3.Segmentarea
Alte sisteme de calcul pot folosi si segmentarea.Acest concept se bazeaza pe
utilizarea de segmente,acestea reprezentand o secventa liniara de adrese,de la
adresa 0 la o valoare maxima.Aceste segmente pot avea lungimi diferite,si in
sine,conceptul de segmentare este accesibil utilizatorului.
In cadrul folosirii segmentarii se pot folosii urmatorii algoritmi de inlocuire a
segmentelor:
Compactarea,”ce are ca scop colectarea tutoror gaurilor intr-o gaura
mare si mutarea acesteia la sfarsitul memoriei.Aceste gauri apar atunci
cand se scot anumite segmente si se introduc altele de dimensiuni mai
mici decat cele scoase.”[2]
First Fit care “parcurge circular lista de gauri si alege prima gaura
suficient de mare pentru a pastra segmentul”[2]
Best fit “alege cea mai mica gaura in care va incapea segmentul
dorit.Ideea este de a gasi potriviri intre segmente si gauri evitandu-se
astfel acoperirea unei gauri mari cu un segment de dimensiune foarte
mica”[2]
De asemenea,un bloc important in gestionarea memoriei este blocul MMU
(memory management unit).Acest bloc pus in corelatie cu conceptul de memorie
virtuala si este folosit astfel : acest bloc incarca programul necesar pentru executie
folosind memoria virtuala care reprezinta o extindere a spatiului de adresare peste
memoria secundara.
Un alt concept important legat de managementul memoriei virtuale este si
tabelul de colectare a deseurilor(Garbage Collector).Acesta este o forma automata
de management a memoriei si este folosit pentru a colecta memoria ocupata de
obiecte care nu mai sunt necesare intr-un anumit program .In continuare ne vom
referi la acest concept prin prescurtarea GC.”Ideea folosita de GC este impartirea
memoriei in doua parti.Prima parte se ocupa de alocari iar partea a doua este
rezervata.
Un prim algoritm este algoritmul Mark-and-Sweep care consta din doua faze:
Faza Mark-porneste de la radacina si parcurge toate obiectele referite
de niste pointeri.Tot in aceasta faza se marcheaza toate obiectele
traversate.
Faza Sweep-se verifica toate obiectele din memoria heap si se iau
obiectele care nu au fost marcate.
Algoritmul poate fi exprimat astfel :
for (fiecare variabila radacina a)
mark(r);
sweep();
Se pot folosi urmatoarele metode recursive :
void mark (Obiect p) {
if (!p.marcat)
p.marcat=true;
for(fiecare obiect q referit de p )
mark(q);
}
Se observa ca aceasta functie parcurge toate obiectele din memoria heap
cu scopul de a localiza obiecte nemarcate.
Functia sweep poate fi prezentat in rumatorul mod
void sweep () {
for (fiecare obiect P din heap)
if (p.marcat)
p.marcat = false
else
elibereaza heap(p)
}
Marele dezavantaj al acestui algoritm este faptulca executia
programului este suspendata cat timp colectorul de gunoi (GC)
functioneaza. “[3][4].
Se poate spune faptul ca acest concept de garbage collector este folosit de
toate mediile de programare pentru a scapa de obiectele care nu mai sunt folosite
in programe.
Un alt algoritm este algoritmul trenului.Pentru a explica acest algoritm ma
voi referi la articolul scris de Thomas Wurthinger-“ Incremental Garbage
Collection: The Train Algorithm ” pe care il vom nota cu numarul de ordine [5]
in cadrul bibliografiei.
“In cardul acestui algoritm trebuie indepliniti doi pasi :
Detectie: colectorul trebuie sa fac distinctia intr-un fel intre
obiectele care sunt active(se folosesc in programe) si
obiectele de care trebuie sa ne dispensam.
Eliberare memorie : sunt doua posibilitati de a sterge
“gunoiul” :
Salvarea obiectelor care sunt active prin copierea lor
intr-o zona de siguranta si apoi eliberarea zonei plina
cu “obiecte gunoi”.
Eliberare spatiu pentru fiecare obiect nemarcat ca fiind
inca activ.
Avem mai multe obiecte in memorie : A,B,C,D,E,F . Exista doi pointeri
catre obiectele A si G care sunt privite ca fiind marcate,dar de asemenea si
obiectul B este marcat deoarece la acesta se poate ajunge prin intermediul lui
A.Se observa ca obiectele C,D,E si F sunt tratate ca “obiecte-gunoi”.
Obiectele marcate vor fi apoi copiate (nu mutate !) intr-o zona de memorie
separata .
Principala idee a acestui algoritm este de a impartii memoria in blocuri mici
si apoi sa puna in aplicare strategia de marcare si de stergere a gunoiului
separat pentru fiecare din aceste blocuri mici.”[5]
In cazul algoritmului Mark-and-Sweep executia programului trebuia
intrerupta pentru a realiza “colectarea de gunoi”.In ceea ce priveste algoritmul
trenului programul este oprit pentru fiecare din aceste mici blocuri,iar
intreruperile sunt mai mici decat in cazul primului algoritm,bineinteles
depinzand si de dimensiunea acestor mici blocuri de memorie.
“O problema care apare este faptul ca unele obiecte de dimensiuni mari nu
pot incapea in aceste mici blocuri de memorie.Acest algoritm poate rezolva si
aceasta problema.
Putem privi spatiul obiectelor (the object space) ca fiind o gara de
trenuri.Blocurile mici de memorie sunt numite acum “masini” si sunt grupate
pentru a forma “trenuri” care pot avea un numar mare de masini.Pentru fiecare
masina vom avea un set de referinte(care ne zic de fapt care obiecte sunt
marcate si care nu-in figura 2 ceste seturi sunt {R1,E},{R2}).
Prima figura poate fi acum reprezentata astfel
Figura 2
In setul de referinte din masina 1.1 este trecut si E( {R1,E} ) deoarece E
este o referinta catre obiectul C ,dar nu este trecut ca referinta la primul
tren.
Deoarece algoritmul proceseaza prima data masina cu numar de ordine cel
mai mic ,doar referintele din masini cu numar de ordine mai maine trebuie sa fie
luate in considerare in seturi,de exemplu aici si C este o referinta catre D dar fiin
intr-o masina mai mica (cu nr de ordin mai mic) nu este trecut in set.
Atunci cand colectorul de gunoi (GC) realizeaza trecerea peste prima
masina ,obiectul A este salvat si copiat intr-un nou tren deoarece este referit de
un pointer radacina (R1).B este referit si el de catre A si de aceea este mutat si B
in acelasi tren ca si A.In ceea ce priveste obiectul C , acesta este referit de un alt
obiect din acelasi tren si este deci mutat la sfarsitul trenului. O figura care
ilustreaza aceasta este urmatoarea
Se observa ca astfel prima masina ramane libera si poate fi stearsa.se
observa de asemenea ca si seturile de referinte s-au updatat,si acum
primul tren nu mai este referit deloc si deci la urmatoarea iteratie poate fi
sters.O figura care ilustreaza totul este urmatoarea in care se observa ca
primul tren poate fi sters.
Figura 3
Acest algoritm poate fi descris in urmatorii pasi :
1) selecteaza trenul cu numar de ordin cel mai mic
2)Sterge tot trenul daca nu exista un set pentru acel tren si apoi
iesire,altfel mergi la pasul 3
3)Selecteaza masina cu numar de ordin cel mai mic din cadrul
trenului.
4)Pentru fiecare element al unei masini
a)Daca obiectul n-a fost procesat mai devreme salveaza prin
copierea intr-un nou tren daca este referit de un pointer radacina.
b)Muta toate obiectele din aceeasi masina care pot fi folosite
mai departe din obiectul salvat,in acelasi tren
In acest pas este necesar updatarea seturilor de referinte ( ca in
figura 3). Daca un obiect este referit de mai multi pointeri
radacina care descriu trenurile atunci obietul poate fi copiat
oriunde.
5)Sterge masina si iesi.
In cazul in care se foloseste o structura ciclica trebuie avut grija la faptul
ca acest algoritm nu trebuie sa functioneze in bucla infinita.
In concluzie, algoritmul trenului este folosit des in combinatii cu alti
algoritmi de colectare.Acest algoritm se foloseste atunci cand unsistem trebuie
sa reactioneze foarte rapid.Algoritmul trenului poate fi folosit de asemenea si in
sistemele distribuite. “[5]
In concluzie astfel de algoritmi de gestiune ai memoriei, ca si cei
prezentati la inceputul lucrarii , sunt foarte importanti deoarece sistemele de
calcul ,si mai ales o componenta importanta a acestuia cum este memoria,
trebuie sa fie guvernate de reguli(strategii) pentru a nu avea haos .Consider
faptul ca acesti algoritmi sunt foarte importanti prin modul lor de gestiune al
memoriei si de asemenea sunt indispensabili atunci cand se doreste viteza si
precizie.
Bibliografie
[1] Andrew Tanenbaum-“Modern Operating Systems ed 3”
[2] Radu Radescu –“Arhitectura Sistemelor de Calcul”
[3] Richard Jones – “Garbage Collection :algorithms for automatic dynamic
memory management”
[4] Cursuri de la “Technion – Israel Institute of Technology”
[5] Thomas Wurthinger-“ Incremental Garbage Collection: The Train
Algorithm ”
Partea a-III-a
Gestiunea memoriei in Linux Pruiu Ana-Maria, grupa 431A
1.1. Introducere:
Memoria interna (primara) este o componenta importanta a
calculatoarelor si are rolul de a pastra datele si programele aferente
procesului in curs de executie. Memoria executabila a unui calculator este
alcatuita din memoria interna, registrii CPU si memoria cache. Atunci cand
se doreste sa se prelucreze date acestea sunt incarcate de unitatea aritmetica
si logica din memoria interna in registrii CPU si cand prelucrarea a luat
sfarsit rezultatele sunt stocate in memoria interna. De pe alta parte memoria
externa (secudara) este folosita pentru a stoca datele un timp indelungat.
Memoria este un vector de octeti, iar fiecare dintre acesti vectori are o
adresa proprie. In memoria principala sunt stocate instructiunile (codul) si
datele. Trebuie sa avem in vedere ca memoria este una dintre cele mai
importante resurse ale unui sistem de calcul si trebuie sa fie gestionata cat
mai bine. Din aceasta cauza sistemul de gestiune al memoriei este una dintre
cele mai importante parti ale unui sistem de operare.
Termenul de gestiune a memoriei se refera la mecanismele
implementate de un sistem de operare pentru a asigura aplicatiilor resursele
de memorie de care au nevoie. Aceste resurse pot fi resurse de memorie
virtuala (utilizand un hard disk sau orice mediu de stocare non-RAM
aditional), memorie protejata (accesul exclusiv al unui program la o regiune
de memorie) si memorie partajata (mai multe procese au acces la aceeasi
zona de memorie).
Serviciile de gestiune a memoriei in Linux sunt construite pe o baza
de programare care include un dispozitiv periferic numit MMU (Memory
Management Unit). MMU traduce adrese fizice de memorie in adrese liniare
care sunt folosite de sistemul de operare, iar acesta solicita o intrerupere de
pagina atunci cand CPU (unitatea centrala de prelucrare) incearca sa
acceseze o zona de memorie la care nu are dreptul sa aiba ajunga.
Unitatea de memorie de baza a Linux-ului este pagina care este o zona
de memorie continua fara suprapuneri. Toata memoria fizica este
oraganizata in pagini , iar marimea unei pagini depinde de arhitectura
procesorului. In mod normal o pagina utilizata de Linux are marimea de
4096 de octeti.
Sistemul de gestiune al memoriei asigura:
Alocarea de spatiu de memorie interna proceselor
Protectie: fiecare proces din sistem are propriul spatiu de
adrese virtual. Aceste spatii de adresa sunt complet separate
unul de celelalt si in timp ce un proces ruleaza o aplicatie
acesta nu trebuie sa influenteze celelalte procese. De asemenea
putem avea zone de memorie protejate, in care nu se poate
scrie.
Minimizarea timpului de acces la locatiile de memorie
Memorie viruala partajata: desi memoria virtuala permite
proceselor sa aiba spatii de adrese separate, totusi pot exista si
situatii cand procesele trebuie sa partajeze memoria. Un
exemplu in care se executa acelasi cod intre mai multe procese
sunt bibliotecile dinamce.
1.2 Gestiunea memoriei fizice in Linux:
Memoria fizica este divizata in unitati discrete numite pagini.
Marimea unei pagini depinde de arhitectura procesorului, dar sistemele
actuale utilizeaza de obicei pagini de 4096 de octeti. Constanta PAGE_SIZE
(definita in <asm/page.h> ) ofera dimensiunea paginii pentru orice
arhitectura.
Paginile sunt impartite in patru segmente:
User Code
User Data
Kernel Code
Kernel Data
In modul utilizator (user mode) se acceseaza doar User Code si User Data.
In modul Kernel se acceseaza de asemenea si User Data.
In Linux pot exista doua posibilitati:
In Kernel: poti aloca unul dintre cele mai mici obiecte kernel utilizand
alocatorul slab. Se poate aloca un bloc de memorie cu kmalloc, dar
acesta va aloca doar blocul apropiat cel mai mare pe care il are.
In modul utilizator (user mode): se poate aloca orice cantitate de
memorie utilizand functiile de management heap (heap-ul este o zona
de memorie dedicata alocarii dinamice a memoriei) implemetate in
bibliotecile C standard.
Fiecare pagina fizica are asociat un descriptor de pagina care contine: un
contor de utilizare al paginii, flag-uri, pozitia in swap sau in fisier,
buferele continute de pagina, pozitia in „page cahe”.
Zonele fizice de memorie sunt [1]:
Zona DMA: cu pagini de memorie de 0-16 MB, este folosita
pentru operatii directe cu memoria;
Zona Normal (LowMem): contine pagini de memorie intre 16MB
si 896 MB si este folosita de kernel pentru structurile de date
interne;
Zona HighMem: 896 MB – 4GB/64 GB, include toata memoria
folosita pentru alocarile de sistem;
Prezentarea celor trei zone de memorie [1]
Memoria fizica este impartita intre mai multe noduri, iar fiecare nod are
propriul procesor si are zone de memorie proprii: DMA, NORMAL,
HIGHMEM.
1.3 Alocarea si dealocarea paginilor de memorie :
Linux foloseste principiul de a incarca in memoria fizica doar
informatia necesara si cu care se lucreaza in momentul curent, pentru a lasa
loc in memorie oricarui alt program care are de executat o sarcina anume.
Gestiunea memorie in Linux este realizata de kernel impreuna cu CPU
(unitatea centrala de prelucrare). Acestea sunt responsabile de buna
desfasurare a lucrurilor si trebuie sa realizeze schimbul intre memoria fizica
si cea de pe disc si trebuie sa depuna in memoria fizica toate informatiile
(date, instructiuni, operanzi) de care programele care se executa au nevoie la
un moment dat.
In Linux alocarea si dealocarea de pagini de memorie se realizeaza
utilizand unul din algoritmii [3]:
Alocatorul slab
Algoritmul (alocatorul) buddy
Alocatorul de zona (de sector)
Schema generala a gestiunuii memoriei in Linux [3]
Se poate vedea in aceasta schema ca alocarea memoriei presupune
inlantuirea celor trei alocatori enumerati mai sus si paginile nou alocate sunt
adaugate la spatiul de adrese al memoriei.
1.3.1 Alocatorul de zona ( the zone allocator):
Dupa cum am vazut mai sus memoria este impartita in trei zone si
pentru a aloca /dealoca (elibera) pagini de momorie in/din fiecare zona de
memorie se foloseste alocatorul de zona. O caracteristica particulara a
acestui alocator este ca el lucreaza numai cu pagini de memorie. Memoria
este alocata/ dealocata de alocatorul de zona folosind algoritmul buddy
(algoritmul „prietenului”):
1.3.2 Algoritmul buddy (algoritmul „prietenului”):
Algoritmul buddy este un algoritm de alocare a memoriei destul de
simplu de implementat. Principalul scop al acestui algoritm este de a reduce
fragmentarea externa si in acelasi timp de a realiza operatiile de alocare si
dealocare a memorie cu o viteza cat mai mare, rezultand asftel un timp de
executie mai mic. Pentru a reduce fragmentarea externa paginile libere de
memorie sunt grupate in blocuri de dimensiune variabila, dar cu conditia ca
blocurile sa aiba o dimensiune putere a lui 2. Cu aceste blocuri de pagini se
alcatuiesc liste, iar o lista trebuie sa aiba numai blocuri de aceeasi
dimensiune ( de exemplu o lista de blocuri de cate 2 pagini sau o lista de
blocuri de cate 4 pagini si asa mai departe). De exemplu daca are loc o
cerere de 4 pagini continue se verifica daca exista un bloc liber de 4 pagini si
in cazul in care este gasit blocul se aloca imediat. Sau daca un bloc de 8
pagini este disponibil se imparte acest bloc in doua blocuri de cate 4 pagini
si un bloc se aloca unitatii apelante, iar celalalt este plasat in lista blocurilor
de 4 pagini.
In cazul dealocarii daca exista blocuri adiacente in memoria fizica,
fiecare bloc avand dimensiunea N, iar primul bloc este aliniat la 2N atunci
cele 2 blocuri se unesc intr-un bloc de dimensiune 2N. Se incearca sa se
uneasca cat mai multe blocuri pentru a reduce fragmentarea externa.
In figura urmatoare [3] sunt prezentate listele cu blocuri de pagini
utilizate in algoritmul buddy:
1.3.3 Alocatorul slab:
Acest alocator are zone de memorie diferite pentru obiecte diferite,
zone pe care le putem asemana cu niste placi de mozaic si de aici ii vine si
numele de „slab” care in limba engleza inseamna „lespede”.
Alocatorul slab incearca sa rezolve urmatoarele probleme:
Incearca sa nu interfere prea mult cu alte zone de memorie atunci cand
cauta o zona noua de memorie, se spune ca are o „urma” mica (small
footprint);
Incearca sa nu puna doua obiecte diferite in acceasi linie din cache-ul
de date.
Incearca sa reduca numarul de operatii de initializare asupra noilor
obiecte alocate.
Exista multe situatii cand trebuie sa alocam obiecte care au o
dimensiune mai mica decat cea a unei pagini si trebuie sa gasim solutii sa
alocam parti mai mici de memorie. De aceea a fost introdus alocatorul slab
care incearca sa rezolve aceste situatii. [3]
In cazul alocatorului slab zonele de memorie sunt vazute ca niste
obiecte si fiecare obiect are un constructor si un destructor propriu.
Obiectele dealocate sunt pastrate intr-un cache astfel ca la urmatoarea
folosire a lor nu mai trebuie reinitializate si nu mai este nevoie sa se apeleze
algoritmul buddy. [3]
Alocatorul slab este format din :
Cache: in cache se stocheaza obiectele care au fost recent utilizate,
obiecte care trebuie sa fie de acelasi tip. Cache-ul reprezinta unitatea
cea mai mare de stocare in cadrul alocatorului slab.
Slab: este un container pentru obiecte si este alcatuit din una sau mai
multe pagini. Mai multe slab-uri la un loc se stocheaza intr-un cache.
Obiect: obiectul este unitatea cea mai mica care este utilizata in cazul
alocatorului slab. Mai multe obiecte puse la un loc alcatuiesc un slab.
[2]
Toate aceste unitati pot fi reprezentate grafic astfel [1]:
Bibliografie:
[1] “Understanding the Linux Kernel, 3rd Edition” de Daniel P.
Bovet, Marco Cesati, editura O’Reilly, noiembrie 2005
[2] “Memory Management in Linux - Desktop Companion to the
Linux Source Code”, de Abhishek Nayani, Mel Gorman & Rodrigo
S. de Castro, 25 mai 2002
[3] http://www.codeproject.com/Articles/131862/Special-Features-of-
Linux-Memory-Management-Mechan
[4] “Outline of the Linux Memory Management System” de Joe
Knapka
Partea a-IV-a
Apeluri de sistem pentru gestiunea memoriei:
comparatie Linux si Windows
Manole Ion-Laurentiu, grupa 432A
1.Introducere
Fiecare sistem de calcul detine o memorie RAM (random acces memory) in
care sunt incarcate toate programele si datele cu care lucreaza aceste programe,
date obtinute de la dispozitivele periferice etc. Aceasta memorie insa trebuie
impartita intre toate programele care ruleaza si in acelasi timp nu trebuie sa se
incomodeze intre ele. Alocatorul de memorie este cel care se ocupa cu gestiunea
memoriei. Alocarea memoriei se face ierarhic sistemul de operare fiind cel care are
la dispozitie toata cantitatea de memorie RAM. Acesta atribuie fiecarui program
anumite portiuni din memorie, programele la randul lor gestionand portiunea de
memorie primita.
Alocarea memoriei este de doua tipuri: statica – atunci cand este realizata de
compilator si dinamica - atunci cand alocarea se face in timpul executiei. In timpul
executiei variabilele sunt alocate pe stiva in cazul alocarii statice si si in heap in
cazul alocarii dinamice. Heap-ul este o zona de memorie in care aceasta e alocata
sau eliberata intr-un mod arbitrar. In cazul in care se cunoaste dimensiunea
variabilelor de la inceput este de preferat sa se foloseasca alocarea statica pentru a
preveni erori care apar frecvent in cazul alocarii dinamice.
2.Apeluri de sistem pentru gestiunea memoriei in Linux
brk1(void *addr) - seteaza sfarsitul segmentului de date specificat de campul
addr, cand acea valoare este rezonabila, sistemul are suficienta memorie, si
programul nu si-a atins limita maxima de memorie care ii este alocata.
1 http://linux.die.net/man/2/brk
sbrk2() –incrementeaza spatiul de date al programului incrementand
numarul de bytes. Prin incrementarea cu 0 se poate afla locatia curenta a
programului.
getrlimit() si setrlimit()3 - sunt apeluri de sistem care preiau respectiv
seteaza limita de memorie pe care o poate avea programul respectiv.
mnap4( void *addr) – creaza o noua mapare spatiu virtual la adreselor al
procesului apelant. Adresa de inceput pentru mapare este specificata de catre addr.
Se poate folosi inca un parametru pentru a specifica lungimea.
mremap5() – extinde sau comprima maparea in memorie si poate chiar sa
mute zona de memorie in acelasi timp. In Linux memoria este impartita in pagini
iar mremap() foloseste schema tabelei de pagini Linux. Acest apel de sistem
schimbamaparea dintre adreasele virtuale si paginile de memorie.
munmap6() – este un apel de sistem care sterge maparea pentru un interval
de adrese si cauzeaza referinte invalide de memorie atunci cand se face referire la o
adresa din intervalul precizat. Aceasta regiune este automat demapata cand
procesul este terminat. Pe de alta parte inchiderea fisierului descriptor regiunea nu
este demapata automat.
msync7() – rescrie pe disk schimbarile facute in copia din interiorul
nucleului a fisierului care a fost mapat folosind apelul mmap(). Fara acest apel de
sistea nu exista nici o garantie ca schimbarile sunt scrise inapoi inainte de apelul
munmap(). Cu alte cuvine daca se apeleaza msync() cu parametrii addr si length,
acea parte a memoriei care incepe cu addr si are lungimea length este updatata.
Functiile de biblioteca folosite de catre utilizatori cum ar fi calloc() ,
malloc(), realloc() folosesc apeluri de sistem ca cele de mai sus pentru a-si realiza
functiile.
2 http://linux.die.net/man/2/sbrk 3 http://www.kernel.org/doc/man-pages/online/pages/man2/setrlimit.2 4 http://linux.die.net/man/2/mmap 5 http://linux.die.net/man/2/mremap 6 http://linux.die.net/man/2/munmap 7 http://linux.die.net/man/2/msync
Un exemplu este functia realloc() care foloseste ca apel de sistem
mremaop(), sau functia malloc() care pentru extinderea heap-ului foloseste
apelurile brk() sau mnap().
3.Apeluri de sistem pentru gestiunea memoriei in Windows
In Windows exista doua alocatoare de memorie. Primul este unu a caror
nume de functii incep cu Virtual iar cu acesta efectueza diferite operatii la nivel de
pagina.
VirtualAlloc8 – este o functie care rezerva un interval din spatiul virtual de
adrese al procesorului. Memoria alocata de acasta functie este automat initializata
cu zero daca nu este specificat MEM_RESET. MEM_RESET specifica daca
spatiul de memorie precizat de parametrii functiei VirtualAlloc mai prezinta interes
sau nu. Daca se specifica MEM_RESET, VirtualAlloc ignora valoare lui flProtect
( care reprezinta nivelul de protectie al unui fisier – read-only, read-write sau
write-copy). Un parametru important al functiei VirtuallAlloc este
flAllocationType care daca are voloarea 0x2000 (MEM_RESERVE) rezerva
memorie in spatiul virtual de adrese al procesului fara sa aloce nici un spatiu fizic
de pe disc.
Maparea paginilor rezervate se realizeaza daca valoarea campului
flAllocationType este 0x1000 (MEM_COMMIT).
VirtualProtect9 – este o functie care permite schimbarea protectiei unei
regiuni de pagini din spatiul virtual de adrese a procesului apelant.
VirtualFree10
– este functia de eliberare a paginilor din spatiul virtual de
adrese al procesului apelant. Aceasta functie are ca parametrii lpAddress, dwSize,
dwFreeType. Daca dwFreeType are valoarea MEM_DECOMMIT elibereaza
regiunea specificata a paginilor rezervate. Apelarea functiei cu acest parametru
pentru o pagina care nu a fost alocata nu va face ca functia sa esueze in executie.
Atunci cand dwFreeType are valoarea MEM_RELEASE rezervarea se va sterge cu
totul.
8 http://msdn.microsoft.com/en-us/library/windows/desktop/aa366887(v=vs.85).aspx 9 http://msdn.microsoft.com/en-us/library/windows/desktop/aa366898(v=vs.85).aspx 10 http://msdn.microsoft.com/en-us/library/aa908947.aspx
Cel de-al doilea alocator din Windows este acela de heap. Functiile acestui
alocator au denumiri care incep cu Heap. Exista doua tipuri de heap: primul este
default heap si este caracteristic fiecarui proces in Windows iar cel de-al doilea
este dynamic heap. Un proces poate vrea cate heap-uri dinamice doreste prin
simpla creere si distrugere a lor in timpul executiei. Sistemul foloseste default
heap-ul pentru toate functiile globale si locale de management al memoriei, acesta
fiind folosit de exemplu si ca suport pentru funtia malloc din libraria C.
GetProcessHeap11
– este o functie obtine manipularea default heap-ului
procesului apelant.
HeapAlloc12
– aloca un bloc de memorie dintr-un heap. Memoria alocata nu
poate fi mutata. Parametrii functiei HeapAlloc sunt hHeap si dwFlags. Daca
valoarea din dwFlags este 0x00000004 (HEAP_GENERATE_EXCEPTIONS)
sistemul va genera o exceptie pentru a indica esecul functiei, de exemplu pentru ca
nu mai este memorie libera, in loc sa returneze NULL. O alta valoare importanta a
parametrului dwFlags este 0x00000008 (HEAP_ZERO_MEMORY) care
initializeaza cu zero memoria alocata. De retinut este faptul ca daca nu este folosita
aceasta valoarea a dwFlags memoria alocata nu va fi initializata.
HeapReAlloc13
– este o functie care realoca un bloc de memori dintr-un
heap. Aceasta functie permite blocurilor de memorie sa fie redimensionate si sa
schimbe proprietatile altor blocuri de memorie.
HeapFree14
– elibereaza blocurile de memorie alocate cu HeapAlloc si
HeapReAlloc.
HeapDestroy15
– este functia care distruge obiectul heap specificat. Aceasta
elibereaza toate paginile obiectului heap privat, si invalideaza mentiunile acelui
heap.
11 http://msdn.microsoft.com/en-us/library/windows/desktop/aa366569(v=vs.85).aspx 12 http://msdn.microsoft.com/en-us/library/windows/desktop/aa366597(v=vs.85).aspx 13 http://msdn.microsoft.com/en-us/library/windows/desktop/aa366704(v=vs.85).aspx 14 http://msdn.microsoft.com/en-us/library/windows/desktop/aa366701(v=vs.85).aspx 15 http://msdn.microsoft.com/en-us/library/windows/desktop/aa366700(v=vs.85).aspx
In Windows mai exista doua interfete de alocare numite Global si Local.
Aceste interfete sunt implementate doar pentru a pastra compatibilitatea cu
Windows pe 16 biti, iar intern sunt implementati prin intermediul heap.
Mai jos sunt prezentatea functiile pentru interfata Global si corespondentul pentru
interfata Local precum si cateva flagurile celor doua interfete.
Functii ale momeriei globale Functii ale memoriei locale
GlobalAlloc LocalAlloc
GlobalDiscard LocalDiscard
GlobalFlags LocalFlags
GlobalFree LocalFree
GlobalHandle LocalHandle
GlobalLock LocalLock
GlobalReAlloc LocalReAlloc
GlobalSize LocalSize
GlobalUnlock LocalUnlock
Global memory flag Local memory flag Semnificatie
GMEM_FIXED LMEM_FIXED Aloca memorie fixa
GMEM_MOVEABLE LMEM_MOVEABLE Aloca memorie care poate
fi mutata
GMEM_DISCARDABLE LMEM_DISCARDABLE Aloca memorie care poate
fi mutata si indepartata
GMEM_ZEROINIT LMEM_ZEROINIT Initializeaza memoria cu
zero in timpul alocarii
GMEM_NODISCARD LMEM_NODISCARD Nu indeparta alta memorie
pentru a indeplini novoile
aceste cereres in schimb nu
indeplini aceasta cerere
GMEM_NOCOMPACT LMEM_NOCOMPACT Nu indeparta sau muta alta
memorie pentru a indeplini
novoile aceste cereres in
schimb nu indeplini aceasta
cerere
GMEM_SHARE,
GMEM_DDESHARE
N/A Aloca memorie globala care
e mai efcients in utilizarea
cu DDE.
Bibliografie:
www.cs.cmu.edu/~mihaib/articole/mem/mem-html.html
Operating System Concepts - 7th Edition Addison Wesley