Сторінкова пам'ять — це підхід до організації віртуальної пам'яті, при якому одиницею відображення віртуальних адрес на фізичні є регіон константного розміру (так звана сторінка). Типовий розмір сторінки — 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 та вимкнена в клієнтських.

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