Scrigroup - Documente si articole

Username / Parola inexistente      

Home Documente Upload Resurse Alte limbi doc  

CATEGORII DOCUMENTE





AccessAdobe photoshopAlgoritmiAutocadBaze de dateC
C sharpCalculatoareCorel drawDot netExcelFox pro
FrontpageHardwareHtmlInternetJavaLinux
MatlabMs dosPascalPhpPower pointRetele calculatoare
SqlTutorialsWebdesignWindowsWordXml

Gestionarea memoriei

calculatoare

+ Font mai mare | - Font mai mic







DOCUMENTE SIMILARE

Trimite pe Messenger
Grila - INFORMATICA DE GESTIUNE
Retele cu implicatii la proiectarea sistemelor
SISTEMUL INFORMATIONAL
Concepte cu care lucreaza algoritmii
Avantajele sistemului informatic
Terminarea proceselor
Protocolul UDP
PROIECT DE LECTIE INFORMATICA Clasa: X-a - Tipul 'String' si tipul 'Inregistrare' – aplicatii
Copii de siguranta si evenimente neprevazute
Alte servicii INTERNET

Gestionarea memoriei



1.   Suprapuneri ( Overlays )

In cazul in care este necesar ca intreg spatiul logic de adrese al procesului sa se afle

in memoria fizica inainte ca acesta sa fie lansat in executie, dimensiunea programului va fi limitata  de  dimensiunea  memoriei  fizice.  Pentru  a  permite  unui  program  sa  aiba  o dimensiune mai mare decat a memoriei ce ii poate fi alocata se foloseste uneori o tehnica numita  suprapunere  (  overlay  ).  Ideea  de  baza  este  de  a  pastra  in  memorie  numai  acele instructiuni  si  date  de  care  este  nevoie  permanent.  Celelalte  grupuri  de  instructiuni  sunt incarcate si evacuate in/din o zona de memorie folosita in comun, numai atunci cand aceasta operatie este necesara.

Ca  exemplu,  se  considera  cazul  unui  asamblor  in  doua  treceri:  pe  durata  primei treceri  se  construieste  o  tabela  de  simboluri,  iar  pe  durata  celei  de-a  doua  treceri  se genereaza  cod  in  limbaj  masina.  Gruparea  instructiunilor  se  poate  realiza  astfel:  codul


asociat trecerii 1, codul asociat trecerii 2, tabela de simboluri si rutinele utilizate in comun

de catre ambele treceri.

Trecerea 1                               8 k Trecerea 2                                                10 k Tabela de simboluri                                  14 k Rutine comune                                                5 k

Incarcarea tuturor acestor elemente necesita 37 k de memorie si daca, de exemplu, nu avem la dispozitie decat  32k, programul nu va putea fi lansat in acelasi timp. Poate fi deci definit un driver de suprapuneri ( 2k ) care sa gestioneze memoria cu referire la existenta a doua  module  de  program,  incarcate  succesiv,  in  acelasi  spatiu:  (  A  )  tabela  de  simboluri, rutinele comune, trecerea 1 si ( B ) tabela de simboluri, rutinele comune si trecerea 2. In momentul incheierii executiei trecerii 1 ( modulul A ) are loc un salt la adresa drive-ului care  determina  aducerea  in  memorie  a  modulului  B,  suprainscriind  A  si  apoi  transfera controlul  trecerii  2.  Modulul  A  ocupa  29  K,  in  timp  ce  B  are  nevoie  de  31  K,  deoarece spatiul  de  memorie  disponibil  este  de  32K,  executia  asamblorului  este  posibila.  Codurile asociate lui A si B pot fi pastrate pe disc magnetic sub forma de imagine memorie absoluta

si sunt citite de catre driverul de suprapuneri ori de cate ori este necesar ( executia va fi mai lenta datorita operatiilor de I/O suplimentare ).

Pentru realizarea suprapunerilor nu este necesar ca sistemele de operare sa contina functii  speciale,  implementarea  acestor  metode  de  gestiune  a  memoriei,  care  presupune folosirea unor algoritmi specializati pentru relocare si inlantuire, poate fi realizata complet

de catre utilizator cu ajutorul fisierelor. Cu toate acestea, proiectarea si programarea corecta

a structurii modulelor ce vor fi suprapuse in memorie ramane o sarcina destul de complexa, care  prespune  o  buna  cunoastere  a  intregului  program.  Fiind  vorba  de  programe  de dimensiuni foarte mari ( programele de dimensiuni reduse nu necesita suprapuneri ), acest deziderat este destul de greu de realizat, motiv pentru care se prefera ca in locul metodei ce foloseste   suprapuneri   sa   se   utilizeze   tehnici   automate   care   sa   permita   executarea

programelor mari in cadrul unui spatiu de memorie limitat.

2 Incarcarea dinamica

Modulele de program ( rutinele, de exemplu ) sunt stocate in format relocatabil pe disc  magnetic,  programul  principal  se  reincarca  in  memorie  si  se  lanseaza  in  executie.  In momentul  in  care  se  doreste  apelarea  unei  rutine,  se  verifica  mai  intai  (  cu  ajutorul  unor tabele de evidenta ) daca ea se afla in memorie. Daca nu, se apeleaza un program specializat

de incarcare cu relocatare si inlantuire care aduce in memorie rutina apelata, reactualizeza

tabelele in scopul inregistrarii acestei modificari si apoi cedeaza controlul noii rutine.

Avantajul  incarcarii  dinamice  este  ca  rutinele  sunt  aduse  in  memorie  numai  in momentul  in  care  sunt  apelate,  rutinele  neutilizate  nu  vor  fi  incarcate  niciodata.  Aceasta metoda poate fi folosita, de exemplu, in cazul programelor de dimensiune mare care contin module voluminoase destinate tratarii unor situatii de eroare mai putin frecvente. Ca si in cazul  metodei  suprapunerilor,  incarcarea  dinamica  nu  impune  existenta  unor  facilitati suplimentare       in    cadrul     sistemelor      de    operare,     intreaga      raspundere      a    corectitudinii


functionarii revenind utilizatorului.

3.   Gestionarea memoriei de catre sistemul de operare

Pentru un sistem dat, alegerea unei anumite metode de gestiune a memoriei depinde

de  mai  multi  factori,  dintre  care  proiectarea  hardware  a  sistemului  ocupa  un  loc  foarte important.  Algoritmii  de  gestionare  a  memoriei  descrisi  in  cele  ce  urmeaza  indeplinesc  o

aceeasi cerinta de baza: pentru ca un proces sa poata fi lansat in executie trebuie ca mai intai

sa  fie  incarcat  in  memorie  intregul  sau  spatiu  de  adresare  logica,  restrictie  care  limiteaza

dimensiunea unui progaram la dimensiunea memoriei fizice.

3.1.                  Monitor rezident

Una dintre primele metode de gestionare a memoriei folosite in cadrul sistemelor de operare timpurii este cea care grupeaza memoria in doua zone: una pentru utilizator si una pentru monitorul rezident al sistemului de operare. Monitorul rezident poate fi plasat in zona inferioara a memoriei, sau in cea superioara ( cu referire la valoarea adreselor ). Principalul factor care influenteaza aceasta decizie este in general localizarea vectorului intreruperilor; deoarece acesta se afla, de cele mai multe ori, in zona inferioara a memoriei, se prefera ca si monitorul rezident sa fie plasat in aceeasi zona.

Daca  in  sistem  exista  un  singur  program  utilizator,  protejarea  codului  si  a  datelor monitorului  fata  de  modificarile  accidentale  sau  intentionate  pe  care  acesta  i  le-ar  putea produce  se  poate  realiza  prin  hardware,  implementandu-se  cu  ajutorul  unei  scheme  cu registru  limita  inferioara;  folosirea  unui  registru  limita  superioara  nu  este  necesara, deoarece in memorie nu exista decat un singur utilizator.

In situatia in care se doreste modificarea dinamica a dimensiunii monitorului ( si deci

a locatiei limita inferioara ) in timpul executiei unui program, se pot folosi doua alternative ale schemei de baza.

Prima   metoda   consta   in   incarcarea   programului   utilizator   incepand   din   zona superioara a memoriei, spre zona limita inferioara ( in loc de a se realiza incarcarea in sens invers ( Figura 13 ) ); principalul avantaj este ca spatiul neutilizat va ramane in zona din mijloc a memoriei, unde, daca este necesar, atat programul utilizator, cat si monitorul se vor putea extinde.

A doua metoda, mai generala, consta in amanarea operatiei de asociere a adreselor pana in momentul executiei; schema de relocare dinamica necesita un suport hardware usor diferit ( Figura 14 ). In acest caz, registrul limita inferioara se numeste registru de relocare sau de baza; modificarea adresei de inceput a memoriei ce gazduieste programul utilizator

se  poate  realiza  in  orice  moment  si  consta  in  schimbarea  valorii  registrului  de  baza  si

deplasarea intregii zone utilizator la locatiile corecte in raport cu noua valoare. Programul utilizator lucreaza cu adrese logice cuprinse in domeniul 0” maxim”, conversia acestora

in  adrese  fizice  din  domeniul  R  +  0”   „R  +  maxim”,  in  care  R  este  valoarea  limita

inferioara, fiind realizata de catre hardware-ul folosit pentru asocierea tabelata a adreselor

de memorie.


 

Figura 13                                                                  Figura 14

3.2.                  Multiprogramare cu partitii fixate

Intr-un  sistem  cu  multiprogramare,  este  necesar  ca  memoria  sa  poata  fi  alocata  in mod  eficient  numeroaselor  programe  aflate  in  rezerva  de  job-uri.  Gestionarea  cu  partitii fixate  fragmenteaza  memoria  intr-un  numar  de  zone  de  dimensiune  fixata,  fiecare  dintre acestea putand fi alocata cate unui program selectat dintre cele ce urmeaza a fi executate ( numarul  de  partitii  limiteaza  deci  gradul  de  multiprogramare).  In  momentul  incheierii executiei, partitia devine disponibila pentru incarcarea altui program.

Figura 15

Deoarece in memorie pot exista in acelasi timp mai multe programe, trebuie prevazute si mecanisme de protectie. Pentru fiecare partitie se pot folosi cite doi registri limita ( va fi necesara  o  relocare  statica  in  momentul  asamblarii  sau  al  incarcarii)  sau  cate  o  pereche registru baza registru limita ( solutia permite alocarea dinamica in momentul executiei ); registrul  de baza  contine  valoarea  celei  mai  mici  adrese  fizice,  in  timp  ce  registrul  limita contine valoarea domeniului de adrese logice ( Figura 15 ).


Metode de planificare

Selectarea de catre planificator a unor job-uri aflate in rezerva pentru a fi incarcate in memorie  se  face  in  functie  de  necesarul  de  memorie  declarat  de  acestea  si  de  partitiile disponibile. In continuare vor fi prezentate cateva dintre strategiile folosite in acest scop.

O  prima  metoda  este  clasificarea  tuturor  job-urilor  in  functie  de  necesitatile  de memorie,  in  momentul  aparitiei  lor  in  sistem;  se  creaza  astfel  cate  o  „coada”  de  job-uri pentru  fiecare  partitie.  Clasificarea  poate  fi  realizata  pe  baza  informatiilor  referitoare  la necesarul maxim de memorie furnizate de catre utilizator sau determinate automat de catre sistem. Fiecare „coada” are propria partitie de memorie si va fi planificata separat ( nu apare concurenta intre cozi” pentru accesarea memoriei). De exemplu, daca exista patru partitii

de memorie utilizator, de dimensiune 3K, 7K, 12K si 20K, vor exista patru cozi”, notate

C3, C7, C12 si C20 in care ar putea fi adaugat cate un job de dimensiune 2K, 7K, 10K si respectiv 19K.

O  alta  metoda  este  gruparea  tuturor  job-urilor  intr-o  singura  „coada”  din  care planificatorul va selecta job-ul ce urmeaza a fi incarcat si apoi, pentru a realiza alocarea, va astepta pana in momentul in care devine disponibila o partitie de memorie cu dimensiune corespunzatoare.  Daca  planificatorul  foloseste  o  strategie  de  tip  FCFS  (  First  Come  First Served ), este posibil ca un job sa fie nevoit sa astepte   in „coada” chiar daca partitia ce-i este  necesara  este  disponibila,  numai  pentru  ca  inaintea  lui  se  afla  job-uri  cu  necesar  de memorie ce depaseste dimensiunile acesteia.

Ca  o  consecinta  fireasca  a  acestei  operatii  a  aparut  o  varianta  de  palnificare  ce  nu permite partitiilor de memorie sa stea nefolosite. Atunci cand o partitie devine disponibila se parcurge coada” pana la ultimul job care ar putea fi cuprins in ea si se realizeaza alocarea, chiar daca in „coada” exista alte joburi mai prioritare, dar cu dimensiune mult mai mare. Acordarea permisiunii de alocare a unui job mai mic ( dar mai putin prioritar ) nu impiedica evolutia  acestor  job-uri  care,  oricum,  nu  puteau  folosi  partitia  disponibila  din  cauza dimensiunii necorespunzatoare.

Metoda  prezentata  poate  sa  apara  si  in  alte  variante,  in  functie  de  strategiile  de alocare a memoriei folosite:

   Best Fit Only ( cea mai buna potrivire ); se aloca partitia cu dimensiunea cea mai apropiata  de  necesarul  de  memorie  al  job-ului;  daca  nu  este  disponibila,  job-ul asteapta eliberarea ei;

   Best Available Fit ( cea mai buna potrivire disponibila ); job-ului i se aloca prima partitie disponibila cu dimensiune suficient de mare ca sa il poata cuprinde.

Interschimbarea job-urilor ( Job Swapping )

Atunci cand in sistem exista mai multe job-uri cu dimensiune corespunzatoare unei aceleasi  partitii,  ele  pot  fi  interschimbate  (  introduse/evacuate  in/din  partitia  respectiva  ). Presupunand,  de  exemplu,  ca  se  lucreaza  cu  trei  partitii,  pentru  fiecare  fiind  folosit  un algoritm de planificare de tip Round-Robin, in momentul epuizarii unei cuante programul de gestionare a memoriei va evacua job-ul curent si va introduce un alt job in partitia respectiva



(  Figura  16  )  In  timpul  desfasurarii  acestor  operatii,  planificatorul  UC  poate  permite


executarea unui job din alta partitie. Pentru ca in memorie sa existe job-uri gata de executie

ori  de  cate  ori  actioneaza  planificatorul  UC,  este  de  dorit  ca  programul  de  gestionare  a memorie prin mecanismul descris anterior ( swapping ) sa lucreze cu o viteza adecvata.

Figura 16

In  cazul  algoritmilor  de  planificare  bazati  pe  prioritati  se  foloseste  o  varianta  a metodei prezentate anterior, numita Roll Out / Roll In: daca in sirul de planificare apare un job cu prioritate ridicata, programul de gestionare poate evacua un job mai putin prioritar, care va fi reintrodus in memorie si isi va continua executia numai dupa ce job-ul prioritar s-

a incheiat.

In mod normal, un job care a fost evacuat va fi readus in aceeasi partitie, restrictie impusa  atat  de  strategia  de  alocare,  cat  si  de  metoda  de  relocare.  Daca  relocarea  se realizeaza in momentul asamblarii sau in momentul incarcarii ( relocare statica ), job-ul nu poate fi transferat intr-o alta partitie, daca insa se lucreza cu relocare dinamica ( cu registru baza si registru limita, de exemplu ) acest lucru este posibil.

Interschimbarea job-urilor necesita o memorie externa cu acces direct, rapid, care sa

fie  suficient  de  incapatoare  pentru  a  putea  ingloba  copii  ale  tuturor  imaginilor  memorie utilizator (de obicei se foloseste in acest scop un disc magnetic performant). Toate procesele

ale caror imagini memorie se afla pe disc si care sunt gata sa intre in executie se grupeaza

intr-o „coada”, in timp ce procesele existente in memorie la momentul respectiv formeaza o

„coada”  sistem  separata.  Atunci  cand  planificatorul  UC  doreste  sa  lanseze  in  executie  un proces, el apeleaza dispecerul care verifica daca procesul se afla in memorie. Daca nu, si daca nu exista nici o partitie libera, dispecerul evacueaza din memorie unul dintre procese,

introduce  in  locul  sau  procesul  dorit,  reincarca  registrile  si  transfera  controlul  procesului selectat. Este important ca procesul ales pentru a fi evacuat sa fie inactiv, altfel pot sa apara eroari severe.

Intr-un   sistem   ce   foloseste   interschimbarea   job-urilor   timpul   de   comutare   a contextului  este  ridicat.  Pentru  cresterea  eficientei  este  de  dorit  ca  timpul  de  executie  al fiecarui proces sa fie mare in comparatie cu timpul necesar interschimbarii.


Job-uri cu dimensiune variabila

Exista  situatii  in  care  un  program  aflat  in  executie  genereaza  pe  baza  datelor  de intrare   cereri   suplimentare   de   memorie.   Daca   datorita   acestor   cereri   se   depaseste dimensiunea partitiei alocate deja job-ului, sistemul de operare trebuie sa intervina. Exista trei posibilitati:

   incheierea fortata a job-ului: se considera ca formularea unei cereri suplimentare fata

de  maximul  necesar  declarat  inainte  de  alocarea  partitiei  reprezinta  o  eroare  de executie;

   returnarea controlului programului utilizator cu un indicator de stare care sa specifice

ca  nu  exista  memorie  in  plus ;  programul utilizator  va  decide  apoi  daca  se  incheie imediat  sau  isi  modifica  modul  de  operare,  astfel  incat  sa  lucreze  in  spatiul

disponibil;

   atunci  cand  exista  hardware  de  relocare  dinamica,  se  evacueaza  job-ul,  se  asteapta eliberarea unei partitii cu dimensiune mai mare, se incarca job-ul in aceasta partitie ( relocandu-l  daca  este  necesar  )  si  se  continua  executia;  este  o  solutie  destul  de costisitoare, dar asigura flexibilitate maxima programelor care doresc sa-si modifice cerintele de memorie in functie de necesitati.

Stabilirea dimensiunii partitiilor

Principala  problema  a  metodei  de  gestionare  a  memoriei  ce  utilizeaza  partitii  de dimensiune  fixata  este  gasirea  unei  bune  corespondente  intre  dimensiunile  partitiilor  si necesarul  real  de  memorie  al  job-urilor.  In  proiectare,  decizia  initiala  se  bazeaza  pe  un calcul  probabilistic.  Din  momentul  in  care  sistemul  devine  operational  se  pot  obtine informatii  despre  numarul  si  dimensiunea  job-urilor  ce  se  executa,  pe  baza  carora  sa  se stabileasca apoi o structura a partitiilor mult mai apropiata de necesitatile reale. In general, nivelul  de  performanta  al  unui  sistem  de  calcul  depinde  proportional  de  nivelul  de multiprogramare  care,  la  randul  sau,  este  direct  afectat  de  modul  in  care  este  gestionata memoria:  daca,  de  exemplu,  jumatate  din  spatiul  total  de  memorie  ramane  neutilizat, inseamna ca folosind o metoda de gestionare mai buna ar fi posibil sa se execute de doua ori mai multe job-uri.

Fragmentarea memorie

Un  job  cu  un  necesar  de  memorie  de  x  cuvinte  se  poate  executa  intr-o  partitie  de dimensiune  y  cuvinte,  daca  y>=x.  Diferenta  de  (  y-x  )  cuvinte  reprezinta  o  fragmentare interna, adica o cantitate de memorie continuta in partitia alocata, dar neutilizata. In cazul in care  o  partitie  este  disponibila,  dar  prea  mica  pentru  oricare  dintre  job-urile  aflate  in asteptare se spune ca in sistem exista fragmentare externa. Ambele tipuri de fragmentare genereaza risipa de memorie.

De exemplu, daca se separa o zona de 44K in trei partitii de cate 8K si o partitie de

20K, iar coada” de planificare contine job-uri  ce necesita 15K, 6K, 10K, 10K, partitia de

20K si una dintre partitiile de 8K pot fi alocate job-ului de 15K ( producand o fragmentare interna  de  5K  )  si  respectiv,  job-ului  de  6K  (  producand  fragmentare  interna  de  2K  ). Deoarece restul job-urilor sunt prea mari pentru cele doua partitii disponibile ( fiecare avand


cate 8K ), acestea raman neutilizate, generand o fragmentare externa de 16K. Fragmentarea totala, atat interna cat si externa, va fi de 23K, adica mai mult decat jumatate din intreaga memorie folosita in exemplu. Daca memoria este grupata in partitii de 20K, 8K, si 16K, job-

ul de 15K se poate executa in partitia de 20K, job-ul de 6K in partitia de 8K, iar unul dintre

job-urile  de  10K  in  partitia  de  16K,  producand  o  fragmentare  interna  totala  de  13K,  dar eliminand  fragmentarea  externa.  In  cazul  ideal  in  care  dimensiunile  partilor  se  potrivesc exact  dimensiunilor  job-urilor  se  pot  executa  toate  cele  patru  job-uri  fara  fragmentare interna sau externa.

3.3. Multiprogramare cu partitii variabile

In cazul utilizarii metodei de multiprogramare cu partitii de dimensiune fixata, cea mai dificila problema este optimizarea dimensiunii partitiilor, astfel incat sa se minimizeze fragmentarea memoriei ( interna si externa ). Problema poate fi rezolvata daca se permite modificarea  dinamica  a  marimii  partitiilor.  Initial,  programele  utilizator  au  la  dispozitie intreaga  memorie,  considerata  ca  un  bloc  de  dimensiune  mare.  Sistemul  de  operare genereaza si apoi reactualizeaza un tabel in care pastreaza informatii despre zonele libere si zonele ocupate ale memoriei. In momentul aparitiei unui job, se cauta o zona de memorie suficient de mare si, daca este gasita, se aloca job-ului numai dimensiunea ceruta, restul de memorie disponibila fiind pastrata pentru satisfacerea altor eventuale cereri.

In  general,  in  memorie  se  afla  “risipite”  mai  multe  zone  nealocate,  de  diferite dimensiuni. Atunci cand apare un job care necesita memorie, este aleasa una dintre acestea,

cu dimensiune suficient de mare pentru a satisface cerintele job-ului. Daca zona este prea mare,  o  parte  se  aloca  job-ului,  iar  restul  se  reintegreaza  setului  de  zone  disponibile.  In momentul terminarii executiei, job-ul elibereaza memoria aferenta, care va fi si ea inclusa in

set.

Daca  zona  de  memorie  disponibilizata  este  adiacenta  celorlalte,  ele  se  pot  contopi pentru a forma o zona d dimensiune mare, moment in care se poate verifica daca nu cumva exista job-uri aflate in asteptare care ar putea folosi aceasta noua harta a memoriei. Aceasta situatie reprezinta un caz particular al problemei de alocare dinamica a memoriei ( care are

ca  obiect  modul  in  care  se  poate  satisface  o  cerere  de  dimensiune  precizata  avand  la

dispozitie o lista de zone de memorie disponibile ), pentru care exista mai multe variante de rezolvare.  Cele  mai  utilizate  strategii  de  selectare  a  zonei  de  memorie  ce  urmeaza  a  fi alocata sunt:

   First  Fit  (  prima  potrivire  ):  se  aloca  prima  zona  de  memorie  disponibila  cu dimensiune suficient de mare pentru a satisface cerintele job-ului;

   Best Fit ( cea mai buna potrivire ): dintre toate zonele de memorie disponibile a caror dimensiune permite alocarea job-ului, se alege zona cu cea mai mica dimensiune ( se minimizeaza fragmentarea interna );

   Worst Fit ( cea mai proasta potrivire ): se aloca zona de memorie disponibila care are cea mai mare dimensiune.

La  fel  ca  si  in  cazul  utilizarii  partitiilor  fixate,  metoda  care  foloseste  partitii  de dimensiune  variabila  se  afla  intr-o  stransa  interactiune  cu  planificarea  job-urilor:  exista  o lista a dimensiunii zonelor de memorie disponibile si o „coada” in care se plaseaza job-urile


ce   formuleaza   cereri   de   memorie,   ordonata   conform   algoritmului   folosit   de   catre planificatorul de job-uri. Alocarea memoriei de desfasoara normal pana in momentul in care cerintele de memorie ale unui jub nu mai pot fi satisfacute doarece nu mai este disponibila nici o zona de memorie cu dimensiune suficient de mare. Planificatorul de job-uri va trebui

sa astepte eliberarea unei astfel de zone sau sa inspecteze „coada” pentru a gasi un job mai putin prioritar, al carui necesar de memorie sa poata fi satisfacut.

Suporul   hardware   minim   necesar   pentru   implementarea   metodei   de   gestiune   a memoriei  folosind  partitii  de  dimensiune  variabila  este  aceeasi  ca  si  in  cazul  metodei  ce utilizeaza  partitii  cu  dimensiune  fixa:  doi  registri  in  care  sa  se  pastreze  valorile  adreselor limita  superioara  si  inferioara  ale  partitiei  de  memorie  alocate  job-ului.  Atunci  cand planificatorul  UC  selecteaza  procesul  in  vederea  executiei,  dispecerul  incarca  in  registri valorile corespunzatoare si, deoarece fiecare adresa generata de catre UC este verificata prin comparatie  cu  continutul  acestor  registri,  se  asigura  astfel  protectia  celorlalte  programe utilizator si a datelor aferente fata de modificarile ce ar putea fi generate d catre procesul curent. De fapt, deosebirea dintre metoda cu partitii fixate si  cea  cu  partitii variabile este determinata de catre software, hardware-ul fiind acelasi.

Fragmentarea memoriei

Metoda   gestionarii   memoriei   prin   folosirea   partitiilor   de   dimensiune   variabila prezinta dezavantajul existentei fragmentarii externe, aparute in momentul in care cantitatea totala de memorie disponibila este suficient de mare pentru a satisface o anumita cerere, dar




nu formeaza o zona contigua, ci este risipita in mai multe zone de mica dimensiune. Metoda gestionarii memoriei prin folosirea partitiilor de dimensiune variabila elimina insa aproape

complet  fragmentarea  interna  (  se  aloca  numai  atata  memorie  cata  are  nevoie  job-ul  ce trebuie planificat ). Cu toate acestea, daca zona disponibila este doar putin mai mare decat cea  necesara,  pastrarea  evidentei  celor  cativa  octeti  ramasi  nealocati  ocupa  mai  multa memorie decat s-ar pierde prin alocarea si neutilizarea lor. De aceea, se prefera ca in acest caz  sa  se  aloce  intreaga  zona,  introducandu-se  astfel,  intr-o  mica  masura,  fragmentare interna.

Compactarea

Compactarea reprezinta o metoda folosita pentru rezolvarea problemei fragmentarii

si  consta  in  deplasarea  continutului  zonelor  de  memorie  astfel  incit  sa  se  grupeze  toate zonele libere intr-un bloc de dimensiune mare. Pentru ca programele ce formau continutul zonelor  de  memorie  sa  poata  lucra  la  noile  locatii  trebuie  relocate  toate  adresele  interne.

Daca este vorba despre o relocare statica, compactarea nu poate fi facuta; ea este posibila

numai daca se foloseste relocare dinamica, la momentul executiei, folosind registri limita si

de baza ( in acest caz va fi necesara doar mutarea programului si a datelor si apoi inscrierea noii adrese in registrul de baza ).


3.4.                  Paginarea memorie

Paginarea  este  o  metoda  care  rezolva  problema  fragmentarii  in  alt  mod  decat compactarea  ei:  ea  permite  ca  memoria  alocata  unui  program  sa  nu  fie  contigua,  ceea  ce inseamna ca programului ii poate fi alocata memorie oriunde exista si este disponibila.

Suportul hardware

Se considera ca memoria fizica ( atat cea interna cat si cea auxiliara ) este separata in blocuri de marime fixa numite cadre. Memoria logica este impartita in blocuri de aceeasi dimensiune numite pagini. Atunci cand se doreste lansarea in executie a unui program se transfera paginile sale din memoria auxiliara ( de obicei un disc magnetic cu acces rapid ) in oricare dintre cadrele disponibile ale memoriei interne. Fiecare dintre adresele generate de catre UC este formata prin alipirea a doua informatii: numnarul paginii ( p ) si deplasarea in pagina  (  d  ).  Numarul  paginii  este  utilizat  ca  index  intr-o  tabela  de  pagina  care  contine adresele  de  baza  ale  cadrelor  asociate  paginilor  programului  respectiv.  Adresa  de  baza impreuna cu deplasarea in pagina definesc adresa fizica de memorie ce corespunde adresei logice generate de UC. Figurile 17 si 18 ilustreaza suportul hardware si respectiv modelul de memorie cu paginare ( paginarea memoriei logice si fizice ).

Figura 17


Figura 18

Dimensiunea  in  numar  de  cuvinte  a  paginii  (  si  a  cadrului  )  este  definita  prin hardware. Dimensiunea paginii este de obicei o putere a lui 2, ceea ce usureaza separarea adresei logice in cele doua componente: numar de pagina si deplasare. Daca marimea unei pagini este de 2n  unitati de adresare ( octeti sau cuvinte ), atunci cei mai putini semnificativi

n  biti  ai  adresei  logice  reprezinta  deplasarea  in  pagina  iar  ceilalti  biti,  mai  semnificativi, reprezinta numarul paginii.

Planificarea job-urilor

O  particularitate  deosebit  de  importanta  a  paginarii  este  distinctia  foarte  neta  intre perceptia utilizatorului asupra memoriei si realitatea fizica a acesteia. Programul utilizator considera ca este unicul beneficiar al unui spatiu contiguu de memorie, in timp ce, de fapt,

el  este  risipit  in  mai  multe  zone  distincte,  alaturi  de  alte  programe.  Corespondenta  intre spatiul de adresa utilizator ( logic ) si cel real ( fizic ) este realizata de catre hardware-ul de translatare  a  adresei  (  de  tabelare  ),  operatie  controlata  de  catre  sistemul  de  operare  si invizibila pentru utilizator.

Pentru  a  putea  gestiona  memoria,  sistemul  de  operare  trebuie  sa  aiba  la  dispozitie informatii despre numarul total de cadre, cadrele alocate, cadrele disponibile etc., toate fiind pastrate  intr-o  structura  de  date  numita  tabela  de  cadre.  Fiecare  intrare  a  acestei  tabele corespunde  cate  unui  cadru  al  memoriei  fizice,  ingloband  principalele  sale  caracteristici: este liber sau este alocat, procesul caruia ii este alocata si pagina asociata.

La fel ca si in celelalte cazuri, planificatorul de job-uri este influentat in mod direct

de  catre  metoda  de  gestionare  a  memoriei.  Planificatorul  de  job-uri  examineaza  marimea job-ului  ce  urmeaza  a  fi  executat  (  exprimata  in  numar  de  pagini),  dupa  care  inspecteaza

lista  de  cadre  disponibile,  tinand  cont  de  faptul  ca  pentru  memorarea  fiecarei  pagini

utilizator  este  necesar  un  cadru  in  memoria  interna.  In  urma  alocarii  realizate  de  catre planificator, paginile se incarca pe rand in cadrele alocate, al caror numar va fi memorat in tabela de pagina al job-ului ( Figura 19-a alocarea cadrelor libere inainte si Figura 19-b alocarea cadrelor libere dupa ). Tabela de pagina asociata fiecarui job se pastreaza alaturi

de alte valori semnificative in blocul de control al job-ului.

Sistemul de operare pastreaza cate o copie a tabelei de pagina, a contorului program

si a continutului registrilor fiecarui program utilizator, informatii folosite si de catre dispecer

in momentul comutarii UC de la un proces la altul.


Figura 19 a

Figura 19 - b

Fragmentarea memoriei

Paginarea  elimina  fragmentarea  externa:  unui  job  ii  poate  fi  alocat  orice  cadru,  cu conditia ca acesta sa fie disponibil. Deoarece cadrele nu sunt considerate unuitati de alocare

a  memoriei  (  se  aloca  in  intregime,  nu  partial  ),  este  insa  posibila  aparitia  fragmentarii interne.  Un  exemplu  in  acest  sens  este  situatia  in  care  dimensiunea  job-ului  nu  este  un


multiplu exact al dimensiunii paginii si, prin urmare, ultimul cadru alocat nu este complet ocupat. In cel mai defavorabil caz un job poate fi format din N pagini si 1 cuvant, implicand alocarea   a   N+1   cadre,   ceea   ce   va   genera   o   fragmentare   interna   aproape   egala   cu dimensiunea cadrului.

Implementarea tabelei de pagina

In cazul in care dimensiunea tabelei de pagina este destul de redusa, se poate utiliza pentru implementare un set de registri specializati, foarte rapizi, care sa asigure o eficienta ridicata  a  translatarii  adreselor  (  eficienta  este  o  cerinta  foarte  importanta  deoarece  orice acces la memorie se face numai prin intermediul tabelei de pagina ). Instructiunile destinate incarcarii sau modificarii acestor registri sunt privilegiate, putand fi realizate numai de catre sistemul de operare.

Daca insa dimensiunea tabelei de pagina este mare, se prefera ca ea sa fie pastrata in memoria principala, intr-o zona indicata de valoarea unui Registru de Baza al Tabelei de Pagina ( RBTP ). Atunci cand se doreste sa se lucreze cu alta tabela de pagina decat cea curenta, este suficient sa se incarce registrul cu o alta valoare, reducandu-se astfel in mod substantial  timpul  de  comutare  a  contextului.  O  particularitate  a  acestei  variante  de implementare  este  faptul  ca  accesarea  unui  cuvant  de  memorie  utilizator  necesita  doua operatii de acces la memorie ( unul pentru tabela de pagina si altul pentru cuvantul propriu-

zis ), viteza de operare fiind micsorata de doua ori. Folosind valoarea din RBTP deplasata

cu numarul de pagina p ( continut in adresa logica ), se determina mai intai numarul de cadru c asociat paginii ( pe durata unui prim acces la memoria ce contine tabela de pagina

). Numarul de cadru c impreuna cu deplasarea de pagina d ( continuta in adresa logica )

furnizeaza  apoi  adresa  fizica  reala,  cu  ajutorul  careia  se  poate  accesa  cuvantul  utilizator dorit.

O a treia varianta de implementare, care rezolva si problema expusa anterior, consta

in utilizarea unei memorii hardware speciale, rapide si de mica dimensiune ( set de registri asociativi ) cu urmatoarele proprietati: fiecare registru are doua componente, numite cheie,

si respectiv, valoare; precizandu-se un anumit element, el este comparat simultan cu toate cheile si, daca se depisteaza o coincidenta, se extrage campul valoare corespunzator. Daca

registri asociativi se folosesc impreuna cu tabelele de pagina, in cadrul campului cheie se memoreaza  numarul  de  pagina  iar  in  campul  valoare,  numarul  cadrului  asociat.  Deoarece acest tip de hardware este destul de costisitor, numarul registrilor asociativi nu poate fi prea mare si, prin urmare, setul contine numai cateva dintre intrarile tabelei de pagina. Atunci cand UC  genereaza o adresa logica, daca numarul de pagina coincide cu una dintre chei, numarul de cadru devine imediat disponibil si este folosit pentru a accesa memoria; timpul necesar pentru realizarea intregii operatii este cu cel mult 10% mai mare decat cel aferent

unui acces direct la memorie. Daca insa numarul paginii nu coincide cu nici una dintre chei, pentru  obtinerea  numarului  cadrului  asociat  trebuie  efectuat  un  acces  la  tabela  de  pagina aflata  in  memoria  interna.  Informatia  astfel  obtinuta  va  fi  folosita  atat  pentru  accesarea memoriei utilizator, cat si pentru a fi adaugata in cadrul registrilor asociativi ( impreuna cu numarul  de  pagina  asociat  ),  ca  sa  poata  sa  fie  regasita  rapid  in  cadrul  unei  referiri ulterioare. Din punctul de vedere al timpului efectiv de acces la memorie, s-a demonstrat ca folosirea a 8 pana la 10 registri asociativi asigura o incetinire a operatiilor de memorie cu

aproximativ 20% pana la 10% fata de accesul direct, ceea ce constituie un avantaj in raport


cu performantele celei de-a doua variante de implementare prezentate.

Paginarea in sisteme de time-sharing

Unul dintre avantajele paginarii ( important mai ales in cazul sistemelor de tip time- sharing ) este posibilitatea folosirii in comun de catre job-uri diferite a unui acelasi cadru al memoriei fizice. Singura conditie este ca in acel cadru ( sau in acele cadre ) sa fie memorat cod  reentrent  (  numit  si  cod  pur  ),  adica  un  cod  care  nu  se  poate  modifica  in  timpul executiei. Datorita proprietatii de reentranta, este posibil ca doua sau mai multe procese sa execute simultan acelasi cod, fiecare proces pastrand o copie a registrilor si a datelor proprii

( care sunt diferite ). In memoria fizica este deci necesar sa se pastreze o singura copie a codului comun ( fiecare tabela de pagina indica spre acelasi/aceleasi cadru/cadre ), in timp

ce paginile corespunzatoare datelor proceselor sunt memorate in cadre diferite.

Exemplele      de    utilizare       sunt     numeroase:       editoarele      de    texte,     compilatoarele, asambloarele, sistemele de baze de date, sunt numai cateva dintre produsele software ce pot constitui cod folosit in comun, simultan, de catre mai multi utilizatori a unui sistem de tip time-sharing.  Principalul  avantaj  al  acestei  metode  este  eliminarea  risipei  de  memorie  ( Figura 20 – accesarea in comun a codului in cazul paginarii ).



Figura 20

Cerinta  de  reentranta  fiind  stricta,  codul  folosit  in  comun  trebuie  sa  fie  accesibil numai pentru citire ( read only ), asigurarea acestei protectii fiind o sarcina a sistemului de operare. In general, protejarea paginilor se realizeaza prin asocierea unor biti de protectie,


inclusi  in  tabela  de  pagina.  De  exemplu,  cu  un  singur  bit  se  poate  permite  acces  de citire/scriere  sau  doar  de  citire.  Deoarece  pentru  fiecare  referire  a  memoriei  se  foloseste tabela  de  pagina,  odata  cu  calcularea  adresei  fizice  se  pot  verifica  si  bitii  de  protectie.  O incercare  de  scriere  intr-o  pagina  marcata  numai  pentru  citire,  de  exemplu,  va  genera  o

„capcana” ( trap ) hardware catre sistemul de operare, avand semnificatia de a semnaliza violarea protectiei memoriei.

3.5.                  Segmentarea memoriei

De cele mai multe ori utilizatorul unui sistem percepe memoria nu ca pe o succesiune

de cuvinte ( asa cum este ea in realitate ), ci mai curand ca pe o colectie de zone contigue (

nu  neaparat  ordonate  )  in  care  sunt  stocate  modulele  componente  ale  programelor  ( proceduri,  functii,  etc.  )  si  structurile  de  date  aferente  (  tabele,  matrici,  vectori,  stive, variabile etc. ). Deoarece fiecare dintre module are o anumita functionalitate, de cele mai

multe  ori  zonele  de  memorie  in  care  sunt  stocate  (  numite  si  segmente  )  difera  ca dimensiune  si  pot  fi  identificate  prin  nume:  „functia  pondere”,  programul  principal”,

„matricea A”, etc. In cadrul unui segment, un anumit element poate fi referit prin precizarea deplasarii  fata  de  inceputul  segmentului:  „a  treia  instructiune  a  functiei  Pondere”,  „prima instructiune a programului principal”, al doilea element al vectorului V”, etc.

Segmentarea  reprezinta  o  metoda  de  gestionare  care  accepta  si  foloseste  aceasta viziune a utilizatorului asupra memoriei. Ea trateaza programul utilizator ( spatiul de adrese logice  )  ca  pe  o  colectie  de  segmente,  fiecare  segment  fiind  caracterizat  prin  nume  si lungime. In consecinta, utilizatorul are obligatia de a preciza fiecare adresa logica prin doua elemente:  un  nume  de  segment  si  o  deplasare  (  in  cazul  paginarii,  utilizatorul  specifica adresa logica, separata apoi de catre hardware intr-un numar de pagina si o deplasare! ). De obicei, pentru simplificarea implementarii, se prefera numerotarea segmentelor si apelarea

lor  prin  numar,  nu  prin  nume.  De  cele  mai  multe  ori,  segmentele  sunt  create  in  mod automat, in urma asamblarii, compilarii, etc. De exemplu, un compilator Pascal poate crea

segmente  distincte  pentru:  variabile  globale,  stiva  cu  care  lucreaza  procedurile,  codul fiecarei functii sau proceduri, variabilele locale ale fiecarei functii sau proceduri. Programul

de incarcare va asocia numere tuturor segmentelor astfel create.

Suportul hardware

Segmentarea implica existenta unei corespondente intre adresa logica bidimensionala definita de utilizator ( numarul de segmente s si deplasarea in cadrul segmentului d ) si adresa  fizica  unidimensionala  ce  ii  este  asociata  in  memorie.  Aceasta  corespondenta  este realizata prin intermediul tabelei de segment ( Figura 21 ) care contine un numar de intrari egal  cu  numarul  de  segmente  din  programul  respectiv.  Fiecare  intrare  a  tabelei  contine informatii  despre  adresa  de  baza  a  segmentului  (  notata  baza”  in  figura  )  si  despre dimensiunea acestuia ( notata „dim” in figura ) si poate fi accesata prin folosirea ca index a numarului de segment. Daca deplasarea precizata in cadrul adresei logice nu satisface relatia

0<d<dim, sistemul de operare va fi sesizat printr-o “capcana” cu semnificatia „incercare de adresare  in  afara  segmentului”.  Daca  insa  deplasare  se  incadreaza  intre  limitele  permise, valoarea ei se adauga la valoarea adresei de baza a segmentului, formandu-se astfel adresa din  memoria  fizica  a  cuvantului  dorit  (  se  poate  afirma  deci,  prin  analogie,  ca  tabela  de

segment este o colectie de perechi de registri baza/limita).


Figura 21

Figura 22

Pentru exemplificare se poate folosi situatia prezentata in Figura 22, care ilustreaza


dispunerea in  memorie a unui program format din patru segmente ( numerotate de la 0 la 3

). Pentru fiecare dintre acestea , tabela de segment contine cate o intrare separata, in care se pastreaza  adresa  de  inceput  a  segmentului  in  memoria  fizica  (  baza  )  si  dimensiunea segmentului ( dim ). Astfel, deoarece segmentul 1 incepe la locatia 1500 si are dimensiunea

500  de  cuvinte,  referirea  cuvantului  25  al  segmentului  1  corespunde  in  memoria  fizica

locatiei 1500+25=1525, in timp ce referirea cuvantului 630 al aceluiasi segment va genera o

„capcana” catre sistemul de operare.

Implementarea tabelei de segment

Solutiile  de  implementare  a  tabelei  de  segment  sunt  asemanatoare  celor  folosite  in cazul tabelei de pagina.

O  prima  varianta  (  preferata  pentru  tabelele  de  segment  de  mici  dimensiuni  )  este utilizarea  unor  registri  rapizi  care  sa  asigure  o  viteza  de  acces  foarte  mare  si,  eventual, efectuarea simultana a operatiilor de comparare a deplasarii cu dimensiunea segmentului si respectiv a insumarii deplasarii cu adresa de baza a segmentului.

Atunci cand programul este format dintr-un numar mare de segmente se prefera ca tabela  de  segment  sa  fie  pastrata  in  memorie.  Pentru  a  putea  fi  accesata,  se  foloseste  un registru baza al tabelei segment ( RBTS ) si, deorece programele nu contin acelasi numar de segmente, se utilizeaza in plus un registru lungime al tabelei de segment ( RLTS ). In acest caz,  pentru  fiecare  adresa  logica  (  s,d  )  se  verifica  mai  intai  corectitudinea  numarului  de segment  (  0<s<RLTS  )  si  apoi  se  insumeaza  numarul  segmentului  cu  continutul  RBTS, rezultand astfel adresa din memorie a intrarii tabelei de segment ce va fi citita. Dupa aceea

se  procedeaza  in  mod  obisnuit:  se  compara  deplasarea  cu  dimensiunea  segmentului  si  se calculeaza  adresa  fizica  a  cuvantului  dorit  ca  suma  a  deplasarii  si  a  adresei  de  baza  a segmentului.

La fel ca si in cazul paginarii, cea dea doua varianta de implementare a tabelei de segment necesita doua accese de memorie pentru o singura adresa logica, obligand sistemul

de  calcul  sa  functioneze  de  doua  ori  mai  incet.  Solutionarea  acestei  probleme  presupune folosirea unui set de registri asociativi  in care sa se pastreze cele mei recent utilizate intrari

ale tabelei de segment. Un set destul de redus de registri asociativi ( 8 sau 16 ) poate asigura rezultate destul de performante: timpul de acces la memorie va fi doar cu cel mult 15% pana

la 10% mai mare decat in cazul accesului direct.

Segmentarea in sisteme de time-sharing

La fel ca si in cazul paginarii, segmentarea ofera atat posibilitatea folosirii in comun

de  catre  mai  multi  utilizatori  a  aceluiasi  (  sau  acelorasi  )  segment(e)  cat  si  asigurarea protectiei segmentelor cu ajutorul unor biti dedicati, inclusi in tabela de segment.

Ca  exemplu,  se  poate  folosi  situatia  in  care  un  program  contine  segmente  de instructiuni si segmente de date. Deoarece, in general, intr-o arhitectura moderna de calcul

nu este permis ca instructinile sa se automodifice, segmentelor de instructini li se pot asocia protectii  care  sa  permita  accesul  doar  pentru  citire  sau  doar  pentru  executie.  Verificarea bitilor  de  protectie  este  realizata  de  catre  hardware  in  momentul  efectuarii  operatiilor  de punere  in  corespondenta  a  adresei  logice  cu  cea  fizica.  Se  pot  detecta  astfel  multe  dintre

erorile de programare uzuale, inainte ca ele sa provoace neplaceri semnificative.


Pentru fiecare job, blocul de control ( folosit de catre dispecer in momentul comutarii UC  intre  procese  )  contine  alaturi  de  alte  informatii  si  tabela  de  segment.  Atunci  cand intrarile in tabelele de segment apartinand unor job-uri diferite indica aceeasi locatie fizica, segmentul respectiv va fi folosit in comun ( mecanismul este asemanator cu cel prezentat in cazul paginarii ). Consecinta fireasca este ca orice informatie poate fi folosita in comun daca este  definita  ca  segment.  Deoarece  acest  mecanism  functioneaza  si  pentru  mai  multe segmente, rezulta ca si programele ( colectii de segmente) pot fi la randul lor, folosite in comun  de  catre  mai  multi  utilizatori  (  aparenta  simplitate  a  mecanismului  ascunde  si anumite subtilitati pe care programatorul este bine sa le cunoasca ).

Fragmentarea memoriei

Sarcina  de  a  gasi  si  de  a  aloca  memorie  pentru  toate  segmentele  programului utilizator  revine  planificatorului  de  job-uri.  Situatia  este  asemanatoare  cu  cea  aparuta  in cazul paginarii, cu exceptia faptului ca aici lungimea segmentului este variabila ( paginile aveau  toate  acceasi  dimensiune  ).  Aceasta  inseamna  ca  este  vorba  despre  o  problema  de alocare  dinamica,  la  fel  ca  si  in  cazul  metodei  de  gestionare  a  memoriei  prin  partitii  cu dimensiune variabila, pentru a carei solutionare se pot folosi algoritmi de tip Best Fit sau First Fit.

Daca   nici   una   dintre   zonele   contigue   de   memorie,   disponibile   la   momentul considerat, nu este suficient de mare pentru a cuprinde un anumit segment, poate sa apara fragmentarea  esterna.  In  general,  daca  dimensiunea  medie  a  segmentelor  este  redusa, fragmentarea  externa  nu  va  fi  semnificativa.  Rezolvarea  acestei  probleme  se  realizeza  cu aceleasi metode ca si in cazul gestionarii memoriei prin partitii cu dimensiune variabila.

Paginarea segmentelor

Atat  paginarea  cat  si  segmentarea  au  avantaje  si  dezavantaje.  Acesta  este  motivul pentru  care,  uneori,  s-a  preferat  combinarea  celor  doua  metode  de  gestionare  a  memoriei prezentate anterior.

Un  exemplu  in  acest  sens  este  si  paginarea  segmentelor  (  Figura  23  ),  in  cadrul careia se foloseste avantajul eliminarii fragmentarii externe oferit de catre paginare ( pentru alocare poate fi folosit oricare dintre cadrele disponibile ). Deosebirea dintre aceasta metoda

si  segmentare  este  aceea  ca  intrare  tabelei  de  segment  nu  contine  adresa  de  baza  a segmentului ci adresa de baza a unei tabele de pagina asociata acestui segment. Segmentul este   format   dintr-un   numar   de   pagini   de   aceeasi   dimensiune.   Deplasarea   in   cadrul

segmentului se exprima prin numar de pagina si deplasare in pagina. Cu numarul de pagina

folosit ca index in tabela de pagina a segmentului, se obtine numarul cadrului care, in final,

se  combina  cu  deplasarea  in  pagina  si  formeaza  adresa  fizica.  Deoarece  ultima  pagina  a fiecarui segment nu este intotdeauna complet ocupata, metoda introduce totusi o cantitate redusa de fragmentare externa ( in realitate, atunci cand se lucreaza cu tabele de segment de dimensiuni mari, mecanismul este ceva mai complicat ).


Figura 23








Politica de confidentialitate

DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 621
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Termeni si conditii de utilizare | Contact
© SCRIGROUP 2019 . All rights reserved

Distribuie URL

Adauga cod HTML in site