Як мій комп’ютер утримує процеси від наступання один на одного?
Планувальник ядра слідкує за поділом часу між процесами. Ваша оператційна система також має поділити їх у просторі, щоб процеси не могли «наступати» чужу область оперативної пам’яті. Навіть якщо припустити, що всі програми зроблені так, щоб працювати в купі із «сусідами», ви ж не хочете, щоб помилка в одній з них призвела до неправильної роботи інших. Все, що робить операційна система для вирішення цих проблем називається керуванням пам’яттю^1 .
Кожен процес у вашому зоопарку повинен мати свою ділянку оперативної пам’яті для того, щоб запустити якось свій код, щоб тримати десь дані, які він обробляє. Робоча пам’ять процесу складається з двох областей: область коду із сегментом коду програми, що виконується (доступна процесові тільки для читання) та область даних (доступна як для читання, так і для запису). Кожен процес має свою окрему область даних, але кілька процесів будуть використовувати ту саму область коду, якщо всі ці процеси — одна і та ж програма на диску (наприклад, дві чи три запущені переглядалки *.pdf, або кілька емуляторів терміналу). Така оптимізація — дуже ефективне рішення в роботі з оперативною пам’яттю.
Віртуальна пам’ять: в двох словах
Ефективне тому, що пам’ять коштує гроші. Іноді Вам може її не вистачати для тощо, щоб вмістити всі запущені програми, особливо якщо ви використовуєте великі програми, як, наприклад, сучасні графічні стільниці. Для того, щоб обійти це, Юнікс використовує так звану віртуальну пам’ять. Він і не пробує помістити в оперативку всі програми, які Ви запустили. Він утримує в пам’яті тільки ті дані, які використовуються в цей момент. Всі інші залишаються у спеціальному місці на вашому диску, що називається обмінник^2 .
Зверніть увагу, що колись формулювання «іноді» з попереднього абзацу читалось як «майже завжди». На сучасних комп’ютерах можна запустити Х-сервер та типовий набір програм практично не використовуючи обмінник, тому що всі вони легко помістяться в оперативній пам’яті.
Віртуальна пам’ять: детальне пояснення
Фактично, у попередньому параграфі реальний стан речей дещо занадно спрощено. Так, програми бачать більшість оперативної пам’яті, як один великий плоский банк адрес, зазвичай більший, ніж сумарна кількість фізично наявної оперативної пам’яті. Цього операційна система досягає задопомогою обмінника на твердому диску, а також шляхом об’єднаня спільних блоків пам’яті, що містять один і той сам виконуваний код. Проте насправді Ваш комп’ютер має не менше 5 різних типів пам’яті і різниця між ними може мати дуже велике значення, коли з’явиться потреба налаштувати певні програми на максимальну швидкодію. Для того, щоб справді зрозуміти, що відбувається у Вашій машині, Ви повинні знати принципи роботи їх усіх.
Цими п’ятьма типами пам’яті є: регістри процесора, внутрішній (на чіпі) кеш процесора, зовнішній кеш процесора, основна (оперативна) пам’ять та диск. Причина такого різноманіття проста: швидкість коштує грошей. Ці види пам’яті перелічено у порядку спадання швидкості та вартості. Пам’ять регістрів є найшвидшою та найдорожчою: до неї можна звертатись у довільному порядку мільярд (а то й більше) разів за секунду, в той час як диски є найповільнішими та найдешевшими і забезпечують близько сотні довільних звертань за секунду.
Наведемо список швидкостей для типового домашнього комп’ютера, типового для початку 2000-х. І хоча швидкості і об’єми зростатимуть, а ціни падатимуть, співвідношення між ними, найвірогідніше, зберігатиметься, і саме це співвідношення формує ієрархію пам’яті.
- Диск : Розмір: 10 ГБайт, доступ: 100 кБ/сек.
- Оперативна пам’ять : Розмір: 256 МБайт, доступ: 100 МБ/сек.
- Зовнішній кеш : Розмір: 512 кБайт, доступ: 250 МБ/сек.
- Внутрішній кеш : Розмір: 32 кБайт, доступ: 500 МБ/сек.
- Регістри процесора : Розмір: 28 байт^8 , доступ: 1ГБ/сек.
Неможливо будувати все на найшвидшому типі пам’яті – така система буде надто дорогою. І навіть якби це було не так, швидка пам’ять є нестійкою: вона втрачає всі дані при вимкненні електроенергії. Тому комп’ютери повинні бути обладнані жорсткими дисками або іншими типами пам’яті, що може зберігати дані при вимкненому живленні. З іншого боку, є величезна невідповідність між швидкістю процесорів та швидкістю дисків. Три середніх рівні ієрархії пам’яті (внутрішній та зовнішній кеш і оперативна пам’ять) в основному існують, щоб компенсувати цю різницю.
Лінукс, як і інші Юнікси, має таку особливість, як віртуальна пам’ять. Це означає, що операційна система поводиться так, ніби вона має значно більше оперативної пам’яті, аніж насправді. Ваша фактична оперативна пам’ять служить ніби вікном до більшого віртуального простору пам’яті, частина якої може знаходитись в самій же оперативній пам’яті, а частина — у місці на диску, що називається областю обміну або обмінником. Ядро пересуває блоки пам’яті (їх називають сторінками) між оперативною пам’яттю та диском потай від користувацьких програм, щоб підтримати цю ілюзію. Результатом цього є те, що у Вас стає значно більше віртуальної пам’яті, аніж фізичної, і працює вона не надто повільніше за фізичну.
Те, наскільки віртуальна пам'ять є повільнішою за фізичну залежить від алгоритмів обміну операційної системи і того, наскільки вони підходять тій чи іншій програмі. На щастя, операції зчитування та запису пам’яті, котрі відбуваються близько в часі, мають тенденцію кластеризуватись (групуватись) в просторі пам’яті. Ця тенденція називається локальністю^3 , або, точніше, локальністю звертань^4 . Завдяки цьому ядру значно легше пересувати сторінки пам’яті між диском і оперативною пам’яттю. Якщо ж звернення до віртуальної пам’яті відбуваються в різних, розкиданих по пам’яті місцях, багато потрібної в конкретний момент пам’яті може знаходитись в обміннику на диску, і для таких зверненнь віртуальна пам’ять може стати такою ж повільною, як і диск. Але насправді програми зберігають щільне розташування своєї робочої пам’яті, тому операційна система не повинна буде здійснювати надто багато звертань до диска.
Як показує досвід, найбільш ефективний метод використання пам’яті для більшості програм є дуже простим; він називається LRU^5 . Коли якійсь програмі потрібно виділити пам’ять для роботи але фізична пам’ять уже зайнята, ядро переносить найдавніше вживані блоки інших програм (або навіть тієї самої програми) з оперативної пам’яті на диск, і програма отримує щойно звільнене місце в швидкій оперативній пам’яті для своєї роботи, в той час як пам’ять, котра зараз не обробляється «відпочиває» в обміннику на диску. Всі Юнікси та більшість інших систем, що мають функцію віртуальної пам’яті, використовують різні варіації саме цього методу.
Віртуальна пам’ять – це перша ланка у мості між швидкостями диску та процесора. Вона повністю контролюється операційною системою. Проте є ще різниця між швидкістю оперативної пам’яті та пам’яті регістрів процесора. Зовнішня та внутрішня кеш-пам’ять процесора згладжують цю різницю майже в такий самий спосіб, як ядро це робить із оперативною пам’яттю та диском.
Точно так, як оперативну пам'ять можна уявити вікном на обмінну область диску, зовнішня кеш-пам’ять є свого роду вікном на оперативну пам’ять. Вона є швидшою та меншою за основну пам’ять. Метод LRU реалізовується апаратно, контролером пам’яті Вашого комп’ютера. Тільки одиниця кеш-пам’яті, з історичних причин, називається не стооінкою, а рядком^6 .
Але і це ще не кінець. Внутрішній кеш робить останній крок крізь терни до зірок, кешуючи (запам’ятовуючи) порції даних із зовнішнього кешу. Він є ще меншим і ще швидшим, фактично знаходячись на чипі процесора.
Якщо Ви збираєтесь створювати програми, тепер ви знаєте, що потрібно зробити, аби вони були справді швидкими. Вони повинні розташовувати свою пам’ять щільно (локально), оскільки це робить процес кешування значно ефективнішим. То ж найкращий спосіб робити програми швидкими – це робити їх маленькими. Якщо програма не уповільнюється дисковими операціями В/В чи очікуваннями на мережеві події, вона буде працювати на швидкості внутрішнього кешу процесора, якщо поміститься туди. Якщо Ви не можете зробити всю програму настільки маленькою, Ви повинні подбати, щоб критичні до швидкості частини програми займали якнайменше місця. Технічні деталі таких пояснень лежать поза межами цього посібника; якщо Вам це справді потрібно, треба буде гарно вивчити можливості Вашого компілятора, щоб знати як правильно цього досягнути.
Модуль керування пам’яттю
Навіть коли комп’ютер обладнаний достатньою кількістю оперативної пам’яті, щоб не виникало потреби використовувати обмінник, керівник пам’яттю ядра всерівно має достатньо роботи. Він слідкує за тим, щоб усі запущені програми не лізли у чужі сегменти даних (окрім тих, котрі спеціально призначені для зчитування даних з інших програм), і убезпечити дані одної програми від помилкового перезапису іншою програмою. Щоб зробити це він формує так звану таблицю даних і сегментів. Таблиця оновлюється кожного разу, коли процес потребує більше пам’яті чи відпускає якусь її частину, коли вона йому більше не потрібна (останнє відбувається, як правило, при завершенні роботи процесу).
Ця таблиця використовується для передачі команд до спеціалізованої апаратної одиниці, що називається модуль керування пам’яттю або MMU^7 . У сучасних процесорах цей модуль вмонтований всередину самих процесорів. Він має специфічну властивість виставляти «огорожі» навколо використовуваних ділянок пам’яті, отож недозволені запити програм у чужі ділянки пам’яті натикаються на ці огорожі, і MMU генерує перепин.
Якщо Ви коли-небудь бачили повідомлення «Segmentation fault» або «core dumped» після того, як котрась із програм «зависла», відбулось саме те, про що ішлося — звертання програмою у пам’ять поза межами її власного сегменту. Це свідчить про помилку у програмі — перед тим, як використовувати пам’ять потрібно попросити про новий сегмент у ядра.
Є також і інший аспект для захисту процесів від «перетину інтересів». Можна, наприклад, контролювати їхні можливості доступу до файлів чи пристроїв (котрі у Юнікс’ах теж відображаються у вигляді файлів), що унеможливить погано чи зумисне шкідливо написаним програмам пошкодити важливі частини системи. Ось чому усі Юнікси мають так звані «права доступу до файлів», про що описується в наступних розділах.