Робота з адресним простором процесу з режиму користувача

Із використанням описаних алгоритмів виділення і вилучення інтервалів лінійних адрес реалізовано засоби роботи з адресним простором процесу з режиму користувача. До цих засобів належать системний виклик mmap(); його головним призначенням є реалізація відображуваної пам’яті, а також можливість організувати виділення регіонів пам’яті заданого розміру; функції та системні виклики керування динамічною пам’яттю (malloc() та інші).

Обробка сторінкових переривань

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

Коли переривання відбулося в режимі користувача, визначають, якому із регіонів пам’яті процесу відповідає лінійна адреса, що спричинила це переривання. За відсутності такого регіону процес завершують (відбулося звертання за невірною адресою). Крім того, процес завершують, якщо з’ясовується, що переривання викликане спробою записування в регіон, відкритий для читання. Завершення процесу здійснюють відсиланням йому сигналу SIGSEGV.

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

· За відсутності такої сторінки, якщо вона не завантажена в жодний фрейм, ядро створює новий фрейм і завантажує в нього сторінку – так реалізують завантаження сторінок на вимогу. Якщо процес не звертався до цієї сторінки жодного разу, створюють нову, заповнену нулями, тобто необхідну сторінку завантажують із диска.

· Коли така сторінка наявна, але позначена «тільки для читання», ядро створює новий фрейм і копіює в нього її дані – так реалізують технологію копіювання під час записування.

Реалізація завантаження сторінок на вимогу в Linux пов’язана з тим, що запити процесів на розподіл динамічної пам’яті (на відміну від запитів ядра) вважають не терміновими. Наприклад, під час завантаження процесу у пам’ять імовірність того, що йому негайно знадобляться всі сторінки із програмним кодом, мала. Беручи це до уваги, ядро намагається не завантажувати сторінки у пам’ять доти, поки вони не знадобляться.

Кількість сторінкових переривань за час виконання поточного процесу можна отримати за допомогою системного виклику getrusage():

#include <sys/resource.h>

struct rusage usage;

getrusage(RUSAGE_SELF. &usage); // інформація для поточного процесу

printf ("Усього сторінкових переривань: $d\n", usage.ru_majflt);

Керування пам’яттю під час створення і завершення процесів і потоків

У розділі 3 висвітлювалися відмінності між створенням процесів (fork()) і потоків (clone())) у Linux. Однією з найважливіших є відмінність у реалізації розподілу пам’яті.

Як відомо, системний виклик clone() приймає набір прапорців, що визначають ступінь розподілу ресурсів між потоками. Серед цих прапорців за керування пам’яттю відповідає прапорець CLONE_VM. Якщо його задано, під час створення нового потоку поле mm його керуючого блоку вказуватиме на той самий дескриптор пам’яті, що має потік (або процес), який його створив. Таким чином буде реалізовано загальний адресний простір для потоків одного процесу – усі вони використовуватимуть той самий дескриптор пам’яті.

Системний виклик fork() створює новий адресний простір процесу (при цьому жодну пам’ять не виділяють доти, поки процес не звернеться до адреси пам’яті). Спочатку створюють та ініціалізують новий дескриптор пам’яті, покажчик на нього зберігають у полі mm керуючого блоку процесу. Після цього усі дескриптори регіонів пам’яті предка додають у список регіонів пам’яті нащадка; крім того, копіюють всі таблиці сторінок предка, сторінки позначають «тільки для читання» – для предка і для нащадка (на основі цього працюватиме копіювання під час записування).

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

9.8.2. Організація заміщення сторінок

У цьому розділі розглянемо організацію заміщення сторінок у пам’яті. Відразу ж зазначимо, що в Linux реалізоване фонове заміщення сторінок, за яке відповідає потік ядра kswapd. Цей потік у колишніх версіях ядра запускався за перериванням від таймера кілька разів за секунду, у ядрі версії 2.6 необхідність його запуску визначає наявність вільних сторінок пам’яті у системі.

Списки сторінок

Організація заміщення сторінок у Linux ґрунтується на їх буферизації. Організовують два списки сторінок: список активних (active_list) – містить сторінки, які використовують процеси і визначає робочий набір процесів; список неактивних (inactive_list) – містить сторінки, які не так важливі для процесів (не використовуються в цей момент часу). Модифікована сторінка перебуває в списку неактивних якийсь час, перш ніж її збережуть на диску.

Нові сторінки додають у початок списку неактивних сторінок. За нестачі пам’яті частину сторінок переміщують з кінця списку активних сторінок у початок списку неактивних, а потім починають вивільнення сторінок із кінця списку неактивних сторінок (рис. 9.9).

Рис. 9.9. Списки сторінок менеджера віртуальної пам’яті Linux