Усі сучасні процесори мають можливість перервати інструкцію, що виконується в даний час. Вони зберігають достатньо стану (зазвичай, але не завжди, у стеці), щоб дати можливість відновити виконання пізніше, як ніби нічого не сталося (перервана інструкція буде перезапущена з нуля, як правило). Потім вони починають виконувати обробник переривань , який є лише більш машинним кодом, але розміщується в спеціальному місці, щоб процесор знав, де він знаходиться заздалегідь. Обробники переривань завжди є частиною ядра операційної системи: компонент, який працює з найбільшим привілеєм і відповідає за контроль за виконанням усіх інших компонентів. 1,2
Перерви можуть бути синхронічними , тобто вони викликаються самим процесором як пряма відповідь на те, що робила інструкція, що виконується в даний час, або асинхронно , тобто вони трапляються в непередбачуваний час через зовнішню подію, наприклад, дані, що надходять у мережу порт. Деякі люди резервують термін "переривання" для асинхронних переривань, а синхронні переривання називають "пастками", "помилками" або "винятками", але ці слова мають інші значення, тому я збираюся дотримуватися "синхронного переривання".
Зараз більшість сучасних операційних систем мають поняття про процеси . В основному, це механізм, за допомогою якого комп'ютер може одночасно запускати більше однієї програми, але це також ключовий аспект того, як операційні системи налаштовують захист пам’яті , що є особливістю більшості (але, на жаль, все ще не всі ) сучасні процесори. Це йде разом з віртуальною пам'яттю, що полягає у можливості зміни відображення між адресами пам'яті та фактичними місцями в оперативній пам'яті. Захист пам’яті дозволяє операційній системі надавати кожному процесу свій приватний фрагмент оперативної пам’яті, до якого лише він може отримати доступ. Це також дозволяє операційній системі (яка діє від імені певного процесу) призначити регіони ОЗУ як лише для читання, виконувані файли, спільні між групою взаємодіючих процесів тощо. Також буде фрагмент пам'яті, доступний лише для ядро. 3
Поки кожен процес отримує доступ до пам’яті лише способами, які налаштований на процесор, захист пам’яті невидимий. Коли процес порушує правила, центральний процесор генерує синхронний перерив, просячи ядро розібратися з речами. Часто трапляється, що процес насправді не порушував правила, тільки ядро потрібно виконати певну роботу, перш ніж процес буде дозволено продовжувати. Наприклад, якщо сторінку пам'яті процесу потрібно "виселити" у файл swap, щоб звільнити місце в ОЗУ для чогось іншого, ядро позначить цю сторінку недоступною. Наступного разу, коли процес спробує його використовувати, процесор генерує переривання захисту пам’яті; ядро витягне сторінку зі свопу, поверне її туди, де вона була, знову позначить її доступною та відновить виконання.
Але припустимо, що процес справді порушив правила. Він намагався отримати доступ до сторінки, на якій ніколи не було відображено жодної ОЗУ, або намагався виконати сторінку, яка позначена як такою, що не містить машинного коду, або іншого. Сімейство операційних систем, відомих як "Unix", всі використовують сигнали для вирішення цієї ситуації. 4 Сигнали схожі на переривання, але вони генеруються ядром і поляруються процесами, а не генеруються апаратним забезпеченням і посилаються ядром. Процеси можуть визначати обробники сигналіву власному коді та скажіть ядро, де вони є. Ці обробники сигналів будуть виконуватись, перериваючи нормальний потік управління, коли це необхідно. Усі сигнали мають число і два назви, одне з яких є криптованою абревіатурою, а інше дещо менш криптовалютною фразою. Сигнал, який генерується, коли процес порушує правила захисту пам’яті, є (за домовленістю) № 11, а його назви - SIGSEGV
«Помилка сегментації». 5,6
Важливою відмінністю між сигналами та перериваннями є те, що для кожного сигналу існує поведінка за замовчуванням . Якщо операційна система не зможе визначити обробники для всіх перерв, то це помилка в ОС, і весь комп'ютер вийде з ладу, коли ЦП намагатиметься викликати відсутній обробник. Але процеси не зобов'язані визначати обробники сигналів для всіх сигналів. Якщо ядро генерує сигнал для процесу, і цей сигнал був залишений за умовчанням, ядро просто піде вперед і зробить все, що за замовчуванням, і не турбує процес. Більшість сигналів за замовчуванням поведінки або "нічого не роблять", або "припиняють цей процес і, можливо, також створюють основний дамп". SIGSEGV
є одним із останніх.
Отже, для резюме, у нас є процес, який порушив правила захисту пам’яті. ЦП призупинив процес і створив синхронний переривання. Ядро поле, яке перериває і генерує SIGSEGV
сигнал для процесу. Припустимо, що процес не встановив обробник сигналу для SIGSEGV
, тому ядро виконує поведінку за замовчуванням, яка полягає в припиненні процесу. Це має ті ж ефекти, що і _exit
системний виклик: відкриті файли закриті, пам'ять розміщена тощо.
До цього моменту нічого не роздруковувало жодних повідомлень, які людина може бачити, а оболонка (або, загалом, батьківський процес процесу, який щойно закінчився) взагалі не брав участь. SIGSEGV
переходить до процесу, який порушив правила, а не його батьківського. Наступний крок в послідовності, однак, є те, щоб повідомити про це батьківському процесі , що його дитина був припинений. Це може статися кілька різних способів, найпростіші з яких є , коли батько вже чекає цього повідомлення, використовуючи один з wait
системних викликів ( wait
, waitpid
, wait4
і т.д.). У такому випадку ядро просто спричинить повернення цього системного виклику та надасть батьківському процесу номер коду, який називається статусом виходу. 7 Статус виходу повідомляє батькові, чому дочірній процес припинено; в цьому випадку він дізнається, що дитина була припинена через поведінку SIGSEGV
сигналу за замовчуванням .
Потім батьківський процес може повідомити людину про подію, надрукувавши повідомлення; програми оболонки майже завжди роблять це. Ваш crsh
код не включає код для цього, але це все одно відбувається, тому що звичайна бібліотека C system
працює з повнофункціональною оболонкою /bin/sh
"під кришкою". crsh
є бабуся і дідусь у цьому сценарії; поле сповіщення батьківського процесу поле /bin/sh
, яке друкує його звичайне повідомлення. Потім /bin/sh
він виходить, оскільки більше нічого не має, і реалізація бібліотеки C system
отримує це повідомлення про вихід. Ви можете побачити це повідомлення про вихід у своєму коді, перевіривши повернене значенняsystem
; але це не скаже тобі, що процес онука загинув на сегменті, тому що його спожив проміжний процес оболонки.
Виноски
Деякі операційні системи не реалізують драйвери пристроїв як частину ядра; однак усі обробники переривань все ще повинні бути частиною ядра, і це робить код, який налаштовує захист пам’яті, оскільки апаратне забезпечення не дозволяє нічого, крім ядра, робити ці дії.
Може існувати програма, яка називається "гіпервізор" або "менеджер віртуальної машини", яка є навіть більш привілейованою, ніж ядро, але для цілей цієї відповіді це може вважатися частиною апаратного забезпечення .
Ядро - це програма , але це не процес; це більше схоже на бібліотеку. Усі процеси час від часу виконують частини коду ядра, крім власного коду. Може бути ряд "потоків ядра", які виконують лише код ядра, але вони нас тут не стосуються.
Звичайно, єдина ОС, з якою ви, швидше за все, матимете справу, яка не може вважатися впровадженням Unix, - це, звичайно, Windows. Він не використовує сигналів у цій ситуації. ( На насправді, це не має сигналів, на Windows самий <signal.h>
інтерфейс повністю сфальсифікована бібліотекою C) . Він використовує то , що називається « структурована обробка винятків » замість цього.
Деякі порушення захисту пам’яті генерують SIGBUS
(«Помилка шини») замість SIGSEGV
. Лінія між ними не визначена і змінюється від системи до системи. Якщо ви написали програму, яка визначає обробник для SIGSEGV
, можливо, буде хорошою ідеєю визначити той же обробник для SIGBUS
.
"Помилка сегментації" - це назва переривання, генерованого для порушень захисту пам'яті одним з комп'ютерів, на яких працює оригінал Unix , ймовірно, PDP-11 . " Сегментація " - це тип захисту пам'яті, але сьогодні термін " помилка сегментації " загалом відноситься до будь-якого порушення захисту пам'яті.
Усі інші способи батьківського процесу можуть бути повідомлені про закінчення дитини, в кінцевому підсумку з викликом батьків wait
та отримання статусу виходу. Просто щось спочатку відбувається щось інше.
crsh
є чудовою ідеєю для такого роду експериментів. Дякуємо, що повідомили нам про це та ідею, що стоїть за цим.