Linux Programming Interface pokazuje układ wirtualnej przestrzeni adresowej procesu. Czy każdy region na diagramie jest segmentem?
Z zrozumienia jądra systemu Linux ,
czy to prawda, że poniższe oznacza, że jednostka segmentacji w MMU mapuje segmenty i przesunięcia w segmentach na adres pamięci wirtualnej, a jednostka przywoławcza następnie mapuje adres pamięci wirtualnej na adres pamięci fizycznej?
Jednostka zarządzania pamięcią (MMU) przekształca adres logiczny w adres liniowy za pomocą obwodu sprzętowego zwanego jednostką segmentacji; następnie drugi obwód sprzętowy zwany jednostką przywoławczą przekształca adres liniowy w adres fizyczny (patrz rysunek 2-1).
Dlaczego więc mówi, że Linux nie używa segmentacji, a jedynie stronicowania?
W mikroprocesorach 80x86 zastosowano segmentację, aby zachęcić programistów do podzielenia aplikacji na logicznie powiązane podmioty, takie jak podprogramy lub globalne i lokalne obszary danych. Jednak Linux używa segmentacji w bardzo ograniczony sposób. W rzeczywistości segmentacja i stronicowanie są nieco redundantne, ponieważ obie można wykorzystać do oddzielenia fizycznych przestrzeni adresowych procesów: segmentacja może przypisać inną liniową przestrzeń adresową do każdego procesu, podczas gdy stronicowanie może odwzorować tę samą liniową przestrzeń adresową na różne fizyczne przestrzenie adresowe . Linux woli stronicowanie od segmentacji z następujących powodów:
• Zarządzanie pamięcią jest prostsze, gdy wszystkie procesy używają tych samych wartości rejestru segmentów - to znaczy, gdy dzielą ten sam zestaw adresów liniowych.
• Jednym z celów projektowych Linuxa jest możliwość przenoszenia go na wiele różnych architektur; W szczególności architektury RISC mają ograniczoną obsługę segmentacji.
Wersja 2.6 Linuksa wykorzystuje segmentację tylko wtedy, gdy wymaga tego architektura 80x86.
Odpowiedzi:
Architektura x86-64 nie wykorzystuje segmentacji w trybie długim (tryb 64-bitowy).
Cztery rejestry segmentów: CS, SS, DS i ES są wymuszone na 0, a limit na 2 ^ 64.
https://en.wikipedia.org/wiki/X86_memory_segmentation#Later_developments
OS nie może już ograniczać dostępnych zakresów „adresów liniowych”. Dlatego nie może używać segmentacji do ochrony pamięci; musi polegać wyłącznie na stronicowaniu.
Nie martw się o szczegóły procesorów x86, które miałyby zastosowanie tylko w starszych wersjach 32-bitowych. Linux dla trybów 32-bitowych nie jest tak często używany. Można nawet uznać to za „stan łagodnego zaniedbania przez kilka lat”. Zobacz 32-bitową obsługę x86 w Fedorze [LWN.net, 2017].
(Zdarza się, że 32-bitowy Linux również nie korzysta z segmentacji. Ale nie musisz mi w tym ufać, możesz to po prostu zignorować :-).
źródło
mov eax, [fs:rdi + 16]
). Jądro używa GS (poswapgs
), aby znaleźć stos jądra bieżącego procesu wsyscall
punkcie wejścia. Ale tak, segmentacja nie jest używana jako część głównego mechanizmu zarządzania pamięcią systemu operacyjnego / ochrony pamięci.Ponieważ x86 ma segmenty, nie można ich nie używać. Jednak adresy podstawowe
cs
(segment kodu) ids
(segment danych) są ustawione na zero, więc segmentacja nie jest tak naprawdę używana. Wyjątkiem są lokalne dane wątków, jeden z normalnie nieużywanych rejestrów segmentów wskazuje na lokalne dane wątków. Ma to jednak przede wszystkim na celu uniknięcie rezerwowania jednego z rejestrów ogólnego przeznaczenia do tego zadania.Nie mówi, że Linux nie używa segmentacji na x86, ponieważ nie byłoby to możliwe. Podkreśliłeś już jedną część, Linux używa segmentacji w bardzo ograniczony sposób . Druga część to Linux korzysta z segmentacji tylko wtedy, gdy wymaga tego architektura 80x86
Podałeś już powody, stronicowanie jest łatwiejsze i bardziej przenośne.
źródło
Nie.
Podczas gdy system segmentacji (w 32-bitowym trybie chronionym na x86) jest zaprojektowany do obsługi oddzielnych segmentów kodu, danych i stosu, w praktyce wszystkie segmenty są ustawione w tym samym obszarze pamięci. Oznacza to, że zaczynają się od 0, a kończą na końcu pamięci (*) . Dzięki temu adresy logiczne i adresy liniowe są równe.
Nazywa się to „płaskim” modelem pamięci i jest nieco prostsze niż model, w którym masz wyraźne segmenty, a następnie wskaźniki. W szczególności model segmentowy wymaga dłuższych wskaźników, ponieważ selektor segmentu musi być dołączony oprócz wskaźnika przesunięcia. (16-bitowy selektor segmentów + 32-bitowe przesunięcie, co daje 48-bitowy wskaźnik; w porównaniu z 32-bitowym płaskim wskaźnikiem).
Tryb 64-bitowy tak naprawdę nie obsługuje segmentacji innej niż płaski model pamięci.
Jeśli miałbyś programować w 16-bitowym trybie chronionym na 286, będziesz potrzebował więcej segmentów, ponieważ przestrzeń adresowa ma 24 bity, ale wskaźniki to tylko 16 bitów.
(* Pamiętaj, że nie pamiętam, jak 32-bitowy system Linux obsługuje separację jądra / przestrzeni użytkownika. Segmentacja umożliwiłaby to poprzez ustawienie limitów segmentów przestrzeni użytkownika, aby nie obejmowały przestrzeni jądra. Stronicowanie pozwala na to, ponieważ zapewnia poziom ochrony na stronę).
X86 nadal ma segmenty i nie można ich wyłączyć. Są one używane tak mało, jak to możliwe. W 32-bitowym trybie chronionym segmenty muszą być skonfigurowane dla modelu płaskiego, a nawet w trybie 64-bitowym nadal istnieją.
źródło
wrfsbase
jest nielegalny w trybie chronionym / kompatybilnym, tylko w trybie długim, więc w 32-bitowej przestrzeni użytkownika jądra nie można ustawić wysokiej bazy FS.Linux x86 / 32 nie używa segmentacji w tym sensie, że inicjuje wszystkie segmenty do tego samego adresu liniowego i limitu. Architektura x86 wymaga, aby program miał segmenty: kod może być wykonywany tylko z segmentu kodu, stos może znajdować się tylko w segmencie stosu, dane mogą być przetwarzane tylko w jednym z segmentów danych. Linux omija ten mechanizm, ustawiając wszystkie segmenty w ten sam sposób (z wyjątkami, o których i tak nie wspomina książka), aby ten sam adres logiczny był poprawny w dowolnym segmencie. W rzeczywistości jest to równoważne z brakiem segmentów.
źródło
Są to 2 prawie całkowicie różne zastosowania słowa „segment”
Zwyczaje mają wspólne pochodzenie: jeśli były przy użyciu segmentową modelu pamięci (zwłaszcza bez stronicowanej pamięci wirtualnej), możesz mieć dane i adresy BSS być w stosunku do podstawy DS segmentu, stos względem podstawy SS i kod względem Adres bazowy CS.
Tak więc wiele różnych programów można załadować do różnych adresów liniowych, a nawet przenieść po uruchomieniu, bez zmiany 16 lub 32-bitowych przesunięć względem baz segmentów.
Ale wtedy musisz wiedzieć, do którego segmentu odnosi się wskaźnik, więc masz „dalekie wskaźniki” i tak dalej. (Rzeczywiste 16-bitowe programy x86 często nie musiały uzyskiwać dostępu do swojego kodu jako danych, więc mogłyby gdzieś użyć segmentu kodu 64k, a może innego bloku 64k z DS = SS, z rosnącym stosem z wysokich offsetów i danymi w na dole lub mały model kodu z równymi podstawami segmentów).
Jak segmentacja x86 współdziała ze stronicowaniem
Mapowanie adresów w trybie 32/64-bitowym to:
tabele stron (buforowane przez TLB) mapują liniowo na 32 (starszy tryb), 36 (starszy PAE) lub 52-bitowy (x86-64) adres fizyczny. ( /programming/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-with-the ).
Ten krok jest opcjonalny: stronicowanie musi być włączone podczas uruchamiania poprzez ustawienie bitu w rejestrze kontrolnym. Bez stronicowania adresy liniowe są adresami fizycznymi.
Zauważ, że segmentacja nie pozwala na użycie więcej niż 32 lub 64 bitów wirtualnej przestrzeni adresowej w jednym procesie (lub wątku) , ponieważ płaska (liniowa) przestrzeń adresowa, na którą wszystko jest mapowane, ma tylko taką samą liczbę bitów jak same przesunięcia. (Nie miało to miejsca w przypadku 16-bitowego x86, w którym segmentacja była faktycznie przydatna do użycia więcej niż 64k pamięci z przeważnie 16-bitowymi rejestrami i przesunięciami.)
Procesor buforuje deskryptory segmentów ładowane z GDT (lub LDT), w tym bazę segmentu. Po wyrejestrowaniu wskaźnika, w zależności od tego, w jakim rejestrze się znajduje, domyślnie jest to DS lub SS jako segment. Wartość rejestru (wskaźnik) jest traktowana jako przesunięcie względem podstawy segmentu.
Ponieważ podstawa segmentu wynosi zwykle zero, procesory robią to w szczególnych przypadkach. Albo z innej perspektywy, jeśli zrobić mają niezerową bazowy segmentu, ładunki mają dodatkowe opóźnienia, ponieważ „specjalne” (normalny) przypadek pominięciem dodając adres bazowy nie ma zastosowania.
Jak Linux konfiguruje rejestry segmentów x86:
Podstawa i limit CS / DS / ES / SS to 0 / -1 w trybie 32 i 64-bitowym. Nazywa się to płaskim modelem pamięci, ponieważ wszystkie wskaźniki wskazują na tę samą przestrzeń adresową.
(Architekci procesorów AMD zneutralizowali segmentację poprzez wymuszenie płaskiego modelu pamięci dla trybu 64-bitowego, ponieważ systemy operacyjne głównego nurtu i tak go nie używały, z wyjątkiem ochrony przed brakiem wykonania, która została zapewniona w znacznie lepszy sposób dzięki stronicowaniu z PAE lub x86- Format 64 stronicowania).
TLS (lokalna pamięć wątków): FS i GS nie są ustalone w bazie = 0 w trybie długim. (Były nowe z wersją 386 i nie były domyślnie używane przez żadne instrukcje, nawet
rep
instrukcje-string zawierające ES). Linux x86-64 ustawia adres podstawowy FS dla każdego wątku na adres bloku TLS.np.
mov eax, [fs: 16]
ładuje 32-bitową wartość z 16 bajtów do bloku TLS dla tego wątku.deskryptor segmentu CS wybiera tryb, w którym znajduje się CPU (tryb chroniony 16/32/64-bit / tryb długi). Linux używa pojedynczego wpisu GDT dla wszystkich 64-bitowych procesów w przestrzeni użytkownika i innego wpisu GDT dla wszystkich 32-bitowych procesów w przestrzeni użytkownika. (Aby procesor działał poprawnie, DS / ES również musi mieć ustawione prawidłowe wpisy, podobnie jak SS). Wybiera również poziom uprawnień (jądro (pierścień 0) vs. użytkownik (pierścień 3)), więc nawet po powrocie do 64-bitowej przestrzeni użytkownika, jądro nadal musi zorganizować zmianę CS, używając
iret
lubsysret
zamiast normalnego instrukcja skoku lub ret.W x86-64
syscall
punkt wejścia używaswapgs
do odwrócenia GS z GS przestrzeni użytkownika do jądra, którego używa do znalezienia stosu jądra dla tego wątku. (Specjalny przypadek lokalnego przechowywania wątków).syscall
Instrukcja nie zmienia wskaźnik stosu do punktu na stosie jądra; nadal wskazuje na stos użytkowników, gdy jądro osiąga punkt wejścia 1 .DS / ES / SS również muszą być ustawione na prawidłowe deskryptory segmentów, aby procesor działał w trybie chronionym / trybie długim, nawet jeśli podstawa / limit tych deskryptorów jest ignorowany w trybie długim.
Zasadniczo do TLS stosowana jest segmentacja x86, a do obowiązkowych rzeczy w osdev x86, których wymaga sprzęt.
Przypis 1: Zabawna historia: istnieją archiwa list mailingowych wiadomości między deweloperami jądra a architektami AMD sprzed kilku lat przed wydaniem krzemu AMD64, co spowodowało poprawki w projekcie
syscall
tak, aby był użyteczny. Szczegółowe informacje znajdują się w linkach w tej odpowiedzi .źródło