Сторінкова пам'ять

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку

Сторінкова пам'ять — це підхід до організації віртуальної пам'яті, при якому одиницею відображення віртуальних адрес на фізичні є регіон константного розміру (так звана сторінка). Типовий розмір сторінки — 4096 байт, для деяких архітектур — до 128 Кб.

Підтримка такого режиму присутня в більшості 32-бітних та 64-бітних процесорів. Такий режим є класичним майже для усіх сучасних ОС, в тому числі Windows та сімейства UNIX. Широке використання такого режиму почалося з процесора VAX та ОС VMS з кінця 1970-х років. В сімействі x86 підтримка з'явилася з поколінням 386, першим 32-бітним поколінням.

Вирішувані проблеми[ред. | ред. код]

  • підтримка ізоляції процесів та захисту пам'яті шляхом створення власного віртуального адресного простору для кожного процесу
  • підтримка ізоляції області ядра від коду користувальницького режиму
  • підтримка пам'яті «тільки для читання» та невиконуваної пам'яті
  • підтримка відвантаження невикористовуваних сторінок в зону підкачування на диску (див. свопінг)
  • підтримка відображення файлів в пам'ять
  • підтримка роздільної між процесами пам'яті
  • підтримка системного виклику fork() в ОС сімейства UNIX

Концепція[ред. | ред. код]

Поняття адреси[ред. | ред. код]

Адреса, що використовується в машинному коді (тобто значення вказівника) називається «віртуальна адреса». Адреса, що процесор виставляє на шину називається «фізична адреса».

Таблиця сторінок[ред. | ред. код]

Всі звертання процесору до пам'яті підлягають трансляції через спеціальну структуру, таблицю TLB (Translation Lookaside Buffer), яка розташована в процесорі та є надшвидкою. В цій структурі міститься перетворення деяких (часто 64[джерело?]) віртуальних адрес в фізичні.

Через те, що 64 рядків таблиці недостатньо для реальних програм, в архітектурі використовуються таблиці сторінок, які розміщені в основній пам'яті. Кожна таблиця сторінок сама є сторінкою з тими же вимогами по вирівнюванню та розміром, та складається з записів таблиці даних (page table entries — PTE). Використовується відображення самої таблиці сторінок на одну зі сторінок даних для внесення змін у записи.[джерело?]

Запис таблиці сторінок[ред. | ред. код]

Запис таблиці сторінок зазвичай містить наступну інформацію:

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

Через те, що кількість записів в одній таблиці обмежена та залежить від розміру запису та розміру сторінки, використовується багаторівнева організація таблиць, часто 2 чи 3 рівня, іноді 4 (для 64-бітних архітектур). У випадку 2 рівнів використовується «директорія» сторінок, що має в собі записи, які вказують на фізичні адреси таблиць сторінок. Таблиці містять записи, які вказують вже на сторінки даних. У випадку 3 рівні з'являється ще й «супер-директорія», що містить записи, які вказують на декілька директорій.

Старші біти віртуальної адреси вказують на номер запису в директорії, середні — номер запису в таблиці, молодші йдуть вказують на фізичну адресу без трансляції.

Формат записів таблиць, їх розмір, розмір сторінки та організація таблиць залежать від типу процесора, а іноді і від режиму його роботи.

Сторінкова пам'ять в x86[ред. | ред. код]

Історично склалося так, що x86-процесори використовують 32-бітні PTE, 32-бітні віртуальні адреси, 4Кб сторінки, 1024 записів в таблиці, дворівневі таблиці, старші 10 біт віртуальної адреси — номер запису в директорії, наступні 10 — номер запису в таблиці, молодші 12 — адреса у сторінці. Починаючи з Pentium Pro, процесори архітектури x86 підтримують сторінки розміром 4 Мб.

Процесор в режимі Physical Address Extension та режимі x86_64 (long mode), використовує 64-бітні PTE (задіяні не усі біти фізичної адреси, від 36 в PAE до 48—56 в деяких x86_64), 32-бітні віртуальні адреси, 4Кб сторінки, 512 записів в таблиці, трирівневі таблиці з 4 директоріями та 4 записами в супер-директорії, старші 2 біта віртуальної адреси — номер запису в супер-директорії, наступні 9 — в директорії, наступні 9 — в таблиці.

Фізична адреса директорії або ж супер-директорії завантажена в один з керуючих регістрів процесора. При використанні PAE замість 4Мб сторінок використовуються сторінки в 2Мб.

В архітектурі x86_64 можна використовувати сторінки розміром 4Кб (4096 байтів), 2Мб (2 097 152 байта), і 1Гб (1 073 741 824 байта).

Відмова сторінки (page fault)[ред. | ред. код]

Якщо звернення до пам'яті не може бути відтрансльоване через TLB, тоді мікрокод процесора звертається до таблиць сторінок та намагається завантажити PTE звідти в TLB. Якщо після такої спроби залишилися проблеми, тоді процесор виконує спеціальне переривання, з назвою «відмова сторінки» або англ. «page fault». Опрацьовувач цього переривання знаходиться в підсистемі віртуальної пам'яті ядра ОС.

Деякі процесори (MIPS) не мають мікрокоду для звертання до таблиць та генерують відмову сторінки одразу після невдалого пошуку в TLB, звертання до таблиці та його інтерпретація покладається вже на оброблювач відмови сторінки. Це усуває вимогу до таблиці сторінок відповідати жорстко заданому на рівні апаратури формату.

Причини відмов сторінок:

  • не існує таблиці, що відображує потрібний регіон пам'яті
  • PTE не має встановленого прапорця «сторінка відображена»
  • спроба звернутися з користувацького режиму до сторінки «тільки для ядра»
  • спроба запису в сторінку «тільки для читання»
  • спроба виконання коду із сторінки «виконання заборонене»

Оброблювач відмов в ядрі може завантажити потрібну сторінку з файлу або з зони підкачування (див. свопінг), може створити доступну для запису копію сторінки «тільки для читання», а може й згенерувати в даному процесі виняток "помилка сегментації" (в термінах UNIX — сигнал SIGSEGV).

Таблиці сторінок процесів[ред. | ред. код]

Кожен процес має свій власний набір таблиць сторінок. Регістр директорї сторінок перевантажується при кожному переключенні контексту процесу. Також необхідно очистити ту частину TLB, яка відноситься до цього процесу.

В більшості випадків ядро ОС поміщається в той же адресний простір, що і процеси, для нього резервуються верхні 1-2 гігабайта 32-бітного адресного простору кожного процесу. Метою цих дій є запобігання переключенню таблиць сторінок при вході в ядро на виході з нього. Сторінки ядра позначаються як недоступні для коду режиму користувача.

Пам'ять регіону ядра часто однакова для всіх процесів, але деякі підрегіони ядра (наприклад, регіон, де знаходиться підсистема графіки та відео-драйвер) можуть бути різним для різних груп процесів.

Тому що пам'ять ядра однакова для усіх процесів, відповідні до неї записи в TLB не треба перезавантажувати після переключення процесу. Для цієї оптимізації архітектура x86 підтримує прапорець «глобальний» у PTE.

Файли, відображені в пам'ять[ред. | ред. код]

Оброблювач відмови сторінки в ядрі здатен прочитати потрібну сторінку з файлу.

Це створює можливість легкої оптимізації відображення в пам'ять файлів. Концептуально це теж саме, що виділення пам'яті та читання в неї відрізка файлу, з тією різницею, що читання здійснюється неявно «за вимогою», вираженому відмовою сторінки при намаганні звертання до неї.

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

Третьою перевагою є можливість «забування» (discard) деяких відображених сторінок без завантаження їх в зону підкачування, обов'язкової для виділеної пам'яті. В випадку повторної потреби в сторінці вона може бути швидко завантажена із файлу знов.

Четверта перевага — не-використання дискового кешу в цьому режимі, а це економія при копіюванні даних з кешу в потрібний регіон. Переваги дискового кешу, який оптимізує операції невеликого розміру, а також повторне читання одних й тих самих же даних, повністю зникають при читанні цілих сторінок і тим більше їхніх груп. Недоліки від обов'язкового зайвого копіювання залишаються.

Відображені в пам'ять файли використовують в ОС Windows, а також ОС сімейства UNIX, для завантаження виконуваних модулів та динамічних бібліотек. Вони ж використовуються утилітою GNU grep для читання вхідного файлу[джерело?], а також для завантаження шрифтів в низці графічних підсистем.

Сторінкова та сегментна віртуальна пам'ять[ред. | ред. код]

Величезною перевагою сторінкової віртуальної пам'яті, зрівнюючи з сегментною — це відсутність «ближніх» та «дальніх» вказівників.

Наявність таких концепцій в програмуванні зменшує можливість застосування арифметики вказівників та приводить до великих проблем з переносимістю коду на інші архітектури. так, наприклад, значна частина ПО з відкритим кодом спочатку розроблювалась для безсегментних 32-бітних платформ зі сторінковою пам'яттю і не може бути перенесена на сегментні архітектури без серйозних змін.

Крім того, сегментні архітектури мають важку проблему SS != DS (Stack Segment та Data Segment — сегментні регістри x86), яка була розповсюджена на початку 90-х роках в програмуванні під 16-бітні версії Windows. Ця проблема приводила до складнощів в реалізації динамічних бібліотек, бо вони мають свій DS та SS поточного процесу, що приводить до неможливості використання «ближніх» вказівників в них.

Віртуальна пам'ять та дисковий кеш[ред. | ред. код]

Докладніше: Сторінковий кеш

Підтримка файлів, відображених в пам'ять потребує підтримки ядром ОС структури «сукупність фізичних сторінок, що містять в собі відрізки даного файлу». Відображення файлу в пам'ять реалізується шляхом заповнення входів таблиць посиланнями на сторінки даної структури.

Зрозуміло, що дана структура є вже готовим дисковим кешем. Її використання як кешу також розв'язує проблему когерентності між файлом, доступним через системні виклики read/write, та цим же файлом, відображеним в пам'ять.

Шляхи кешованого вводу/виводу в дисковий файл (FsRtlCopyRead в Windows і аналогічна до неї generic_file_read() в Linux), реалізуються як копіювання даних у фізичні сторінки, відображені на файл.

Така організація кешу є єдиною в Windows, бо ця ОС взагалі не має класичного блокового дискового кешу. Метадані файлових систем кешуються шляхом створення псевдофайлів (IoCreateStreamFileObject) та створення сторінкового кешу для них.

Безпека[ред. | ред. код]

Спочатку, архітектура x86 не мала прапорця «сторінка недоступна на виконання» (NX bit). Підтримка цього прапорця з'явилася в архітектурі x86 як частина режиму PAE (Physical Address Extension) в поколінні Pentium 4, під великим тиском з сторони спеціалістів з комп'ютерної безпеки. Установка цього прапорця на сторінках стека та купи дозволяє реалізувати апаратний захист від виконання даних, що робить неможливим роботу багатьох різновидів шкідливого ПЗ, в тому числі, використання багатьох дірок в Internet Explorer.

Підтримка PAE в Windows, що дає можливість включення захисту від виконання даних, з'вилася в Windows 2000, вона увімкнена «за замовчуванням» в серверній версії Windows та вимкнена в клієнтських.

Див. також[ред. | ред. код]