Zarządzanie pamięcią w 32 i 64-bitowych systemach Windows, cz. I     Windows Server 2008     Zarządzanie pamięcią w 32 i 64-bitowych systemach Windows, cz. III

Zarządzanie pamięcią w 32 i 64-bitowych systemach Windows, cz. II Udostępnij na: Facebook

Windows 2000/XP/2003/Vista/2008

Autor: Jacek Światowiak

Opublikowano: 11 grudnia 2007

Zawartość strony
W poprzedniej części  W poprzedniej części
Systemowe struktury danych trybu chronionego  Systemowe struktury danych trybu chronionego
Segmentacja  Segmentacja
Selektor  Selektor
Deskryptor  Deskryptor
Rejestr Globalnej tablicy deskryptorów  Rejestr Globalnej tablicy deskryptorów
Rejestr lokalnej tablicy deskryptorów  Rejestr lokalnej tablicy deskryptorów
Przeczytaj pozostałe części tego artykułu  Przeczytaj pozostałe części tego artykułu

W poprzedniej części

Omówiono ogólnie tryby pracy procesora zgodnego z i386 w tym adresowanie w trybie rzeczywistym i chronionym, przedstawiono pojęcie adresacji logicznej, liniowej i fizycznej. Zaprezentowano zastosowanie rejestrów procesora, oraz ogólnie mechanizm współpracy z pamięcią.

 Do początku strony Do początku strony

Systemowe struktury danych trybu chronionego

W trybie z ochroną procesor musi mieć dostęp do specjalnych struktur danych (umieszczonych w pamięci). Te struktury danych to:

- tablica opisu segmentów globalnych – GDT (istnieje tylko jedna),

- tablice opisu segmentów lokalnych – LDT (przypisane poszczególnym zadaniom lub grupom zadań),

- tablica wektorów (deskryptorów) przerwań – IDT (istnieje tylko jedna),

- segmenty stanu zadania – TSS (ang. Task State Segment),

Procesor wykorzystuje zestaw rejestrów wewnętrznych ładowanych zmiennymi systemowymi z pamięci. Zmienne te to:

  1. Deskryptory aktualnie używanych segmentów CS, DS, SS, ES,
  2. Adres bazowy (początkowy) i długość tablicy opisu segmentów globalnych – GDT,
  3. Adres bazowy (początkowy) i długość tablicy opisu segmentów lokalnych – LDT,
  4. Indeks (w tablicy GDT) deskryptora aktualnej tablicy LDT,
  5. Indeks (w tablicy GDT lub LDT) deskryptora segmentu TSS aktualnie wykonywanego zadania (procesu, aplikacji),
  6. Adres bazowy (początkowy) i długość segmentu TSS,
  7. Indeks (w tablicy GDT lub LDT) deskryptora bieżącego zadania
  8. Adres bazowy (początkowy) i długość tablicy wektorów przerwań.

Uwaga

Rejestry zawierające zmienne oznaczone jako 1, 2 i 6 są niedostępne dla programisty.

 

 Do początku strony Do początku strony

Segmentacja

Dla trybu 32-bitowego z ochroną. Rejestr segmentowy, który w trybie rzeczywistym wskazuje adres bazowy segmentu, jest tu ładowany pewnym wskaźnikiem zwanym deskryptorem segmentu.

Procesor wyposażony jest w cztery rejestry segmentowe i każdy z nich wykorzystywany jest do obsługi odrębnych zadań systemowych.

GDTR – rejestr deskryptorów globalnych – ang. Global Descriptor Table Register

LDTR – rejestr deskryptorów lokalnych – ang. Local Descriptor Table Register

IDTR – rejestr deskryptorów przerwań – ang. Interrrupt Descriptor Table Register

TR – rejestr zadań – ang. Task Register

Rys. 2.1. Format ogólny rejestru GDTR i IDTR.

Rys. 2.1. Format ogólny rejestru GDTR i IDTR.

 

Rys. 2.2. Format ogólny rejestru LDTR i TR. Część pogrubiona – to cześć widoczna .

Rys. 2.2. Format ogólny rejestru LDTR i TR. Część pogrubiona – to cześć widoczna .

 

Każdy z rejestrów segmentowych podzielony jest na część widoczną (dostępną) dla programisty i część ukrytą (niewidoczną). Dla każdej aplikacji (procesu) dysponujemy 6 rejestrami segmentowymi. Aby zaadresować dany fragment pamięci dla danej aplikacji należy wstępnie wybrać typ segmentu dla kodu, danych czy stosu. Wybór danego typu segmentu de facto skutkuje wybraniem do procesu obliczenia adresu efektywnego konkretnego rejestru segmentowego.

W trybie rzeczywistym rejestr segmentowy zawierał adres bazowy danego segmentu. W trybie z ochroną pełni rolę selektora i zawiera indeks tablicy deskryptorów globalnych GDT (ang. Global Descriptor Table) lub lokalnych LDT (ang. Local Descriptor Table).

Selektor wskazuje więc bezpośrednio na deskryptor segmentu, a pośrednio na segment. Deskryptory segmentów są umieszczane w pamięci, kolejno tworząc tzw. tablice deskryptorów (ang. Descriptor Table). Wykonywany program ma dostęp do dwóch tablic deskryptorów: globalnej tablicy deskryptorów GDT (ang. Globar Descriptor Table) i lokalnej tablicy deskryptorów LDT (ang. Local Descriptor Table). Takie podejście umożliwia realizacje ochrony pamięci w systemach wielozadaniowych. W systemie może istnieć zatem wiele tablic LDT. Każde zadanie, proces, wątek ma swoją własną tablicę deskryptorów.

 Do początku strony Do początku strony

Selektor

Ogólny format selektora przedstawiony jest niżej:

Rys. 2.3. Selektor ładowany do rejestru segmentowego.

Rys. 2.3. Selektor ładowany do rejestru segmentowego.

 

Gdzie:

RPL – oznacza poziom ochrony (ang. Requestor’s Privilege Level) – omówiony zostanie oddzielnie

TI – wskaźnik tablicy (ang. Table Indicator),

O – oznacza, iż deskryptor odnosi się do globalnej tablicy deskryptorów,

1 - oznacza tablicę lokalną,

Numer deskryptora – 13-bitowe pole. Zatem każda z tablic może zawierać do 8192 deskryptorów.

Uwaga

W ukrytym rejestrze związanych z rejestrem segmentowym zawarty jest wyliczony automatycznie adres bazowy (ang. Segment Base Address) i wielkość (ang. Segment Size) segmentu wskazanego przez selektor oraz dodatkowe atrybuty używane przez mechanizmy ochrony procesora.

Pola Segment Size i Segment Base Address są liczbami 32 bitowymi (lub 64-bitowymi – dla trybu 64-bitowego).

 

 Do początku strony Do początku strony

Deskryptor

Deskryptor jest 8-bajtowym rekordem opisującym m.in. położenie i wielkość danego segmentuw pamięci.

Rys. 2.4. Generalny format deskryptora segmentu.

Rys. 2.4. Generalny format deskryptora segmentu.

 

Dla segmentu danych bity 8, 9 i 10 oznaczane są również, jako:

A – Accessed

W – Write Enable

E – Expansion-Direction.

Dla segmentu kodu bity 8, 9 i 10 oznaczane są również, jako:

A – Accessed

R – Read Enabled

C - Conforming

Tabela 2. Konfiguracje bitów 8-11 a właściwości operacji przeprowadzanych na segmentach kodu i danych
Wartość dziesiętnie Bit 11 Bit 10 Bit 9 Bit 8 Typ segmentu Opis
    E W A    
0 0 0 0 0 Dane tylko odczyt
1 0 0 0 1 Dane odczyt i dostęp
2 0 0 1 0 Dane odczyt i zapis
3 0 0 1 1 Dane odczyt, zapis, dostęp
4 0 1 0 0 Dane segment rozszerzalny w dół – tylko odczyt
5 0 1 0 1 Dane segment rozszerzalny w dół – odczyt i dostęp
6 0 1 1 0 Dane segment rozszerzalny w dół – odczyt i zapis
7 0 1 1 1 Dane segment rozszerzalny w dół – odczyt, zapis i dostęp
    C R A    
8 1 0 0 0 Kod tylko wykonywanie
9 1 0 0 1 Kod wykonywanie i dostęp
10 1 0 1 0 Kod wykonywanie i odczyt
11 1 0 1 1 Kod wykonywanie, odczyt i dostęp
12 1 1 0 0 kod tylko wykonywanie – segment zgodny
13 1 1 0 1 Kod wykonywanie i dostęp – segment zgodny
14 1 1 1 0 Kod wykonywanie i odczyt – segment zgodny
15 1 1 1 1 Kod Wykonywanie, odczyt i dostęp – segment zgodny

 

S – (ang. Segment Descriptor System/Code and Data) - rodzaj segmentu

Gdzie:

1 – oznacza segmenty pamięci – kodu lub danych,

0 – oznacza segmenty specjalne (tzw. systemowe struktury danych) i furtki (ang. Gates),

Systemowe segmenty zostały podzielone na następujące kategorie:

- lokalne tablicy deskryptorów LDT (ang. Local Descriptor Table)

- deskryptory stanu zadania TSS (ang. Task-State segment)

- deskryptory furtek wywołania (ang. Call-Gate descriptor)

- furtki przerwań (ang. Interrupt-Gate descriptor)

- furtki potrzasku (ang. Trap-Gate descriptor)

- furtki zadań (ang. Task-Gate descriptor)

Dla S=0 dostępne są następujące typy segmentów:

Tabela 3. Segmenty systemowe
Wartość dziesiętnie Bit 11 Bit 10 Bit 9 Bit 8 Tryb IA-32 (32-bitowy) Tryb IA-32e ( 64-bitowy)
0 0 0 0 0 Zarezerwowany Górny 8 lub 16-bajtowy deskryptor
1 0 0 0 1 16-bit TSS (dostępne) Zarezerwowany
2 0 0 1 0 LDT LDT
3 0 0 1 1 16-bit TSS (zajęte) Zarezerwowany
4 0 1 0 0 16-bit Call-Gate Zarezerwowany
5 0 1 0 1 Task Gate Zarezerwowany
6 0 1 1 0 16-bit Interrupt-Gate Zarezerwowany
7 0 1 1 1 16-bit Trap-Gate Zarezerwowany
8 1 0 0 0 Zarezerwowany Zarezerwowany
9 1 0 0 1 32-bit TSS (dostępne) 64-bit TSS (dostępne)
10 1 0 1 0 Zarezerwowany Zarezerwowany
11 1 0 1 1 32-bit TSS (zajęte) 64-bit TSS (zajęte)
12 1 1 0 0 32-bit Call-Gate 64-bit Call-Gate
13 1 1 0 1 Zarezerwowany Zarezerwowany
14 1 1 1 0 32-bit Interrupt-Gate 64-bit Interrupt-Gate
15 1 1 1 1 32-bit Trap-Gate 64-bit Trap-Gate

 

DPL – (ang. Descriptor Privilage Level) - poziom ochrony opisywanego segmentu – opisany zostanie oddzielnie,

P - (ang. Present) - segment obecny,

AVL – (ang. Available to software) - pole dostępne dla programu (nie wykorzystywane wewnętrznie przez procesor),

D (czasami oznaczany jako B - BIG) – (ang. Default) - bit długości słowa. Ma znaczenie tylko w segmentach kodu, rozszerzalnych w dół, albo w segmentach adresowanych za pomocą rejestru SS. Krótko mówiąc oznacza naturalną dla danego trybu pracy procesora długość wykorzystywanych argumentów (16 lub 32 bitowe).

W przypadku segmentu kodu:

D=1 oznacza stosowanie 32-bitowego przemieszczenia w adresie argumentu oraz 32-bitowych (obok 8-bitowych) argumentów,

D=0 oznacza stosowanie 16-bitowego przemieszczenia w adresie argumentu oraz 16-bitowych (obok 8-bitowych) argumentów,

 

W przypadku segmentu rozszerzalnego w dół, oznacza największe możliwe przemieszczenie w obrębie segmentu:

D=1 oznacza przemieszczenie 4 GB

D=0 oznacza przemieszczenie 64 KB

 

Ważne:

W przypadku operacji z wykorzystaniem segmentu stosu SS:

D=1 oznacza, iż w operacjach bierze udział 32-bitowy wskaźnik stosu (ESP),

D=0 oznacza, iż w operacjach bierze udział 16-bitowy wskaźnik stosu (SP),

 

Bit D ma zastosowanie przy pracy z trybem 32-bitowego adresowania pamięci w trybie płaskim (ang. Flat Model),

G – (ang. Granularity) - ziarnistość – umożliwia opis segmentu o wielkości 4 GB za pomocą tylko 20 bitów. Jednostką wielkości segmentu może być:

G=0 – 1 bajt – wtedy długość segmentu może wynosić maks. 1 MB,

G=1 – 1 blok o wielkość 4 KB – wtedy segment może mieć wielkość 4 GB,

Bit ziarnistości opisuje jedynie wielkość segmentu. Adres bazowy opisany jest zawsze z dokładnością do 1 bajtu (adres bazowy jest liczbą 32-bitową).

L – (ang. Large) - 64-bitowy segment kodu (ma znaczenie tylko dla trybu IA-32e).

Uwaga

Rozmiar logicznej przestrzeni adresowej, określa się jako maksymalną wielkość pamięci widzianą przez jedno zadanie. Każdy program ma dostęp do tablicy GDT i jednej tablicy lokalnej LDT, może więc korzystać z 16384 segmentów (8192 w GDT + 8192 w swojej LDT razem 16K).

 

O wielkości logicznej przestrzeni adresowej decyduje liczba segmentów i ich długość. Dla procesora 80386 (gdzie maksymalna wielkość segmentu i przemieszczenie (przesunięcie, offset lub limit – w literaturze występują różne określenia) jest liczbą 32 bitową); maksymalna wielkość logicznej (wirtualnej) przestrzeni adresowej wynosi 16K * 4 GB = 64 TB.

 

Rys. 2.5. Tabele deskryptorów: globalna (GDT), lokalna (LDT) i ich zależności.

Rys. 2.5. Tabele deskryptorów: globalna (GDT), lokalna (LDT) i ich zależności.

 

W celu zoptymalizowania procesu adresowania wprowadzono ukryte rejestry deskryptorów, są one automatycznie ładowane danymi z odpowiedniego deskryptora podczas umieszczania selektora w danym rejestrze segmentowym.

Położenie w pamięci tablicy deskryptorów globalnych GDT i aktualnie wykorzystywanej tablicy lokalnej LDT jest określane przez zawartość specjalnych rejestrów procesora:

 Do początku strony Do początku strony

Rejestr Globalnej tablicy deskryptorów

GDTR – rejestr globalnej tablicy deskryptorów (ang. Globar Descriptor Table Register).

Rys. 2.6. Format tablicy GDTR.

Rys. 2.6. Format tablicy GDTR.

 

 Do początku strony Do początku strony

Rejestr lokalnej tablicy deskryptorów

LDTR – rejestr lokalnej tablicy deskryptorów (ang. Local Descriptor Table Register).

Rys. 2.7. Format tablicy LDTR.

Rys. 2.7. Format tablicy LDTR.

 

Uwaga

W ukrytym rejestrze związanych z rejestrem segmentowym LDTR zawarty jest wyliczony automatycznie adres bazowy (ang. Segment Base Address) i wielkość (ang. Segment Size) segmentu wskazanego przez powyższy selektor oraz dodatkowe atrybuty używane przez mechanizmy ochrony procesora.

Pola Segment Size i Segment Base Address są liczbami 32-bitowymi (lub 64-bitowymi dla trybu 64-bitoweg).

 

W każdym z rejestrów jest umieszczony adres początku odpowiedniej tablicy (adres liniowy w procesorze 80386 i późniejszych). Struktura rejestru LDTR jest analogiczna do rejestru segmentowego; zawiera również część ukrytą będącą odpowiednikiem rejestru deskryptora segmentu.

LDTR sama stanowi odrębny segment danych, należący do tzw. segmentów systemowych. Jako segment ma swój deskryptor, który to musi być umieszczony w globalnej tablicy deskryptorów.

Tablica LDTR jak i GDTR może zawierać do 8192 deskryptorów, tzn. jej długość nie może przekraczać 64 KB.

Poniżej na rysunku przedstawiano mechanizm translacji adresu logicznego na liniowy dla segmentu opisanego w lokalnej tablicy deskryptorów.

Rys. 2.8. Sposób translacji adresu logicznego na liniowy dla segmentu opisanego w lokalnej tablicy deskryptorów.

Rys. 2.8. Sposób translacji adresu logicznego na liniowy dla segmentu opisanego w lokalnej tablicy deskryptorów.

 

W następnej części przedstawimy mechanizm stronicowania i współpracę z pamięcią wirtualną.

 Do początku strony Do początku strony

Przeczytaj pozostałe części tego artykułu


Jacek Światowiak Jacek Światowiak (MCT, MCSE, MCSE+M, MCSE+S, MCTS, MCP)
Absolwent Wydział Elektroniki, Telekomunikacji i Informatyki Politechniki Gdańskiej. Obecnie zatrudniony w Altkom Akademia S.A. jako Trener Technologii Microsoft. Posiada bogate doświadczenie w zakresie wdrażania różnych technologii informatycznych. Od 2002 roku wykładowca Technologii i Protokołów Sieciowych na Podyplomowym Studium Politechniki Gdańskiej. Jest współautorem skryptu dla studentów informatyki: „Protokoły IPv6 – Opis protokołów. Materiały do laboratorium”. Posiada certyfikaty MCT, MCSE, MCSE+M, MCSE+S, MCTS Microsoft Exchange Server 2007, MCP ID 3621156.
 Do początku strony Do początku strony

Zarządzanie pamięcią w 32 i 64-bitowych systemach Windows, cz. I     Windows Server 2008     Zarządzanie pamięcią w 32 i 64-bitowych systemach Windows, cz. III