Як ядро керує пам’яттю
Раніше ми побачили, як організована віртуальна пам'ять процесу. Тепер розглянемо механізми, завдяки яким ядро керує пам'яттю. Звернемося до нашої програми:

У Windows, блок EPROCESS - це грубо кажучи щось середнє між структурами task_struct і mm_struct. Аналогом дескриптора області віртуальної пам'яті є Virtual Address Descriptor або VAD, інформація про ці дескриптори зберігається в AVL-дереві. Знаєте що найсмішніше при порівнянні Windows та Linux? Це те, що відмінностей якраз не так багато.
Захист пам'яті здійснюється на посторінковій основі, оскільки сторінка - це найменший "шматочок" пам'яті, для якого можна виставити прапори "U/S" та "R/W". Варто однак враховувати, що теоретично дві різні віртуальні сторінки, що мають набір прапорів, що відрізняються, можуть відповідати одній і тій же фізичній сторінці. Зауважте, у форматі page table запису не передбачені прапори, пов'язані із забороною виконання коду. Саме тому класичний x86-пейджинг ніяк не перешкоджає виконанню коду в стеку, що полегшує експлуатацію вразливостей, в основі яких переповнення буфера в стеку (невиконані стеки все одно схильні до уразливостей, в даному випадку використовується техніка return-to-libc та інші прийоми). Відсутність no-execute прапора також свідчить про інший важливий аспект: прапори доступу, що містяться в дескрипторі VMA, не завжди мають прямі відповідності в системі захисту, що реалізується процесором, і можуть лише відповідати цій системі більшою чи меншою мірою. Образно кажучи, ядро робить все, що в його силах, але зрештою архітектура процесора накладає свої обмеження на те, що можна продати.

Ядрі Linux веде облік кожного page-фрейму за допомогою спеціальногодескриптора та кількох прапорів. Взяті разом ці дескриптори описують всю оперативну пам'ять комп'ютера; у кожний момент часу відомий точний стан будь-якого page-фрейму. В основі управління фізичною пам'яттю лежить алгоритм Buddy memory allocation. Таким чином, page-фрейм вважається вільним, якщо він доступний для виділення з погляду Buddy-алгоритму. Виділений під використання page-фрейм може бути "анонімним" (у такому випадку він містить дані програми) або може перебувати в т.зв. сторінковий кеш (page cache) і зберігати порцію даних з деякого файлу або блокового пристрою. Існують і інші, більш екзотичні варіанти використання page-фреймів, але не давайте їх зараз чіпати. У Windows є аналогічна структура для обліку page-фреймів і називається база даних Page Frame Number. А тепер, давайте зібрамо воєдино всі ці концепції - області віртуальної пам'яті (VMA), page table-записи та page-фрейми - і подивимося як це працює. Далі йде приклад купи в user space програми:

Прямокутники з блакитним фоном позначають віртуальні сторінки, що знаходяться в межах віртуальної пам'яті. Стрілки позначають page table-записи, за допомогою яких віртуальні сторінки "меппуються" в page-фреми (фізичні сторінки). Деякі віртуальні сторінки не мають стрілок; це означає, що у відповідних їм page table записах прапор присутності встановлений у 0. Причиною тому може бути те, що дані віртуальні страйки можливо ще не використовувалися разу або тому, що відповідні їм фізичні сторінки були вивантажені в своп. У будь-якому випадку, спроба доступу до цих сторінок призведе до page fault, навіть незважаючи на те, що віртуальні сторінки знаходяться в межах деякої VMA. Може здатися дивним, щоіснує подібного роду різночитання – сторінки в межах VMA, проте доступ до них є невалідним – але так дійсно часто відбувається.
VMA є свого роду “контракт” між програмою та ядром. Ви просите ядро виконати якусь дію (наприклад, виділити пам'ять або замеппувати файл), ядро каже “без проблем” і створює нову чи оновлює існуючу VMA. Але ядро при цьому не поспішає виконувати саме прохання, натомість ядро відкладе безпосереднє виконання запитаної дії до того моменту, поки не станеться page fault. Так, виходить, що ядро - це такий собі "ледачий негідник"; це є основним принципом управління віртуальною пам'яттю. Даний принцип застосовується в більшості ситуацій – деякі з них можуть бути цілком знайомими, деякі – несподіваними, але загальне правило такого, що VMA лише фіксує, про що було домовлено, тоді як page table-записи відображають те, що безпосередньо було зроблено лінивим ядром. Ці дві структури разом беруть участь у управлінні пам'яттю програми; обидві структури відіграють певну роль при обробці page fault, вивільненні пам'яті, вивантаженні сторінок у своп і т.д. Розглянемо простий випадок виділення пам'яті:

Припустимо, VMA таки знайшлася. Подальша обробка page fault така – ядро дивиться на вміст page table запису та тип VMA. У прикладі, page table запис свідчить у тому, що сторінки у пам'яті немає. Більше того, наш запис абсолютно порожній (складається з одних нулів), і в Linux це означає, що відповідна віртуальна сторінка взагалі ще жодного разу не була замеппірована. Оскільки ми маємо справу з "анонімною" VMA, то всі подальші дії будуть пов'язані тільки з оперативною пам'яттю і для обробки цієї ситуації викликаєтьсяфункція do_anonymous_page(). Ця функція здійснює виділення page-фрейму і меппирует до нього віртуальну сторінку шляхом внесення потрібних даних у page table запис.
Справа могла й інакше. Page table запис для вивантаженої у своп сторінки, наприклад, має прапор присутності встановлений нуль, але решта запису не порожня. Інші біти зберігають інформацію про знаходження сторінки у свопі. Функція do_swap_page() зчитує вміст цієї сторінки з диска та завантажує сторінку в оперативну пам'ять – це тип page fault називають major fault.
На цьому завершимо першу частину нашого екскурсу, як ядро управляє пам'яттю. У наступній статті ми ускладнимо картину, доповнивши її роботою з файлами, щоб у нас створилося повне уявлення про основні концепції управління пам'яттю, включаючи деякі аспекти продуктивності.