Про операционную систему House

В одной давней уже дискуссии речь зашла о том, что все современные операционные системы – говно, и true-программисты должны писать все на правильных языках, а именно – на Haskell. В качестве примера правильной ОС была приведена система House – как утверждает Википедия, “экспериментальная операционная система, написанная целиком на функциональном языке программирования Haskell”.

Я не сторонник подхода “а давайте везде использовать ООП/функции высших порядков/монады (манды?)/Haskell/чистый ассемблер/придумать-и-вписать-что-то-свое” – особенно когда объявляется, что только этот подход позволит избавиться от багов, ускорить десятикратно работу программистов, и главное – является единственно верным. Именно это заставило меня со скептицизмом отнестись к заявлению [info]udpn о том, что Haskell – правильный язык для написания операционных систем, после чего я пообещал “посмотреть” на пресловутый House.

Разумеется, пообещав, я благополучно записал это обещание в свой импровизированный todo list, где оно и протерялось. Вспомнить про него меня заставил вчерашний срач, начатый на хабре силами [info]vit_r и продолженный в ЖЖ [info]metaclass.

Как известно, в любых компьютерных программах “волшебным образом” заводятся ошибки. С ними можно поступить тремя способами – грубо говоря, Abort, Retry и Ignore. За третий способ бьют морду уже в программистских ПТУ, так что речь пойдет о двух более принятых в приличном обществе стратегиях. Более подробно об этом можно почитать по ссылкам выше, а также найти в ЖЖ [info]vit_r расширенный вариант хабровской записи. Я же вернусь к теме операционных систем.

Стратегии “обработки ошибок”, которые я назвал abort и retry, появились, видимо, уже на заре программирования. В операционной системе MULTICS, созданной в середине 60-х, по разного рода пересказам одних и тех же жареных фактов в учебниках по операционным системам, большую часть кода занимала обработка ошибок – то есть попытки восстановить состояние системы, “несмотря ни на что”. Это можно отнести к стратегии “retry”.

Другой вариант был реализован в “потомке” MULTICS – точнее, в самых первых версиях Unix. При каких-либо странных ситуациях система просто “впадала в панику” и благополучно “грохалась”. Сформулированный в виде “Если надо выйти из строя, делайте это шумно и как можно быстрее”, этот подход обычно относят к “философии UNIX” вообще.

Естественно, что для “операционных систем вообще” желательно “выходить из строя” как можно реже. Именно этому посвящено и замечание [info]migmit с последовавшей небольшой дискуссией про микроядра в целом (как один из способов совместить fail fast и общую “отказоустойчивость”) и горячо мной любимый Minix в частности.

vityaz

Если верить адептам Haskell-ной веры, то в вопросе “налево пойдешь – коня потеряешь, направо пойдешь – сам пропадешь” “Abort или Retry?” имеется еще один вариант – якобы их истинно правильный язык программирования сам не дает допустить ошибку. В качестве примера такого применения Haskell и была приведена ОС House.

К сожалению, в данном случае мы имеем дело с совершенно искренним заблуждением о том, что используя “правильные инструменты”, можно полностью оградить себя от ошибок в программах. Говоря терминами из провокационной записи [info]vit_r, так ведут себя “гении”. Это поведение очень похоже на разнообразных новоявленных великих фотографов, которые покупают в “Эльдорадо” в кредит недорогую зеркалку и ведут себя, как Картье-Брессоны. К сожалению, ни дорогой фотоаппарат, ни Haskell в руках “идиота” не превратят его в “гения”.

Вернемся непосредственно к House. Архитектура этой системы, согласно ее описанию, довольно проста. Это “микроядерная” операционная система, с явным разделением “политики” (реализованной на Haskell) и “механизма” (на C с ассемблерными вставками). Ядро ОС – это примерно 1200 тысячи строчек на Haskell и 750 строчек на C. На C реализованы некоторые компоненты, необходимые для работы Haskell-ного рантайма и “низкоуровневого” взаимодействия с “железом”.

Сишная часть реализует в этой операционке специальную монаду H – от Hardware, позволяющую хаскелльному коду выполнять операции с такими штуками, как страницы памяти или обработчики прерываний. Кроме того, микроядро на Haskell умеет взаимодействовать с драйверами устройств – тоже написанными на Haskell, но выполняющимися не в Ring 0, а в нормальном “пользовательском” Ring 3. В общем, классическое микроядро, ничего особенного.

Правда, весь кайф обламывает вот какой момент. Ядро тащит с собой весь рантайм от GHC, который оценивается где-то в 50 тысяч строк кода на Си. Там, в этом рантайме, находится хаскельное расширение Concurrent Haskell – которое в этой ОС выполняет функции планировщика и менеджера процессов. Получается типичный пример “каши из топора” – с тем же успехом House можно называть “написанной на Си”. Более того, trusted computing base оказывается просто громадной по сравнению, скажем, с Minix, где все ядро – это около 7 тысяч строк на Си с небольшими ассемблерными вставками.

Очень сложно говорить о написании ОС на языках с “богатым” рантаймом, как у Haskell. Как только в этом рантайме реализованы потоки – то теряется смысл разговора о планировщике, он реализуется исключительно средствами, встроенными в язык. Если рантайм окажется, так сказать, POSIX-эквивалентным, то говорить о разработке ОС на таком языке уже как-то смешно.

Что интересно – от ошибок не спасает даже Haskell. В более поздней статье с описанием этой операционки приводится пример типичной ошибки с “граничными условиями” – вместо 4096 байт менеджер памяти пытался обнулить 4097, правда, с оговорочкой – мол, если бы мы аккуратно использовали вывод типов, то этой ошибки и не возникло бы. Так используйте, кто мешает?

В целом – проект заслуживает внимания, как своего рода “технический курьез”, типа построенного без единого гвоздя деревянного здания. Правда, при внимательном рассмотрении гвозди все же обнаруживаются – но тщательным образом замаскированы. Более интересно тут другое – что в “настоящей” микроядерной ОС системное и прикладное программирование мало чем отличаются. В принципе, в том же Minix никто не мешает написать системный компонент, типа драйвера устройства или даже планировщика задач на каком-нибудь экзотическом для этой задачи языке. А что, вас не устроит драйвер сетевой карты на Common Lisp?

2 комментария

  1. const.me пишет:

    Очень напоминает Singularity.

    • Если только идеей использовать “нетрадиционный” язык программирования.

      В Singularity вся межпроцессная изоляция осуществляется только за счет фич .NET и проверки инвариантов на этапе инсталляции. Использование для этого возможностей “железа” выглядит как-то надежней.