Чи кожна область на діаграмі є сегментом?
Це 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-бітному режимі:
- сегмент: зміщення (база сегмента, що має на увазі реєстр, що містить зсув, або переосмислюється префіксом інструкції)
- 32 або 64-бітна лінійна віртуальна адреса = база + зміщення. (У моделі з плоскою пам'яттю, як використовується Linux, покажчики / зсуви = лінійні адреси теж. За винятком доступу до TLS щодо FS або GS.)
таблиці сторінок (кешована 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
щоб воно було корисним. Докладніше див. Посилання у цій відповіді .