Чи вводить операційна система власний машинний код під час відкриття програми?


32

Я вивчаю процесори і знаю, як він читає програму з пам'яті та виконує її вказівки. Я також розумію, що ОС розділяє програми в процесах, а потім чергує їх між собою так швидко, що ви думаєте, що вони працюють одночасно, але насправді кожна програма працює окремо в процесорі. Але якщо ОС також є купою коду, що працює в процесорі, то як він може управляти процесами?

Я думав, і єдине пояснення, про яке я міг би подумати, це: коли ОС завантажує програму із зовнішньої пам’яті в оперативну пам’ять, вона додає власні вказівки в середині вихідних інструкцій програми, тож програма виконується, програма може зателефонувати в ОС і зробити деякі речі. Я вважаю, що існує вказівка, що ОС додасть до програми, що дозволить центральному процесорові деякий час повертатися до коду ОС. А також я вважаю, що коли ОС завантажує програму, вона перевіряє, чи є якісь заборонені вказівки (які могли б перейти на заборонені адреси в пам'яті) і усуває потім.

Чи я думаю про річ? Я не студент CS, а насправді студент з математики. Якщо можливо, я хотів би отримати хорошу книгу про це, тому що я не знайшов нікого, який би пояснював, як ОС може керувати процесом, якщо ОС також є купою коду, що працює в центральному процесорі, і він не може працювати при цьому час програми. Книги говорять лише про те, що ОС може керувати речами, а тепер як.


7
Див.: Контекстний перемикач ОС здійснює контекстний перехід на додаток. Потім програма може запитувати служби ОС, які мають контекст назад в ОС. Коли програма закінчується, контекст повертається до ОС.
Гай Кодер

4
Дивіться також "syscall".
Рафаель


1
Якщо коментарі та відповіді не відповідають на ваше запитання на ваше розуміння чи задоволення, то, будь ласка, попросіть додаткову інформацію як коментар та поясніть, що ви думаєте чи де загубитесь, або що конкретно вам потрібно детальніше.
Гай Кодер

2
Я вважаю, що переривання , підключення (переривання), апаратний таймер (із плануванням- гачок) та пейджингові повідомлення (часткова відповідь на ваше зауваження щодо забороненої пам'яті) - це основні ключові слова, які вам потрібні. ОС потрібно дуже тісно співпрацювати з процесором, щоб запускати його код лише за потреби. Таким чином, більшість процесорних потужностей може використовуватися для фактичних обчислень, а не для управління ними.
Палець

Відповіді:


35

Ні. Операційна система не возиться з кодом програми, вводячи в неї новий код. Це матиме ряд недоліків.

  1. Це буде трудомістким, оскільки ОС повинна буде переглядати весь виконуваний файл, вносячи його зміни. Зазвичай частина виконуваного файлу завантажується лише за потреби. Крім того, вставка коштує дорого, оскільки вам доведеться перемістити безліч речей.

  2. Через нерозбірливість проблеми зупинки неможливо знати, куди потрібно вставити вказівки "Перейти назад до ОС". Наприклад, якщо код включає щось на кшталт while (true) {i++;}, вам обов'язково потрібно вставити гачок всередині цього циклу, але умова в циклі ( true, тут) може бути довільно складною, тому ви не можете вирішити, наскільки він циклічний. З іншого боку, було б дуже неефективно вставляти гачки у кожен цикл: наприклад, вистрибування назад до ОС під час for (i=0; i<3; i++) {j=j+i;}дуже уповільнить процес. І, з тієї ж причини, ви не можете виявити короткі петлі, щоб залишити їх у спокої.

  3. Через невирішеність проблеми зупинки неможливо дізнатися, чи змінили код зміни змісту програми. Наприклад, припустимо, що ви використовуєте в своїй програмі C покажчики функцій. Введення нового коду перемістить розташування функцій, тож, коли ви зателефонували за ним через вказівник, ви перескочили б у неправильне місце. Якби програміст був достатньо хворий для використання обчислених стрибків, вони теж не зможуть.

  4. Це зіграло б веселе пекло з будь-якою антивірусною системою, оскільки воно також змінило б код вірусу і примрушило всі ваші контрольні суми.

Ви можете подолати проблему, що зупиняється, імітуючи код і вставляючи гачки в будь-який цикл, який виконується більше певної фіксованої кількості разів. Однак це вимагало б надзвичайно дорогого моделювання всієї програми до того, як її дозволили виконати.

Насправді, якби ви хотіли вставити код, компілятор був би природним місцем для цього. Таким чином, вам доведеться це зробити лише один раз, але це все одно не вийде з другої та третьої причин, наведених вище. (І хтось може написати компілятор, який не програвав.)

Існує три основні способи відновлення управління ОС процесами.

  1. У кооперативних (або не випереджаючих) системах є yieldфункція, яку може викликати процес, щоб повернути управління ОС. Звичайно, якщо це ваш єдиний механізм, ви покладаєтесь на процеси, які добре поводяться, і процес, який не поступається, буде зависати процесором, поки він не припиниться.

  2. Щоб уникнути цієї проблеми, використовується переривання таймера. Процесори дозволяють ОС реєструвати зворотні дзвінки для різних типів переривань, які реалізує процесор. ОС використовує цей механізм для реєстрації зворотного дзвінка для переривання таймера, який періодично запускається, що дозволяє виконувати власний код.

  3. Кожен раз, коли процес намагається прочитати файл або взаємодіяти з обладнанням будь-яким іншим способом, він просить ОС зробити роботу над цим. Коли ОС вимагає зробити щось за допомогою процесу, він може прийняти рішення про припинення цього процесу та почати запускати інший. Це може здатися трохи макіавельським, але це правильно зробити: введення / виведення диска повільне, тому ви можете також пустити процес B в той час, як процес A чекає, коли прядильні грудочки металу перемістяться в потрібне місце. Мережевий введення / виведення ще повільніше. Клавіатурний ввод / вивід є льодовиковим, оскільки люди не є гігагерцевими істотами.


5
Чи можете ви розвинути більше на останній 2-й точці? Мені цікаво це питання, і я відчуваю, що пояснення тут пропущено. Мені здається, що питання полягає в тому, як "ОС забирає процесор з процесу", а у вашій відповіді сказано "ОС обробляє це". але як? Візьміть нескінченний цикл у своєму першому прикладі: як це не заморозити комп’ютер?
BiAiB

3
Деякі ОС це роблять, більшість ОС принаймні возиться з кодом, щоб зробити "посилання", тому програму можна завантажувати за будь-якою адресою
Ian Ringrose

1
@BiAiB Ключовим словом тут є "переривання". Процесор - це не просто те, що обробляє заданий потік інструкцій, він також може бути асинхронно перерваний з окремого джерела - головне для нас, переривання вводу / виводу та годинника. Оскільки лише простір коду ядра може працювати з перериваннями, Windows може бути впевнений, що зможе "вкрасти" роботу з будь-якого запущеного процесу в будь-який час, коли цього захоче. Обробники переривань можуть виконувати будь-який код, який вони хочуть, включаючи "зберігати регістри процесора десь і відновлювати їх звідси (інший потік)". Надзвичайно спрощений, але це контекстний перемикач.
Луаан

1
Додавання до цієї відповіді; стиль багатозадачності, згаданий у пунктах 2 та 3, називається "попереджувальним багатозадачністю", назва посилається на здатність ОС відмовлятись від запущеного процесу. Кооперативне багатозадачність часто використовувалося в старих операційних системах; в Windows принаймні попереджувальна багатозадачність не була введена до Windows 95. Я читав про принаймні одну систему промислового управління, яка застосовується сьогодні, яка досі використовує Windows 3.1 виключно для своєї багатозадачності в режимі реального часу.
Джейсон C

3
@BiAiB Насправді ви помиляєтесь. Настільні процесори не запускають код послідовно та синхронно починаючи з i486. Однак навіть старші процесори все ще мали асинхронні входи - переривання. Уявіть запит на апаратне переривання (IRQ) так само, як штифт на самому процесорі - коли він отримує 1, процесор припиняє все, що робить, і починає обробляти переривання (що в основному означає "зберегти стан і перейти до адреси в пам'яті"). Сама обробка переривань не є x86чи яким би то не було кодом, воно буквально провідне. Після стрибків він знову виконує (будь-який) x86код. Нитки - це спосіб вищої абстракції.
Луаан

12

Незважаючи на те, що відповідь Девіда Ріхербі хороша, він виглядає таким чином, як сучасні операційні системи зупиняють існуючі програми. Моя відповідь повинна бути точною для архітектури x86 або x86_64, яка є єдиною, яка зазвичай використовується для настільних ПК та ноутбуків. Інші архітектури повинні мати подібні методи досягнення цього.

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

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

З пам’яті, коли це відбувається, ядро ​​операційної системи має зберегти те, де був код, і коли ядро ​​закінчило робити те, що для цього потрібно, відновлює попередній стан програми. Таким чином, програма навіть не знає, що вона була перервана.

Процес не може змінити таблицю переривань з двох причин. Перша полягає в тому, що вона працює в захищеному середовищі, тому якщо вона намагається викликати певний захищений код складання, процесор спровокує інше переривання. Друга причина - віртуальна пам'ять. Розташування таблиці переривань становить від 0x0 до 0x3FF у реальній пам'яті, але при користувацьких процесах це розташування зазвичай не відображається, а спроба читання незробленої пам'яті призведе до іншого переривання, тому без захищеної функції та можливості запису на реальну оперативну пам'ять , користувацький процес не може його змінити.


4
Переривання не визначені операційною системою, вони задаються апаратними засобами. І більшість сучасних архітектур мають спеціальні інструкції щодо виклику операційної системи. i386 використовував для цього перерву (програмне забезпечення, створене програмним забезпеченням), але більше не робиться таким чином для його наступників.
фонбранд

2
Я знаю, що переривання визначаються процесором, але ядро ​​встановлює покажчики. Я, можливо, пояснив це погано. Я також подумав, що Linux ще використовував int 9 для розмови з ядром, але, можливо, зараз є кращі способи.
Programmdude

Це досить оманлива відповідь, хоча думка про те, що попереджувальні планувальники керуються тимчасовими перериваннями, є правильним. Спершу варто зазначити, що таймер знаходиться в апаратному забезпеченні. Крім того, щоб уточнити, що процес "збереження ... відновлення" називається контекстним комутатором і, в основному, включає збереження всіх регістрів процесора (що включає вказівник інструкції), серед іншого. Також процеси можуть ефективно змінювати таблиці переривань, це називається "захищений режим", який також визначає віртуальну пам'ять, і існує вже з 286 року - вказівник на таблицю переривань зберігається в регістрі, що записується.
Джейсон C

(Також навіть таблиця переривань у режимі реального режиму була переміщена - не заблокована на першій сторінці пам'яті - з 8086 року.)
Jason C

1
Ця відповідь пропускає критичну деталь. Коли відбувається переривання, процесор не переходить безпосередньо на ядро. Натомість він спочатку зберігає існуючі регістри, потім переходить на інший стек, і лише після цього викликається ядро. Викликати ядро ​​випадковим стеком з випадкової програми було б досить поганою ідеєю. Також остання частина вводить в оману. Ви не отримаєте перерви "намагатися" читати невстановлену пам'ять; це просто неможливо. Ви читаєте з віртуальних адрес, а незроблена пам'ять просто не має віртуальної адреси.
MSalters

5

Ядро ОС отримує контроль від запущеного процесу завдяки оброблювачам переривань тактового процесора, а не шляхом введення коду в процес.

Ви повинні прочитати про переривання, щоб отримати більше роз'яснень щодо того, як вони працюють і як ядра ОС обробляють їх та реалізують різні функції.


Не просто переривання годинника: будь-яке переривання. А також інструкції щодо зміни режиму.
Жил "ТАК - перестань бути злим"

3

Там є метод , подібний до того , що ви описали: кооперативну багатозадачність . В ОС не вставляються інструкції, але кожна програма повинна бути написана для виклику функцій ОС, які можуть вибрати інший із спільних процесів. Це має недоліки, які ви описуєте: одне збої програми виймає всю систему. Вікна до 3.0 включаючи, працювали так; 3.0 в "захищеному режимі" і вище не було.

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

(Деякі системи виконують перезапис інструкцій обмеженим чином щодо завантаження програми, званого "thunking", і процесор Transmeta динамічно перекомпілюється у свій власний набір інструкцій)


AFAICR 3.1 також співпрацював. У програмі Win95 з'явилася превентивна багатозадачність. Захищений режим в основному приніс ізоляцію адресного простору (що покращує стабільність, але в значній мірі не пов'язані між собою).
cHao

Мислення не переписує та не вводить код у додаток. Модифікований навантажувач базується на ОС, а не продукт програми. Інтерпретативні мови, складені, такі як використання компіляторів JIT, не змінюють код і нічого не вводять у код. Вони переводять вихідний код у виконуваний файл. Знову ж таки, це не те саме, що вводити код у додаток.
Дейв Гордон

Transmeta взяв виконуваний код x86 як своє джерело, а не інтерпретаційну мову. І я думав , що один випадок , в якому код буде впорскується: працює під отладчиком. Системи X86 зазвичай перезаписують інструкцію на точці розриву "INT 03", яка потрапляє у відладчик. Після відновлення вихідний опкод відновлюється.
pjc50

Налагодження навряд чи хтось запускає додаток; виходить за рамки розробника програми. Тому я не думаю, що це дійсно допомагає ОП.
Дейв Гордон

3

Багатозадачність не вимагає нічого, як введення коду. В такій операційній системі, як Windows, є компонент коду операційної системи, який називається планувальником, який спирається на апаратне переривання, ініційоване апаратним таймером. Це використовується операційною системою для перемикання між різними програмами і самою собою, завдяки чому все це здається, що наше людське сприйняття відбувається одночасно.

В основному, операційна система програмує апаратний таймер, щоб вимикатись так часто ... можливо, 100 разів на секунду. Коли таймер вимикається, він генерує апаратне переривання - сигнал, який повідомляє процесору припинити, що він робить, зберегти його стан на стеці, змінити режим на щось більш привілейоване і виконати код, який він знайде в спеціально призначеному місце в пам’яті. Цей код є частиною планувальника, який вирішує, що робити далі. Можливо, відновиться якийсь інший процес, і в цьому випадку йому доведеться виконати те, що відомо як "контекстний перемикач" - замінивши всю його поточний стан (включаючи таблиці віртуальної пам'яті) на інший процес. Повертаючись до процесу, він повинен відновити весь контекст цього процесу,

«Спеціально відведене» місце в пам’яті не повинно знати нічого, крім операційної системи. Реалізація відрізняється, але суть полягає в тому, що ЦП реагуватиме на різні переривання, здійснюючи пошук таблиці; розташування таблиці знаходиться на певному місці в пам'яті (визначається апаратною конструкцією процесора), вміст таблиці встановлюється операційною системою (як правило, під час завантаження), і "тип" переривання визначатиме, який запис в таблиці слід використовувати як "звичайний режим переривання".

Ніщо з цього не передбачає "введення коду" ... воно засноване на коді, що міститься в операційній системі у взаємодії з апаратними функціями ЦП та його підтримуючої схеми.


2

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

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

Більшість інструкцій, що виконуються (наприклад, у звичайних додатках), можна віртуалізувати за допомогою апаратного забезпечення, але саме ядро ​​ОС використовує інструкції, які не можна віртуалізувати, тому що якщо машинний код здогадки ОС був виконаний немодифікованим, він "вибухне" "управління хостом VMware. Наприклад, гостьовій ОС потрібно запустити в найбільш привілейоване захисне кільце і встановити таблицю переривань. Якби це було дозволено, VMware втратив би контроль над обладнанням.

VMware переробляє ці інструкції в коді ОС перед її виконанням, замінюючи їх стрибками в код VMware, що імітує потрібний ефект.

Тож ця техніка дещо аналогічна тому, що ви описуєте.


2

Є різні випадки, коли операційна система може "вводити код" у програму. На основі 68000 версій системи Apple Macintosh створена таблиця всіх сегментів входу сегмента (розташована безпосередньо перед статичними глобальними змінними, IIRC). Коли програма запускається, кожен запис у таблиці складається з інструкції щодо пастки, за якою слідує номер сегмента та зміщення в сегмент. Якщо пастка виконана, система перегляне слова після інструкції пастки, щоб побачити, який сегмент і зсув потрібен, завантажте сегмент (якщо його ще немає), додайте стартову адресу сегмента до зміщення та потім замініть пастку стрибком до цієї щойно обчисленої адреси.

На старшому програмному забезпеченні ПК, хоча це не було технічно зроблено "ОС", звичайно, щоб код був побудований з інструкціями щодо пастки замість інструкцій з математики копроцесора. Якщо не було встановлено математичного співпроцесора, обробник пастки буде імітувати його. Якщо був встановлений копроцесор, перший раз, коли буде взято пастку, обробник замінить інструкцію пастки на інструкцію копроцесора; майбутні виконання цього ж коду будуть використовувати безпосередньо інструкцію співпроцесора.


Метод FP досі використовується у процесорах ARM, у яких на відміну від процесорів x86 все ще є варіанти, що не мають FP. Але це рідко, оскільки більшість використання ARM є у спеціалізованих пристроях. У таких середовищах зазвичай відомо, чи буде процесор мати FP-можливості.
MSalters

Ні в одному з цих випадків ОС не вводила код у додаток. Щоб ОС вводила код, йому потрібна ліцензія постачальника програмного забезпечення для "модифікації" програми, яку вона не отримує. ОС НЕ вводить код.
Дейв Гордон

Інструкції з захопленими @DaveGordon можна зрозуміло, що це ОС, що вводить код у додаток.
Жил 'ТАК - перестань бути злим'

Інструкції з пасткою @MSalters трапляються зазвичай у віртуальних машинах.
Жил "ТАК - перестань бути злим"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.