Як запорожці операційну систему писали. Частина 0.1



Все почалося півтора місяці тому, коли я від нахлынувшего за результатами співбесід почуття свободи і вседозволеності вирішив написати нову 64-бітну операційну систему. 10 років тому у мене вже був подібний досвід, але тоді система була 32-х розрядної і мала іншими недоліками, які зараз-то я і вирішив виправити. Наприклад, у тій системі (DuS) не було memory management, тобто сторінкової організації, тому пам'ять доводилося розподіляти до запуску програм, що спричиняло незручності.

Але головне, там не було доступу до мережі, а як же можна працювати в системі, якщо там немає Інтернету?

Отже, система називається LDuS, літера L натякає на довгий (тобто 64-бітний) режим. Нещодавно з'явилася ОС Android L, де L означає те ж саме, там передбачена можливість роботи з 64-розрядною архітектурою ARM, а самі смартфони з такою архітектурою обіцяють ось-ось з'явитися. Те, що вийшло до даного моменту, я назвав версією 0.1.

Завантаження

Завантаження

Завантаження влаштована 4-х ступінчаста, якщо не більше: boot-сектор -> початковий завантажувач -> завантажувач LDuS -> ядро LDuS. Перші два етапи я взяв без змін з DuS, зокрема, початковий завантажувач дозволяє завантажувати файли з завантажувального диска, який повинен мати мою файлову систему dusfs. Завантажувач LDuS завантажує ядро, заповнює всі необхідні таблиці процесора, маппирует 1-й мегабайт пам'яті з тотожним адресами, переходить у 64-бітний режим і викликає ядро. Завантажувач невеликий, тому написана на асемблері fasm, де зручно перемикатися між різною розрядністю коду.

Я прийняв рішення після включення 64-бітного режиму ніколи (на відміну від DuS) не переходити в реальний режим для звернення до BIOSу. Якщо і доведеться навіщо-то звертатися, то варто зробити емуляцію режиму 8086. Тому карту пам'яті становить завантажувач (звертаючись до переривання int 15h BIOS'У) ще до переходу в 64-бітний режим і потім передає її ядра.

Модулі

Ядро і всі інші модулі написані на C і компілюються стандартним gcc. Оскільки віртуальний адресу займає 64 біта (в реальності 48), то кожному модулю можна присвоїти свій унікальний адресу, що містить геш-код імені модуля і деяку іншу інформацію, зокрема. модулі діляться на категорії за інтересами. Значить, лінкування бібліотек в процесі роботи системи не потрібна. При зверненні до бібліотечної функції ядро перехоплює переривання відсутності сторінки, за адресою знаходить бібліотеку і завантажує її. Щоб все це пояснити gcc, довелося освоїти мову його зв'язування і написати файл *.ld, куди заносяться адреси всіх використовуваних модулем об'єктів, його статичних змінних, а також самого модуля. Цей файл генерується програмою ldus з файлу *.ldus, де перераховані вихідні тексти, що входять у модуль, імпортовані модулі, а також експортовані і статичні об'єкти.

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

Трикутник Паскаля

Ядро

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

Завданням можна призначати два типи пріоритетів: відносний та абсолютний. Якщо є завдання з більш високим абсолютним пріоритетом, то завдання з більш низьким не виконуються взагалі, це актуально для обробників переривань. Відносний пріоритет дозволяє одним завданням виконуватися в заданий число раз швидше інших. Виходячи з відносних пріоритетів всіх запущених і очікують у даний момент задач обчислюються ваги, на які діляться тики процесора. А саме, вага завдання — це частка її відносного пріоритету в сумі всіх відносних пріоритетів.

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

Шлюзи

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

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

Перегляд файлів

Відображення пам'яті

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

Так само планується організувати роботу з файлами. В даний час є драйвер файлової системи в пам'яті (ramfs), в якому, по-перше, доступні у вигляді файлів модулі, вбудовані в ядро, а по-друге, можна створювати нові файли і каталоги, довжина кожного файлу обмежена половиною розміру оперативної пам'яті. Крім того, є загальна концепція файлової підсистеми і написані відповідні функції стандратной бібліотеки [f]open/[f]read/[f]write.

Якщо в класичному розумінні буферизований ввід-вивід (тобто функції fopen/fread/fwrite...) є надбудовою на базовим (open/read/write), то в LDuS вони знаходяться на одному рівні. Справа в тому, що файли у LDuS не читаються/пишуться, а тільки відображаються, тобто пам'ять, в якій эаписан файл, вклинюється в пам'ять процесу, яка бажає з ним працювати. А значить, з відкритим файлом завжди асоційований буфер, під яким розуміється відображення цього файлу в пам'ять.

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

Ввід-висновок

Є драйвер клавіатури і окремий драйвер відео VGA, поки тільки текстової частини, зате з квітами, цілих 16 класичних кольорів VGA! Я не став вмонтувати драйвер VGA в ядро, залишивши там тільки необхідну для ядра термінальну частину, а зробив окремий процес, все ж у нас микроядерная архітектура. Крім того, драйвер VGA на відміну від вбудованого в ядро дозволяє працювати з відеопам'яттю без перемикання завдань. А саме, сторінка відеопам'яті відображена у віртуальний простір процесу, тому він може самостійно змінювати вміст свого екрану, що значно швидше, ніж викликати функцію для виведення кожного символу, як це зроблено в ядерному драйвері. Слід уточнити, що відображена не сама апаратна відеопам'ять, оскільки операція запису в неї нешвидка, а її образ, тобто звичайна пам'ять. Перенесенням даних з способу активного екрану в справжню відеопам'ять періодично займається драйвер VGA.

В цьому ж драйвері зроблена підтримка 8 екранів, між якими можна перемикатися натисканням Ctrl-Alt-F1...F8. На екранах можна запускати незалежні паралельно працюючі завдання, навіть занадто незалежні, наприклад, з одного екрану можна вбити програму на іншому, оскільки ядро не розуміє і не збирається розуміти права доступу в Unix-стилі (користувачі, root і т. д.), а розуміє тільки, що хто породив процес, той його і може вбити, або батько, або дід і т. д. Так що виходить 8 незалежних операційних підсистем, що мають, щоправда, загальну файлову систему.

Commander

Оболонка

В даний час в LDuS є найпростіший інтерпретатор командного рядка (shell) і класичний набір програм у дусі Norton Commander, а саме, оболонка (DuS Commander), можливості якої задаються в конфігурації і визначаються набором супутніх програм, і дві супутні програми — переглядач і редактор текстів. Ці програми були написані давно для моєї ж 32-розрядної системи DuS, так що нововведенням є не самі вони, а тільки їх вбудовування в нову систему. Для встранивания був реалізований режим сумісності з 32-бітними програмами і написаний модуль, що реалізує деякі функції DuS.

далі

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

Вихідні тексти системи можна знайти на тут. Доступний образ завантажувального диска, який можна легко підключити до емулятора і відтворити показане на відео. Для запуску DuS Commander потрібно виконати команду tools/dc.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.