Чи Linux не використовує сегментацію, а лише пейджінг?


24

Інтерфейс програмування Linux показує макет віртуального адресного простору процесу. Чи кожна область на діаграмі є сегментом?

введіть тут опис зображення

З розуміння ядра Linux ,

чи правильно, що зазначено нижче, означає, що блок сегментації в MMU відображає сегменти та зсуви в межах сегментів у адресу віртуальної пам'яті, а блок підключення потім відображає адресу віртуальної пам'яті до фізичної адреси пам'яті?

Блок управління пам'яттю (MMU) перетворює логічну адресу в лінійну адресу за допомогою апаратної схеми, що називається сегментацією; згодом друга апаратна схема, яка називається підказовим пристроєм, перетворює лінійну адресу у фізичну адресу (див. малюнок 2-1).

введіть тут опис зображення

Тоді чому сказано, що Linux не використовує сегментацію, а лише пейджінг?

Сегментація включена в мікропроцесори розміром 80x86, щоб заохотити програмістів розділити свої програми на логічно пов'язані об'єкти, такі як підпрограми або глобальні та локальні області даних. Однак Linux використовує сегментацію дуже обмеженим чином. Насправді сегментація та підказка є дещо зайвими, тому що обидва можуть використовуватися для розділення просторів фізичних адрес процесів: сегментація може призначити різний лінійний адресний простір для кожного процесу, тоді як підказка може відображати один і той же лінійний простір адрес у різні фізичні простори адрес . Linux надає перевагу перейменуванню на сегментацію з наступних причин:

• Управління пам'яттю простіше, коли всі процеси використовують однакові значення реєстру сегментів, тобто коли вони мають однаковий набір лінійних адрес.

• Однією з цілей дизайну Linux є портативність для широкого кола архітектур; Зокрема, архітектури RISC мають обмежену підтримку сегментації.

Версія 2,6 Linux використовує сегментацію лише тоді, коли цього вимагає архітектура 80x86.


Чи можете ви вказати видання, будь ласка. Також може бути корисно вказати імена авторів. Я знаю, щонайменше перший - з видатної фігури. Однак обидва названі імена трохи загальні, мені спочатку було не ясно, що ти говориш про книги :-).
sourcejedi

2
Знову "Сегментація була включена в мікропроцесори 80x86 ...": Це просто не так. Це спадщина процесорів 808x, які мали 16-бітні покажчики даних та 64 Кб сегменти пам'яті. Покажчики сегментів дозволяли вам перемикати сегменти, щоб отримати більше пам'яті. Ця архітектура перенесена на 80x86 (розмір вказівника збільшився до 33 біт). На сьогоднішній день у моделі x86_64 у вас є 64 бітні покажчики, які можуть (теоретично - я думаю, що фактично використовуються лише 48 біт) адресувати 16 екзабайтів, тому сегменти не потрібні.
jamesqf

2
@jamesqf, добре, що 32-бітний захищений режим у 386 підтримує сегменти, які сильно відрізняються від 16-байтових масштабованих покажчиків, які вони мають у 8086, тому це не просто звичайна спадщина. Це, звичайно, нічого не сказати про їх корисність.
ilkkachu

@jamesqf 80186 мав таку ж модель пам'яті, що і 8086, без "33 біт"
Jasen

Немає гідної відповіді, отже, лише коментар: сегменти та сторінки порівнянні лише за умов обміну (наприклад, заміни сторінки на заміну сегментів), і в цьому контексті заміни сторінки просто вимикає заміну сегмента з води. Якщо ви поміняєте / вимикаєте сегменти, вам потрібно буде поміняти місцями цілий сегмент, який може бути 2-4 Гб. Це ніколи не було реальною справою для використання на x86. Зі сторінками ви завжди можете працювати на одиницях 4 КБ. Що стосується доступу до пам'яті, то сегменти та сторінки пов’язані через таблиці сторінок, а порівняння - це яблука з апельсинами.
vhu

Відповіді:


20

Архітектура x86-64 не використовує сегментацію в довгому режимі (64-бітний режим).

Чотири з сегментних регістрів: CS, SS, DS і ES змушені до 0, а межа до 2 ^ 64.

https://en.wikipedia.org/wiki/X86_memory_segmentation#Later_developments

В ОС більше не можна обмежувати, які діапазони "лінійних адрес" доступні. Тому він не може використовувати сегментацію для захисту пам'яті; вона повинна повністю покладатися на пейджінг.

Не турбуйтеся про деталі процесорів x86, які застосовуватимуться лише при запущеному 32-бітному режимі. Linux для 32-бітних режимів використовується не так багато. Це навіть може вважатися "в стані доброякісного знехтування протягом кількох років". Дивіться підтримку 32- бітової x86 у Fedora [LWN.net, 2017].

(Буває, що 32-розрядний Linux також не використовує сегментацію. Але мені не потрібно мені довіряти, ви можете просто проігнорувати це :-).


Це трохи завищення. base / limit встановлюються на 0 / -1 у тривалому режимі для застарілих сегментів original-8086 (CS / DS / ES / SS), але FS і GS все ще мають довільну сегментну базу. І дескриптор сегмента, завантажений у CS, визначає, чи працює ЦП у 32 чи 64-бітному режимі. І користувацький простір на x86-64 Linux використовує FS для локального зберігання потоків ( mov eax, [fs:rdi + 16]). Ядро використовує GS (після swapgs), щоб знайти поточний стек ядра процесу у syscallточці входу. Але так, сегментація не використовується як частина основного механізму управління пам'яттю / захисту пам'яті.
Пітер Кордес

Це в основному те, що цитата у питанні, що означає "версія 2.6 для Linux, використовує сегментацію лише тоді, коли цього вимагає архітектура 80x86". Але ваш другий абзац в основному неправильний. Linux використовує сегментацію в основному однаково в 32 та 64-бітних режимах. тобто база = 0 / межа = 2 ^ 32 або 2 ^ 64 для класичних сегментів (CS / DS / ES / SS), які неявно використовуються звичайними інструкціями. У 32-розрядному Linux-коді немає нічого зайвого; функціональність HW є, але не використовується.
Пітер Кордес

@PeterCordes ви в основному неправильно трактуєте відповідь :-). Тому я відредагував це, щоб спробувати зробити свій аргумент менш неоднозначним.
sourcejedi

Гарне поліпшення, тепер це не вводить в оману. Я повністю згоден з вашим реальним моментом, який полягає в тому, що ви можете і повинні повністю ігнорувати сегментацію x86, тому що вона використовується лише для речей управління системою osdev і для TLS. Якщо ви хочете врешті-решт дізнатися про це, це зрозуміти набагато простіше після того, як ви вже зрозуміли x86 asm з плоскою моделлю пам'яті.
Пітер Кордес

8

Оскільки x86 має сегменти, їх не можна використовувати. Але як cs(сегмент коду), так і ds(сегмент даних) базові адреси встановлені на нуль, тому сегментація насправді не використовується. Виняток становлять локальні дані потоку, один із звичайно невикористаних сегментів реєстру вказує на потокові локальні дані. Але це головним чином, щоб уникнути резервування одного з регістрів загального призначення для цього завдання.

Це не говорить про те, що Linux не використовує сегментацію на x86, оскільки це було б неможливо. Ви вже виділили одну частину, Linux використовує сегментацію дуже обмеженим чином . Друга частина - Linux використовує сегментацію лише тоді, коли цього вимагає архітектура 80x86

Ви вже цитували причини, пейджинги простіші та портативніші.


7

Чи кожна область на діаграмі є сегментом?

Ні.

Хоча система сегментації (в 32-бітному захищеному режимі на x86) призначена для підтримки окремих сегментів коду, даних та стеків, на практиці всі сегменти встановлені в одній області пам’яті. Тобто вони починаються з 0 і закінчуються в кінці пам'яті (*) . Це робить логічні та лінійні адреси рівними.

Це називається "плоскою" моделлю пам'яті і дещо простішою, ніж модель, де у вас є окремі сегменти, а потім вказівники всередині них. Зокрема, сегментована модель потребує більш довгих покажчиків, оскільки селекторний селектор повинен бути включений крім покажчика зміщення. (16-розрядний селектор + 32-бітове зміщення для загального 48-бітного покажчика; проти 32-бітного плоского вказівника.)

64-бітний довгий режим насправді навіть не підтримує сегментацію, окрім моделі з плоскою пам'яттю.

Якби ви програмували в 16-бітному захищеному режимі на 286, ви б мали більше потреби в сегментах, оскільки адресний простір становить 24 біти, але покажчики - лише 16 біт.

(* Зауважте, що я не можу пригадати, як 32-розрядний Linux обробляє поділ ядра / користувача. Сегментація дозволить це встановити обмеження сегментів простору користувача, щоб вони не включали простір ядра. Пейджинг дозволяє це, оскільки він забезпечує рівень захисту на сторінці.)

Тоді чому сказано, що Linux не використовує сегментацію, а лише пейджінг?

У x86 все ще є сегменти, і ви не можете їх відключити. Вони просто використовуються якомога менше. У 32-бітному захищеному режимі сегменти потрібно налаштувати для плоскої моделі, і навіть у 64-бітному режимі вони все ще існують.


Так, я думаю, що 32-розрядне ядро ​​могло б пом'якшити Meltdown дешевше, ніж змінювати таблиці сторінок, встановивши обмеження сегментів на CS / DS / ES / SS, які не дозволяють користувачеві простору отримати доступ вище 2G або 3G. (Вулкан Meltdown - це вирішення для біта ядра / користувача в записах таблиці таблиць, що дозволяє користувачу простір читати зі сторінок, які відображені лише для ядра). Сторінки VDSO можуть бути відображені у верхній частині 4G, хоча: / wrfsbaseнезаконне в захищеному / компактному режимі, лише тривалий режим, тому для 32-бітного простору користувача ядро ​​не могло встановити базу FS високою.
Пітер Кордес

У 64-бітному ядрі 32-розрядний простір користувача може потенційно далеко перейти до 64-розрядного кодового сегмента, тому ви не можете залежати від обмежень сегмента захисту Meltdown, тільки, можливо, у чистому 32-бітному ядрі. (Що має великі недоліки на машинах з великою кількістю фізичної оперативної пам’яті, наприклад, не вистачає пам'яті для стекових потоків.) У будь-якому випадку, так, Linux захищає пам’ять ядра під час пейджингу, залишаючи базовий / лімітний = 0 / -1 у просторі користувача для нормального сегменти (не FS / GS, які використовуються для локального зберігання потоків).
Пітер Кордес

До того, як біт NX був підтриманий у таблицях апаратних сторінок (PAE), деякі ранні виправлення безпеки використовували сегментацію для створення невиконаних стеків для коду простору користувача. наприклад, linux.com/news/exec-shield-new-linux-security-feature (у публікації Інго Молнара згадується "Відмінник" Сонячного дизайнера "" невиконаний патч стека "".)
Пітер Кордес

3

Linux x86 / 32 не використовує сегментацію в тому сенсі, що вона ініціалізує всі сегменти до однієї лінійної адреси та обмеження. x86 архітектура вимагає, щоб програма мала сегменти: код може бути виконаний лише з кодового сегмента, стек може бути розміщений лише в сегменті стека, даними можна керувати лише в одному з сегментів даних. Linux обходить цей механізм, встановлюючи всі сегменти однаково (за винятком, які ваша книга все одно не згадується), так що однакова логічна адреса є дійсною в будь-якому сегменті. Це насправді еквівалентно тому, що взагалі немає сегментів.


2

Чи кожна область на діаграмі є сегментом?

Це 2 майже абсолютно різних вживання слова "сегмент"

  • регістри сегментації / сегмента x86 : сучасні ОС x86 використовують плоску модель пам’яті, де всі сегменти мають однакову базу = 0 і ліміт = макс в 32-бітному режимі, те саме, що апаратне забезпечення примушує це в 64-бітному режимі , роблячи сегментацію видом вестигіального . (За винятком FS або GS, які використовуються для локального зберігання потоків навіть у 64-бітному режимі.)
  • Секції / сегменти Linker / Program-loader. ( Яка різниця розділу та сегмента у форматі файлу ELF )

Звичаї мають спільне походження: якщо ви були з використанням сегментированной моделі пам'яті (особливо без сторінкової віртуальної пам'яті), ви можете мати дані і BSS адреса бути щодо сегмента бази DS, стек по відношенню до основи SS і коду по відношенню до Базова адреса CS.

Таким чином, кілька різних програм можна завантажувати на різні лінійні адреси або навіть переміщувати після запуску, не змінюючи 16 або 32-бітових зсувів відносно бази сегментів.

Але тоді ви повинні знати, до якого сегмента вказівник є відносним, тому у вас є "далекі вказівники" тощо. (Фактично 16-бітовим програмам x86 часто не потрібно було отримувати доступ до свого коду як даних, тому можна було б десь використовувати 64-кодовий сегмент коду, а може бути й інший блок з 64k з DS = SS. внизу. Або крихітна модель коду з усіма базами сегментів рівними).


Як сегментація x86 взаємодіє з пейджингом

Відображення адреси в 32/64-бітному режимі:

  1. сегмент: зміщення (база сегмента, що має на увазі реєстр, що містить зсув, або переосмислюється префіксом інструкції)
  2. 32 або 64-бітна лінійна віртуальна адреса = база + зміщення. (У моделі з плоскою пам'яттю, як використовується Linux, покажчики / зсуви = лінійні адреси теж. За винятком доступу до TLS щодо FS або GS.)
  3. таблиці сторінок (кешована TLB) відображають фізичну адресу 32 (спадковий режим), 36 (старе PAE) або 52-бітну (x86-64) фізичну адресу. ( /programming/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-with-the ).

    Цей крок є необов’язковим: під час завантаження підключення потрібно активувати, встановивши трохи в регістрі керування. Без підкачки лінійні адреси - це фізичні адреси.

Зауважте, що сегментація не дозволяє використовувати більше 32 або 64 біт віртуального адресного простору в одному процесі (або потоці) , оскільки плоский (лінійний) адресний простір, на який все відображено, має лише таку ж кількість бітів, як і самі компенсації. (Це було не так для 16-розрядних x86, де сегментація насправді була корисною для використання понад 64 кб пам'яті з переважно 16-бітовими регістрами та компенсаціями.)


Процесор кешує дескриптори сегментів, завантажені з GDT (або LDT), включаючи базу сегмента. Коли ви відмежуєте покажчик, залежно від того, у якому реєстрі він знаходиться, він за замовчуванням відповідає або DS, або SS як сегмент. Значення регістру (покажчик) трактується як зміщення від бази сегмента.

Оскільки база сегмента зазвичай дорівнює нулю, ЦП це роблять в особливих випадках. Або з іншого боку, якщо ви робите маєте ненулевую базу сегмента, навантаження мають додаткову затримку , тому що «спеціальний» (нормальний) випадок обходу додавання базовий адреса не застосовується.


Як Linux налаштовує регістри сегмента x86:

База та межа CS / DS / ES / SS всі 0 / -1 у 32 та 64-бітному режимі. Це називається плоскою моделлю пам'яті, оскільки всі вказівники вказують на один і той же адресний простір.

(Архітектори процесора AMD нейтралізували сегментацію, застосувавши плоску модель пам'яті для 64-бітного режиму, оскільки основні ОС не використовували її, за винятком захисту no-exec, яка була забезпечена набагато кращим способом під час з'єднання з PAE або x86- 64 формат таблиці сторінок.)

  • TLS (Thread Local Storage): FS та GS не зафіксовані на базі = 0 у тривалому режимі. (Вони були новими з 386, і не використовуються неявно жодними інструкціями, навіть не- repстроковими інструкціями, які використовують ES). x86-64 Linux встановлює базову адресу FS для кожного потоку на адресу блоку TLS.

    наприклад, mov eax, [fs: 16]завантажує 32-бітове значення з 16 байт у блок TLS для цього потоку.

  • дескриптор сегмента CS вибирає, в якому режимі знаходиться процесор (16/32/64-бітний захищений режим / довгий режим). Linux використовує один запис GDT для всіх 64-розрядних процесів у просторі користувача та інший запис GDT для всіх 32-бітних процесів у просторі користувача. (Для того, щоб CPU працював правильно, DS / ES також повинні бути встановлені на дійсні записи, а також SS). Він також вибирає рівень привілеїв (ядро (кільце 0) проти користувача (кільце 3)), тому навіть при поверненні до 64-розрядного простору користувача ядро ​​все одно має організувати зміну CS, використовуючи iretабо sysretзамість звичайного інструкція зі стрибків чи повторів.

  • У x86-64 syscallточка входу використовує swapgsдля перевертання GS з GS-простору користувача до ядра, який він використовує для пошуку стека ядра для цього потоку. (Спеціалізований випадок локального зберігання потоків). syscallІнструкція не змінює покажчик стека на точку в стеці ядра; він все ще вказує на стек користувача, коли ядро ​​досягне точки входу 1 .

  • DS / ES / SS також повинні бути встановлені для дійсних дескрипторів сегмента для ЦП для роботи в захищеному режимі / довгому режимі, навіть якщо база / обмеження з цих дескрипторів ігноруються в довгому режимі.

Таким чином, сегментація x86 використовується як для TLS, так і для обов'язкових речей x86 osdev, які вимагає від вас обладнання.


Виноска 1: Історія розваг: існують архіви списку розсилки повідомлень між розробниками ядра та архітекторами AMD за пару років до випуску кремнію AMD64, що призвело до змін у дизайні, syscallщоб воно було корисним. Докладніше див. Посилання у цій відповіді .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.