Як операційні системи… запускаються… без роботи ОС?


167

Мені зараз справді цікаво. Я програміст Python, і це питання мене просто заплутало: Ви пишете ОС. Як ви це запускаєте? Це потрібно якось запустити, і це в рамках іншої ОС?

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

Це стосується ядра UNIX? Якщо так, то що таке ядро ​​Unix або ядро ​​взагалі?

Я впевнений, що ОС набагато складніше, ніж це, але як це працює?


14
Я досить впевнений, що для цього BIOS - це дійсно невелика ОС, яка завантажує роботу з більшою ОС.
sevenseacat

64
ОС є зручною , але вона вам не потрібна для запуску програм на комп’ютері.
Андрес Ф.

10
Цілком можливо навіть писати програмне забезпечення, яке не є ОС, без ОС. Багато перекладачів Forth традиційно працювали без ОС (або можна сказати, що це ОС). Це навіть не так складно. Якщо ви знаєте C, ви можете насолоджуватися написанням такої програми (можливо, невеликої гри), як навчальної вправи.
Макс

44
Ця плутанина - це одна з витрат на чудові, безпечні, високо абстраговані обчислювальні системи, якими ми користуємося сьогодні: люди можуть бути дуже хорошими та компетентними програмістами і не знають навіть основ про те, як працює комп'ютер. Як низько ви хочете піти? Для дуже низьких, але все ж вище фізики див. Як запрограмовані перші мікропроцесори? на Electronics.SE.
dmckee

2
Програмування здійснювалося до винаходу сучасної концепції ОС. Очевидно щось на цьому рівні - це те, що стартує в ОС. ОС завантажуються. Зазвичай це, принаймні, згадується в програмі CS на 4 роки в якийсь момент, оскільки більшість вимагає комп'ютерної теорії курсу операційних систем.
Ріг

Відповіді:


263

Існує безліч веб-сайтів, які проходять процес завантаження (наприклад, How Computers Boot Up ). Коротше кажучи, це багатоступеневий процес, який продовжує створювати систему за раз, поки вона нарешті не може запустити процеси в ОС.

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

Досить неймовірно, що це взагалі працює!


108
+1 за заключне речення.
CVn

39
Є причина, що називається "завантаження"; термін короткий для "завантажувального завантаження", як і в "підтягуванні себе завантажувальними".
KeithS

5
Був час, коли комусь доводилося вводити чи перемикати код завантажувального коду. Іноді це був простий перехід до першої інструкції програми в ПЗУ. В іншому випадку було прочитано код з пристрою та перейти до першої інструкції програми в даних, які були прочитані. Зараз набагато простіше.
BillThor


15
@BillThor: Де під «набагато простішим» ви, звичайно, маєте на увазі «набагато складніше». Вони просто простіші у використанні .
Рафаель Швайкерт

173

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

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

У будь-якому разі, хоча ОС може бути реалізована в (наприклад) C, вона не матиме всіх звичайних бібліотек C, доступних для неї. Зокрема, у ньому не буде нормальних бібліотек 'stdio'. Швидше він буде реалізовувати (наприклад) драйвер дискового пристрою, який дозволяє йому читати і записувати дискові блоки. Він буде реалізовувати файлову систему поверх шару блоку диска, а поверх цього реалізовуватиме системні виклики, які виконує бібліотеки часу виконання програми для (наприклад) створення, читання та запису файлів тощо.

Як програма може працювати без роботи в ОС?

Це має бути спеціальний додаток (наприклад, операційна система), який знає, як безпосередньо взаємодіяти з обладнанням вводу / виводу тощо.

Як ви скажете комп'ютеру запускати, скажімо, C, і виконувати ці команди на екрані, якщо у нього немає ОС для запуску?

Ви цього не робите.

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

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

Відповідь Білла охоплює завантажувальний процес - процес, коли ви переходите від вимкненої машини до машини, на якій працює нормальна операційна система. Однак варто зауважити, що коли BIOS виконує свої завдання, він (як правило) надає повний контроль над апаратним забезпеченням до основної операційної системи і не грає ніякої подальшої ролі - до наступного перезавантаження системи. Основна ОС, безумовно, не працює «в межах» BIOS у звичайному розумінні.

Це стосується ядра UNIX? Якщо так, що таке ядро ​​Unix або ядро ​​взагалі?

Так.

Ядро UNIX є ядром операційної системи UNIX. Саме частина UNIX виконує всі описані вище речі "голий метал".

Ідея "ядра" полягає в тому, що ви намагаєтеся розділити програмне забезпечення системи на основні речі (для яких потрібен фізичний доступ до пристрою, всю пам'ять тощо) та непрофільні речі. Ядро складається з основного матеріалу.

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


6
Феноменальна відповідь. Я дам вам ще кілька подій, якщо це можливо.
weberc2

7
Тут багато хороших відповідей, тому я додав це як коментар, оскільки ніхто до цього часу не згадував: Однією з ключових особливостей в ОС є можливість декількох програм виконувати "одночасно" з точки зору користувача. Можливість планувати процеси та захищати процеси від зміни поведінки один одного, як правило, є лише функціями, що знаходяться лише в ОС, а не вбудованого програмного забезпечення або BIOS.
Шон Барбо

2
The idea of a "kernel" is that you try to separate the system software into core stuffЛегко запам’ятати, зазначивши, що термін kernelпоходить від німецької Kern, що означає ядро ​​/ ядро.
deed02392

1
Люблю цю відповідь тут, бо в ній згадується, що вона складається і пов'язує двійковий код, який працює не C.
Тревіс Пессетто

3
"Відхід від цього зробив користувачів ПК менш розумними." - Не менш розумні ... менш комп'ютерні грамотні. Можна також сказати, що це збільшило кількість користувачів ПК.
Стівен C

62

На початку в процесорі не було сили.

І Людина сказав "нехай буде сила", і процесор почав читати з вказаної адреси в пам'яті і виконувати інструкцію, яка там була присутня. Потім наступний і так далі до кінця влади.

Це було завантаження. Його завдання полягало у завантаженні ще одного програмного забезпечення для отримання доступу до навколишнього середовища, де було основне програмне забезпечення, та завантаження його.

Нарешті, дружній екран запропонував вам увійти.


58
Цю відповідь слід перенести на christianity.stackexchange.com
Coomie

6
що таке "задана адреса", звідки вона походить. Вибачте за те, що тут грав Чарльза Дарвіна.
Midhat

7
@Midhat - Перша адреса, яку отримає процесор, всередині нього провідна. Зазвичай це 0.
mouviciel

17
... А 7-го дня Людина відпочив у своїх іграх
канадський Люк

9
@mouviciel Адреса пам'яті призначена 0x7C00для будь-якої x86сумісної архітектури і спочатку має бути заповнена BIOS, який зазвичай завантажує перший сектор будь-якого завантажувального пристрою, який він надає перевагу ... Хоча
приємна

29

Вибачте, що спізнився, але опишу його як таке:

  • Материнська плата отримує потужність.

  • Електричні ланцюги синхронізації запускаються та стабілізуються при необхідності, виходячи лише з їх електричних характеристик. Деякі новіші пристрої можуть фактично використовувати дуже обмежений мікропроцесор або секвенсор.

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

    - jkerian о 5:20, 25 жовтня

  • Харчування віддається процесору та оперативній пам'яті.

  • Процесор завантажує (виходячи із своєї внутрішньої проводки) дані від BIOS. На деяких машинах BIOS може бути дзеркально відображений в оперативній пам'яті, а потім виконаний звідти, але це є рідкісним IIRC.

    Увімкнувши процесори, сумісні з x86, починаються з адреси 0xFFFFFFF0 в адресному просторі ...

    -Micheal Steil, 17 помилок Microsoft, зроблені в системі безпеки Xbox ( архів )

  • BIOS здійснює дзвінки до апаратних портів та адрес, які використовуються материнською платою для дискового та іншого апаратного вводу-виводу та розкручує диски, оснащується рештою оперативної пам’яті, серед іншого.

  • Код BIOS (за допомогою параметрів CMOS, що зберігається в апаратному забезпеченні) використовує команди низького рівня IDE або SATA для читання завантажувального сектору кожного диска в порядку, визначеному CMOS або користувачем, що замінює меню.

  • Перший диск із завантажувальним сектором виконує завантажувальний сектор. Цей завантажувальний сектор - це асамблея, яка має інструкції завантажувати більше даних з диска, завантажувати більші NTLDR, пізніші етапи GRUBтощо.

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

Тоді ви отримуєте дружню паніку з ядром, задушливий пінгвін, або ваш диск перемелюється через зупинку голови. =) В альтернативному сценарії ваше ядро ​​встановлює таблиці процесів, структури пам’яті та монтує диски, завантажує драйвери, модулі та графічний інтерфейс або набір послуг (якщо на сервері). Потім програми виконуються під час зчитування їх заголовків, а їх збірка приводиться в пам'ять і відображається відповідно.


2
Слід зазначити, що багато речей, як-от "ланцюги синхронізації запускаються та стабілізуються, якщо це необхідно", насправді в техніці більше не трапляються. Величезна кількість цієї роботи - це насправді надзвичайно спеціалізоване програмне забезпечення, яке працює на дуже обмежених підпроцесорах / секвенсорах. - Привітний інженер мікропрограмного забезпечення мікрорайонів
jkerian

@jkerian Ви не заперечуєте, що я цитував ваш коментар у своєму дописі?
ζ--

хе, зовсім не так.
jkerian

BIOS не є операційною системою. BIOS - це скорочення для базової системи вводу / виводу, і саме це робить BIOS. Це дозволяє програмістам використовувати ресурси низького рівня з наданими виробником драйверами. Коли ОС переходить у режим захисту (32 біт) або довгий (64 біт), BIOS більше не доступний, і ОС використовує власні драйвери, які в основному замінюють функціонал, який BIOS надав на "нижчих" рівнях. Сучасні операційні системи, наприклад Linux та Windows, використовують BIOS лише для виявлення зручних розділів оперативної пам’яті та завантаження власного більш досконалого завантажувача, який може завантажувати потрібні драйвери.
Ганнес Карппіла

1
@HannesKarppila оновлено; зараз це вже близько чотирьох років, і я більше не активний на цьому сайті.
ζ--

15

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

Таке програмне забезпечення для перекладача, як правило, пишеться мовою, яка компілюється в машинний код, наприклад, C або C ++. Машинний код - це те, з чим може працювати процесор. Що може зробити процесор - це прочитати кілька байт з пам'яті і залежно від значень байтів запустити певну операцію. Отже, одна байтна послідовність - це команда для завантаження деяких даних із пам'яті в регістр, інша послідовність для додавання двох значень, інша для збереження значення з реєстру назад в основну пам'ять і незабаром (регістр - це спеціальна область пам'яті, яка є частиною з процесора, де він може працювати найкраще), більшість цих команд на цьому рівні досить низькі. Для цих інструкцій машинного коду, читаними людиною, є код асемблера. В основному цей машинний код - це те, що зберігається у файлах .exe or.com у Windows або всередині бінарних файлів Linux / Unix.

Тепер, якщо комп'ютер запущений, він німий, він має деякі проводки, які будуть читати такі інструкції машинного коду. На ПК це звичайно (в даний час) є мікросхемою EEPROM на материнській платі, що містить BIOS (основна система вихідного виходу), ця система не може зробити багато чого, вона може полегшити доступ до деяких апаратних засобів тощо, а потім зробити ключову операцію: перейдіть до завантажте та скопіюйте в пам'ять перші кілька байтів (він же головний запис завантаження, MBR), а потім скажіть процесору "ось, там ваша програма", процесор буде потім розглядати ці байти як машинний код і виконувати його. Зазвичай це деякий завантажувач операційної системи, який завантажить ядро ​​з деякими параметрами, а потім передасть керування цьому ядру, яке потім завантажить все йогоdriver, щоб отримати доступ до всього обладнання, завантажити якусь програму робочого столу чи оболонки чи будь-що інше і дозволить користувачеві ввійти та використовувати систему.


6
"інтерпільований"? Я ніколи раніше не чув цього терміна.
Брайан Оуклі

3
Цей термін використовувався багато 5 років тому, щоб описати "сучасних" перекладачів, які мають чітку фазу компіляції, яка є окремою від виконання. Не маю уявлення, чи цей термін вижив де-небудь ;-)
johannes

1
"інтерпретований"? Я ніколи раніше не чув цього терміна.
Коул Джонсон

12

Ви запитуєте "Як програма може працювати без роботи в ОС". Проста відповідь - "ОС не програма". Хоча ОС може бути створена тими ж інструментами, що і додаток, і виготовлена ​​з тієї ж сировини, це не одне і те ж. ОС не має грати за тими ж правилами, що і програма.

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

Отже, ОС - це просто машинний код, який існує у відомому місці і який може безпосередньо взаємодіяти з обладнанням.


1
+1 Я думаю, що це найкраща відповідь. Щодо абстракцій, я вважаю, що ви прибиваєте це на правильних рівнях.
Преет Сангха

6

Відповідь на ваше запитання вимагає знання того, як виглядає нативний (для процесора) код і як його інтерпретують CPU.

Зазвичай весь процес компіляції заснований на перекладі речей, які ви пишете на C, Pascal або навіть Python (використовуючи pypy) та C #, на речі, які CPU розуміє, тобто прості інструкції, такі як "зберігати щось під [адресою пам'яті]" "," додавати номери, що зберігаються в регістрах eax і ebx "," функція виклику foo "," порівняння eax до 10 ". Ці інструкції, виконані по черзі, роблять те, що ви хотіли зробити зі своїм кодом.

Тепер подумайте про це: вам не дуже потрібна ОС для виконання цього рідного коду! Все, що вам потрібно - це завантажити цей код у пам'ять і сказати процесору, що він є, і ви хочете, щоб він був виконаний. Не турбуйтеся занадто сильно цим. Ось ця робота повинна турбуватися про BIOS - він завантажує ваш код (лише один і один сектор), відразу після запуску центрального процесора, під фізичною адресою 0x7C00. Тоді ЦП почне виконувати цей один сектор (512 Б) вашого коду. І ти можеш робити все, що собі уявляєш! Без, звичайно, ніякої підтримки з боку ОС. Це тому, що ВИ є операційною системою. Класно, так? Ні стандартної бібліотеки, ні підсилення, ні пітона, ні програм, ні драйверів! Ви повинні все написати самостійно.

А як ти спілкуєшся з обладнанням? Ну, у вас є два варіанти:

  1. Ви залишаєтесь у "Реальному режимі" - режимі виконання процесора лише 1 Мб пам'яті (і навіть менше), без розширених функцій процесора, таких як розширення процесора, захист пам’яті, багатозадачність; 16-бітний виконуваний код, стародавній режим адресації ... Але з деякими підпрограмами, передбаченими BIOS, включаючи простий вихід з екрана, підтримку клавіатури, введення / виведення диска та управління потужністю. Одним словом, ви повернулися до часів MS-DOS та 16-бітних процесорів.
  2. Ви переходите до "Захищеного режиму" з усіма функціями, які має ваш процесор, усією встановленою пам'яттю тощо. Але в захищеному режимі ви САМО повністю самотні, і вам доведеться робити ВСЕ власноруч (і ви спілкуєтеся з обладнанням, використовуючи інструкції "в" і "вихід" для введення / виведення даних в порти вводу / виводу і за допомогою переривань. / О). Чи потрібно говорити кожній ОС, оскільки Windows 95 і найперший Linux вибрали цю опцію?

Тепер ви запитуєте, що таке ядро. Незабаром, ядро ​​- це все, що ви не бачите і не переживаєте безпосередньо. Він управляє разом із драйверами всім, починаючи від клавіатури майже до всіх апаратних засобів всередині вашого ПК. Ви спілкуєтесь з ним графічною оболонкою або терміналом. Або за функціями всередині вашого коду, які тепер виконуються, на щастя, за підтримки ОС.

Для кращого розуміння можу дати вам одну пораду: спробуйте написати власну ОС. Навіть якщо на екрані вийде "Hello world".


3

Існують деякі відмінності щодо функціонування операційної системи, які дуже залежать від системи. Щоб бути корисною, система повинна мати деяку передбачувану поведінку при запуску, наприклад, "запустити виконання за адресою X". Для систем з енергонезалежним сховищем (наприклад, флеш-пам’яттю), відображеним у їх програмному просторі, це досить просто, оскільки ви просто переконайтеся, що ставите код запуску в потрібне місце в програмному просторі процесора. Це надзвичайно часто для мікроконтролерів. Деякі системи повинні отримати свої програми запуску з іншого місця, перш ніж виконувати його. У цих системах будуть вбудовані деякі операції з провідним (або майже провідним) кабелем. Є деякі процесори, які отримують свій стартовий код через i2c з іншого чіпа,

Системи, що використовують сімейство процесорів x86, зазвичай використовують багатоступеневий процес завантаження, який є досить складним через його еволюції та зворотної сумісності. Система виконує деякі мікропрограми (звані BIOS - Basic Input / Output System, або подібні), які знаходяться в деякій енергонезалежній пам'яті на материнській платі. Іноді частина або вся ця прошивка копіюється (переселяється) в оперативну пам’ять, щоб зробити її швидше виконаною. Цей код був написаний із знанням того, яке обладнання буде присутнім та корисним для завантаження.

Запуск прошивки зазвичай пишеться з припущеннями про те, яке обладнання буде присутнє в системі. Роки тому на машині 286, ймовірно, було б припущення про те, що на вході / виводу X буде контролер дискети, який буде завантажувати сектор 0 у певне місце пам’яті, якби давали певний набір команд (і код у секторі 0 знає, як використовувати власні функції BIOS для завантаження більшої кількості коду, і з часом завантажується достатньо коду для завантаження ОС). На мікроконтролері може бути припущення, що є послідовний порт, який працює з певними налаштуваннями, що він повинен чекати команд (для оновлення більш складної прошивки) протягом X кількість часу, перш ніж продовжувати процес завантаження.

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

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

Код ОС зазвичай записується в суміші С і збірки. Перший код для ядра ОС, ймовірно, завжди в зборі і робить такі речі, як налаштування стека, на який спирається код C, а потім викликає функцію C. Інша рукописна збірка також буде там, тому що деякі операції, які ОС повинна виконувати, часто не виявляються в C (наприклад, контекстна комутація / заміна стеків). Часто спеціальні прапори доводиться передавати компілятору C, щоб сказати, що він не покладається на стандартні бібліотеки, якими користується більшість програм C, і не сподіватися, що існуєint main(int argc, char *argv[])в програмі. Крім того, не повинні використовуватися спеціальні параметри лінкера, якими ніколи не користуються більшість програмістів прикладних програм. Це може змусити програму ядра очікувати завантаження за певною адресою або налаштування речей так, щоб вони мали зовнішні змінні в певних місцях, хоча ці змінні ніколи не декларувались у жодному коді С (це корисно для пам'яті, відображеної вводу / виводу або інші спеціальні місця пам'яті).

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


3

Щоб зрозуміти, як працюють операційні системи, може бути корисно розділити їх на дві категорії: ті, які просто надають послуги додаткам за запитом, і ті, які використовують апаратні функції в центральному процесорі, щоб додатки не могли робити те, чого вони не повинні. MS-DOS був колишнього стилю; всі версії Windows, починаючи з 3.0, були останнім стилем (принаймні, коли працює щось більш потужне, ніж 8086).

Оригінальний комп'ютер IBM, що працює під управлінням PC-DOS або MS-DOS, був би прикладом колишнього стилю "ОС". Якби програма бажала відобразити персонаж на екрані, це було б декількома способами. Він може викликати розпорядок, який попросить MS-DOS надіслати його на "стандартний вихід". Якби це зробило, MS-DOS перевірить, чи перенаправляється вихід, і, якщо ні, він викликав би підпрограму, що зберігається в ПЗУ (в колекції процедур, яку IBM називає базовою системою вводу / виводу), яка відображатиме символ у положення курсору та перемістіть курсор ("написати телетип"). Потім програма BIOS зберігатиме пару байтів десь у діапазоні 0xB800: 0 до 0xB800: 3999; обладнання на кольоровому графічному адаптері буде неодноразово отримувати пари байтів у межах цього діапазону, використовуючи перший байт кожної пари для вибору форми символів, а другий - для вибору кольорів переднього плану та фону. Байти отримують і обробляють сигналами червоного, зеленого та синього кольорів у послідовності, яка дає розбірливий текст.

Програми на комп'ютері IBM можуть відображати текст, використовуючи процедуру "стандартного виходу" DOS або використовуючи режим BIOS "запису телетексту" або зберігаючи його безпосередньо для відображення пам'яті. Багато програм, яким потрібно було відобразити багато тексту, швидко вибрали останній підхід, оскільки це може бути буквально в сотні разів швидше, ніж використання підпрограм DOS. Це було не тому, що процедури DOS і BIOS були виключно неефективними; якщо дисплей не вимкнений, він може бути записаний лише в певний час. Процедура BIOS для виведення символу була розроблена так, що його можна було викликати в будь-який час; кожен запит, таким чином, повинен був починатись знову, чекаючи потрібного часу для виконання операції запису. Навпаки, код програми, який знав, що потрібно зробити, міг організуватися навколо наявних можливостей для написання дисплея.

Ключовим моментом тут є те, що, хоча DOS і BIOS забезпечували спосіб виведення тексту на дисплей, у подібних здібностях не було нічого особливо «магічного». Додаток, який хотів записати текст на дисплей, міг би зробити це так само ефективно, принаймні, якщо апаратне забезпечення дисплея працювало так, як очікувало додаток (якщо хтось встановив монохромний адаптер дисплея, який був схожий на CGA, але мав пам'ять символів що знаходиться в 0xB000: 0000-0xB000: 3999), BIOS автоматично виводить символи туди; додаток, запрограмований на роботу з MDA або CGA, може це зробити також, але додаток, запрограмований саме для CGA, буде абсолютно марним для MDA).

У нових системах справи дещо інакше. Процесори мають різні "привілейовані" режими. Вони починаються в найбільш привілейованому режимі, де коду дозволяється робити все, що завгодно. Потім вони можуть перейти в обмежений режим, коли доступні лише вибрані діапазони пам'яті або засоби вводу / виводу. Код не може безпосередньо перейти з обмеженого режиму назад у режим привілеїв, але процесор визначив точки входу в привілейований режим, і код з обмеженим режимом може попросити процесор почати запуск коду в одній з цих точок входу у привілейованому режимі. Крім того, існують точки входу в пільговий режим, пов'язані з низкою операцій, які забороняться в режимі з обмеженим доступом. Припустимо, наприклад, що хтось хотів запускати декілька програм MS-DOS одночасно, у кожного з них є власний екран. Якби програми могли записати безпосередньо на контролер дисплея на 0xB800: 0, жодного способу не завадити перезапису однієї програми на екран іншої програми. З іншого боку, ОС може запускати програму в обмеженому режимі і відловлювати будь-який доступ до пам'яті дисплея; якщо б було виявлено, що програма, яка повинна була знаходитись у «фоновому режимі», намагалася записати 0xB800: 160, вона могла б зберігати дані в деяку пам’ять, яку вона відклала як буфер екрану фонових програм. Якщо згодом цю програму переключити на передній план, буфер може бути скопійований на реальний екран. ОС може запускати програму в обмеженому режимі і відловлювати будь-який доступ до пам'яті дисплея; якщо б було виявлено, що програма, яка повинна була знаходитись у «фоновому режимі», намагалася записати 0xB800: 160, вона могла б зберігати дані в деяку пам’ять, яку вона відклала як буфер екрану фонових програм. Якщо згодом цю програму переключити на передній план, буфер може бути скопійований на реальний екран. ОС може запускати програму в обмеженому режимі і відловлювати будь-який доступ до пам'яті дисплея; якщо б було виявлено, що програма, яка повинна була знаходитись у «фоновому режимі», намагалася записати 0xB800: 160, вона могла б зберігати дані в деяку пам’ять, яку вона відклала як буфер екрану фонових програм. Якщо згодом цю програму переключити на передній план, буфер може бути скопійований на реальний екран.

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


2

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

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

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


1

Ви пишете ОС. Це потрібно якось запустити, і це в рамках іншої ОС?

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

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


Навести дуже спрощений приклад того, як це працює:

Ваша програма повідомляє операційній системі написати щось у файл. До вашої програми операційна система надає такі поняття, як файли та каталоги.

Що стосується апаратури, то ці поняття не існують. Обладнання забезпечує такі поняття, як диски, розділені на нерухомі блоки в 512 байт. Операційна система вирішує, які блоки використовувати для вашого файлу, та деякі інші блоки для метаданих, наприклад, ім'я, розмір та розташування файлу на диску. Потім він повідомляє обладнання: запишіть ці 512 байти в сектор з цим номером на диску з цим номером; запишіть ці 512 байти в сектор з цим різним числом на диску з тим самим числом; і так далі.

Те, як операційна система підказує зробити апаратне забезпечення, дуже різниться. Однією з функцій операційної системи є захист програм від цих відмінностей. Для прикладу диска, на одному з апаратних засобів операційна система повинна записати номер диска і сектора в порт вводу / виводу, а потім записати байти один за одним в окремий порт вводу / виводу. На іншому типі апаратного забезпечення операційній системі доведеться скопіювати цілі 512 байти сектору в область пам’яті, записати розташування цієї області пам’яті в спеціальне місце пам’яті та записати номер диска та сектора на інший спеціальне місце пам'яті.


Сьогодні обладнання високого класу надзвичайно складне. Посібники, що містять усі деталі програмування, - це двері, що мають тисячі сторінок; Наприклад, останнє керівництво процесора Intel - це сім томів, загалом понад 4000 сторінок, і це лише для процесора. Більшість інших компонентів відкривають блоки пам'яті або порти вводу / виводу, яким операційна система може сказати процесору відображати адреси у своєму адресному просторі. Деякі з цих компонентів розкривають ще більше речей за кількома портами вводу / виводу або адресами пам'яті; як приклад, RTC (годинник у режимі реального часу, компонент, який утримує час комп'ютера під час його вимкнення), містить кілька сотень байт пам'яті за парою входів вводу-виводу, і це дуже простий компонент, починаючи з оригінальний ПК / AT. Такі речі, як жорсткі диски, мають цілі окремі процесори, з якою операційна система спілкується за допомогою стандартизованих команд. GPU ще складніше.

Кілька людей у ​​коментарях вище запропонували Arduino. Я погоджуюся з ними, це зрозуміти набагато простіше - ATmega328, який робить усе на Arduino Uno, окрім виставлення USB-роз'єму як послідовного порту, має посібник із лише кількома сотнями сторінок. У Arduino ви працюєте безпосередньо на апаратному забезпеченні, не маючи між собою операційної системи; лише кілька невеликих процедур бібліотеки, якими вам не доведеться користуватися, якщо не хочете.


1

Приклади, які можна виконати

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

Код усіх прикладів, наведених нижче, присутній у цьому репортажі GitHub .

Завантажувальний сектор

На x86 найпростіша річ і найнижчий рівень - це створити головний завантажувальний сектор (MBR) , який є типом завантажувального сектора , а потім встановити його на диск.

Тут ми створюємо один printfзаклик:

printf '\364%509s\125\252' > main.img
sudo apt-get install qemu-system-x86
qemu-system-x86_64 -hda main.img

Результат:

введіть тут опис зображення

Тестовано на Ubuntu 18.04, QEMU 2.11.1.

main.img містить наступне:

  • \364у octal == 0xf4в шістнадцятковій версії: кодування hltінструкції, яке повідомляє ЦП припинити роботу.

    Тому наша програма нічого не зробить: лише запустити та зупинити.

    Ми використовуємо восьмеричні, оскільки \xшестинадцяткові числа не визначені POSIX.

    Ми могли легко отримати це кодування за допомогою:

    echo hlt > a.asm
    nasm -f bin a.asm
    hd a
    

    але 0xf4кодування також задокументоване в посібнику Intel, звичайно.

  • %509sвиробляють 509 просторів. Потрібно заповнити файл до байта 510.

  • \125\252у octal ==, 0x55а далі 0xaa: магічні байти, необхідні для обладнання. Вони повинні бути байтами 511 та 512.

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

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

Запуск на реальному обладнання

Емулятори - це цікаво, але апаратне забезпечення - справжня справа.

Однак зауважте, що це небезпечно, і ви можете помити свій диск помилково: робіть це лише на старих машинах, які не містять критичних даних! Або ще краще, такі розробки, як Raspberry Pi, див. Приклад ARM нижче.

Для типового ноутбука вам потрібно зробити щось на кшталт:

  • Запишіть зображення на USB-накопичувач (знищить ваші дані!):

    sudo dd if=main.img of=/dev/sdX
    
  • підключіть USB до комп'ютера

  • Увімкніть його

  • скажіть, щоб він завантажувався з USB.

    Це означає змусити прошивку вибрати USB перед жорстким диском.

    Якщо це не поведінка вашої машини за замовчуванням, продовжуйте натискати клавіші Enter, F12, ESC або інші такі дивні клавіші після ввімкнення живлення, поки не з’явиться меню завантаження, де ви можете вибрати для завантаження з USB.

    Часто можна налаштувати порядок пошуку в цих меню.

Наприклад, на моєму старому Lenovo Thinkpad T430, UEFI BIOS 1.16, я бачу:

Привіт Світ

Тепер, коли ми зробили мінімальну програму, переходимо до привітного світу.

Очевидне питання: як зробити IO? Кілька варіантів:

  • попросіть прошивку, наприклад, BIOS або UEFI, чи робити це для нас
  • VGA: спеціальна область пам'яті, яка надрукується на екрані, якщо записана на. Можна використовувати в захищеному режимі.
  • написати драйвер і поговорити безпосередньо з обладнанням дисплея. Це "правильний" спосіб це зробити: більш потужний, але складніший.
  • серійний порт . Це дуже простий стандартизований протокол, який надсилає та витягує символи з хост-терміналу.

    Джерело .

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

    Це справді прикро, оскільки такі інтерфейси дуже корисні, наприклад, для налагодження ядра Linux .

  • використовувати функції налагодження чіпів. ARM називає їх напівхостинг, наприклад. Що стосується реального обладнання, воно потребує додаткової апаратної та програмної підтримки, але на емуляторах це може бути безкоштовна зручна опція. Приклад .

Тут ми зробимо приклад BIOS, як це простіше на x86. Але зауважте, що це не самий надійний метод.

головний.С

.code16
    mov $msg, %si
    mov $0x0e, %ah
loop:
    lodsb
    or %al, %al
    jz halt
    int $0x10
    jmp loop
halt:
    hlt
msg:
    .asciz "hello world"

link.ld

SECTIONS
{
    . = 0x7c00;
    .text :
    {
        __start = .;
        *(.text)
        . = 0x1FE;
        SHORT(0xAA55)
    }
}

Зберіть і зв’яжіть із:

gcc -c -g -o main.o main.S
ld --oformat binary -o main.img -T linker.ld main.o

Результат:

введіть тут опис зображення

Тестовано на: Lenovo Thinkpad T430, UEFI BIOS 1.16. Диск, створений на хості Ubuntu 18.04.

Окрім стандартних інструкцій зі зборки, ми маємо:

  • .code16: повідомляє GAS виводити 16-бітний код

  • cli: вимкнути переривання програмного забезпечення. Це може змусити процесор знову почати працювати післяhlt

  • int $0x10: робить виклик BIOS Це те, що друкує персонажів один за одним.

Важливими прапорами посилань є:

  • --oformat binary: виводить необроблений двійковий код складання, не перекручуйте його у файлі ELF, як це стосується звичайних виконуваних файлів.

Використовуйте C замість складання

Оскільки C збирається до складання, використовуючи C без стандартної бібліотеки досить просто, вам в основному просто потрібно:

  • сценарій посилання, щоб розмістити речі в пам’яті в потрібному місці
  • прапори, які повідомляють GCC не використовувати стандартну бібліотеку
  • крихітна точка вступу для складання, яка встановлює необхідний стан C main, зокрема:
    • стек
    • нуль BSS

TODO: посилання на такий приклад x86 на GitHub. Ось ARM, який я створив .

Речі стають веселішими, якщо ви хочете використовувати стандартну бібліотеку, оскільки у нас немає ядра Linux, яке реалізує більшу частину стандартної функціональності бібліотеки С через POSIX .

Кілька можливостей, не звертаючись до повномасштабної ОС, як Linux, включають:

  • Ньюліб

    Детальний приклад за адресою: https://electronics.stackexchange.com/questions/223929/c-standard-libraries-on-bare-metal/223931

    У Newlib вам належить реалізувати системні дзвінки самостійно, але у вас виходить дуже мінімальна система, і їх дуже легко реалізувати.

    Наприклад, ви можете переспрямувати printfна системи UART або ARM або реалізувати за exit()допомогою напівгостінгу .

  • вбудовані операційні системи, такі як FreeRTOS та Zephyr .

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

    Їх можна розглядати як свого роду попередньо реалізований Newlib.

ARM

У ARM загальні ідеї однакові. Я завантажив:

Для Raspberry Pi https://github.com/dwelch67/raspberrypi виглядає як найпопулярніший підручник, доступний сьогодні.

Деякі відмінності від x86 включають:

  • IO робиться шляхом прямого написання на магічні адреси, немає inі outінструкцій.

    Це називається IO пам'яті, відображеної в пам'яті .

  • для деяких справжніх апаратних засобів, таких як Raspberry Pi, ви можете самостійно додати прошивку (BIOS) до образу диска.

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

Прошивка

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

Насправді спочатку працює так звана прошивка , яка є програмним забезпеченням:

  • виготовлені виробниками обладнання
  • як правило, із закритим джерелом, але ймовірно, на основі С
  • зберігається в пам'яті лише для читання, і тому важче / неможливо змінити без згоди продавця.

Добре відомі прошивки включають:

  • БІОС : стара загальноприйнята прошивка x86. SeaBIOS - це реалізація з відкритим кодом за замовчуванням, що використовується QEMU.
  • UEFI : наступник BIOS, краще стандартизований, але більш здатний і неймовірно роздутий.
  • Coreboot : шляхетна спроба з відкритим вихідним кодом

Прошивка робить такі речі:

  • петлю на кожен жорсткий диск, USB, мережу тощо, поки ви не знайдете щось завантажувальне.

    Коли ми запускаємо QEMU, -hdaкаже, що main.imgце жорсткий диск, підключений до обладнання, і

    hda є першою, яку випробували, і вона використовується.

  • завантажте перші 512 байти на адресу оперативної пам’яті 0x7c00, покладіть туди RIP процесора та нехай він працює

  • показувати на дисплеї такі речі, як меню завантаження або виклики друку BIOS

Прошивка пропонує ОС-подібний функціонал, від якого залежить більшість ОС. Наприклад, підмножина Python перенесена для запуску в BIOS / UEFI: https://www.youtube.com/watch?v=bYQ_lq5dcvM

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

Як каже цей розробник CoreOS :

Важка частина

Коли ви вмикаєте ПК, мікросхеми, що складають чіпсет (northbridge, southbridge та SuperIO), ще не ініціалізуються належним чином. Навіть незважаючи на те, що BIOS ROM настільки віддалений від процесора, як це міг бути, це доступно процесору, оскільки він повинен бути, інакше процесор не матиме інструкцій виконувати. Це не означає, що BIOS ROM повністю відображений, як правило, ні. Але достатньо лише відобразити карту, щоб розпочати процес завантаження. Будь-які інші пристрої, просто забудьте про це.

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

Опублікувати початковий стан BIOS

Як і багато речей у техніці, стандартизація є слабкою, і одна з речей, на яку не слід покладатися, - це початковий стан регістрів, коли ваш код починає працювати після BIOS.

Тож зробіть собі прихильність і використовуйте код ініціалізації, наприклад: https://stackoverflow.com/a/32509555/895245

Реєстри подобаються %dsі %esмають важливі побічні ефекти, тому вам слід скасувати їх, навіть якщо явно їх не використовуєте.

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

GNU GRUB Multiboot

Завантажувальні сектори прості, але вони не дуже зручні:

  • на диску ви можете мати лише одну ОС
  • код навантаження повинен бути дійсно невеликим і вміщуватися в 512 байт. Це можна вирішити за допомогою виклику BIOS int 0x13 .
  • вам доведеться зробити багато запуску самостійно, як перейти в захищений режим

Саме з цих причин GNU GRUB створив більш зручний формат файлу під назвою multiboot.

Мінімальний робочий приклад: https://github.com/cirosantilli/x86-bare-metal-examples/tree/d217b180be4220a0b4a453f31275d38e697a99e0/multiboot/hello-world

Я також використовую його на моїх прикладах репортажу GitHub, щоб мати можливість легко запускати всі приклади на реальному обладнання, не спалюючи USB мільйон разів. У QEMU це виглядає приблизно так:

введіть тут опис зображення

Якщо ви готуєте вашу ОС як багатозавантажувальний файл, то GRUB зможе знайти її у звичайній файловій системі.

Саме це робить більшість дистрибутивів, підкладаючи зображення ОС /boot.

Файли з багатозавантажуваними файлами - це в основному файл ELF зі спеціальним заголовком. Вони визначені GRUB за адресою: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html

Ви можете перетворити багатозавантажений файл на завантажувальний диск за допомогою grub-mkrescue.

El Torito

Формат, який можна записати на компакт-диски: https://en.wikipedia.org/wiki/El_Torito_%28CD-ROM_standard%29

Можна також створити гібридне зображення, яке працює на ISO або USB. Це можна зробити за допомогою grub-mkrescue( приклад ), а також зробити ядро ​​Linux при make isoimageвикористанні isohybrid.

Ресурси

  • http://wiki.osdev.org - прекрасне джерело для цих питань.
  • https://github.com/scanlime/metalkit - це більш автоматизована / загальна система збирання голого металу, яка забезпечує крихітний спеціальний API
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.