Содержание статьи
«Стрекоза» — необычное имя для операционной системы семейства BSD. И это неспроста: система действительно странная, неоднозначная и полная противоречивых технических решений. У DragonFly гибридное ядро, что сближает ее с микроядерными операционками, она использует инновационный для своего времени подход для работы на SMP-системах, в ее состав включена непривычная по дизайну, но очень эффективная файловая система HAMMER, обладающая возможностями ZFS/btrfs и способная работать на кластере.
Многоядерный бум
История DragonFly, как и история OpenBSD, началась с конфликта. Но если во втором случае конфликт был вызван не совсем джентльменским поведением человека, то в первом проблемой стало виденье будущего. В то время (начало 2000-х) кипела работа над следующей версией FreeBSD — 5.0, в которую должно было войти много новшеств (devfs, GEOM), а также были сделаны первые шаги для избавления от так называемой глобальной блокировки ядра (Big Giant Lock) при работе системы на многоядерных/многопроцессорных платформах (SMP).
Именно принятый план избавления от глобальной блокировки и стал причиной конфликта между разработчиками. Как известно, SMP-системы отличаются тем, что имеют единое адресное пространство для всех процессорных ядер. Другими словами, все ядра используют одну память, которая никак между ними не делится, всем доступно все. Возникает очевидная проблема: что будет, если два потока исполнения ядра попытаются получить доступ или изменить одну и ту же структуру данных (значение переменной sysctl, например) одновременно? Ответ: возникнет коллизия (поток, который должен был сделать это вторым, может сделать это первым, или наоборот — со всеми вытекающими отсюда последствиями). Самое простое решение этой проблемы: запретить исполнение всего кода ядра одновременно несколькими процессорами с помощью глобальной блокировки, как и было сделано в FreeBSD 4. Пока один процессор исполняет код ядра, второй ждет.
Само собой, такой подход неэффективен, и в FreeBSD 5 было запланировано начать постепенное избавление от этого костыля с помощью точечных блокировок (mutex), которые будут установлены на все сколько-нибудь значимые структуры данных. Так код ядра мог исполняться несколькими процессорами, а ждать приходилось только в случае одновременного доступа к одним и тем же структурам данных и подсистемам.
Но и у этого подхода было несколько недостатков. Во-первых, ядро превращалось в кучу кода с массой локов, многие из которых к тому же зависели друг от друга. Во-вторых, код становился малоэффективным: блокировки применялись повсеместно и без расчета на то, что определенные типы данных в разное время могут или не могут иметь проблемы с одновременным доступом, так как актуальны только для одного процессорного ядра. Например, процессы могут работать все на одном ядре (и тогда блокировка не нужна) или быть разбросаны по разным (блокировка нужна).
Один из активных разработчиков ядра FreeBSD и Linux Мэттью Диллон (Matthew Dillon) предложил решить эти и другие возможные проблемы, отказавшись от блокировок и заменив их на три ключевые технологии: механизм сообщений, привязку данных ядра к процессорам/ядрам и распараллеливание подсистем ядра между процессорами и ядрами.
Для реализации этих идей планировалось запустить отдельный планировщик процессов/потоков на каждый процессор. Для доступа к данным ядра предполагалось использовать механизм сообщений, который позволил бы избежать блокировок, обеспечил правильную последовательность выполнения операций доступа и не накладывал бы никакого оверхеда, когда несколько потоков, претендующих на доступ к одним и тем же данным, исполняются на одном процессоре. Код ядра по возможности планировалось распараллелить по разным процессорам/ядрам.
В результате можно было убить целый взвод зайцев одним выстрелом:
- Блокировки для доступа к данным процессов внутри ядра не требовались, так как данные структуры обслуживались независимо для каждого процессора.
- Блокировки для доступа к данным ядра в большинстве случаев не нужны, вместо них использовался бы механизм сообщений (поток просто отправляет запрос, и он ставится в очередь на обработку).
- Достигалась гораздо более высокая производительность благодаря распараллеливанию подсистем ядра: намного эффективнее запустить по одному стеку TCP/IP на каждое процессорное ядро и обеспечить их слаженную работу с помощью сообщений, чем расставлять по всему сетевому стеку блокировки или блокировать весь сетевой стек целиком.
Представители FreeBSD Core Team, однако, были категорически против столь глобальных и резких изменений и настаивали на использовании блокировок: нельзя просто так взять и сломать ядро топором. Мэттью же считал, что систему необходимо разом перевести на новые рельсы, потому как постепенный переход невозможен, а блокировки затянут дело на годы. Слово за слово, Диллон покидает Core Team и отправляется в свободное плаванье.
DragonFly
Так в июне 2003 года появляется новая операционная система DragonFly BSD, основанная на кодовой базе FreeBSD 4.8. Именно в ней Мэттью Диллон практически в одиночку реализует все свои идеи. Внутриядерный аллокатор памяти, сетевой код, подсистемы ввода-вывода и виртуальной файловой системы теперь разделены на несколько копий, по количеству процессоров/ядер, появляется позаимствованный из микроядерных систем механизм сообщений, а также сериализующие токены (Serializing tokens) вместо mutex в тех местах, где без блокировок обойтись нельзя (например, при доступе к тем же переменным sysctl).
Сериализующие токены работают по принципу очереди в кабинет к стоматологу: пришедший на прием человек (поток) берет талон (токен), ждет, пока взявший талон до него не покинет кабинет, и заходит только после него. Если же человек где-то задержался, все остальные люди не будут заблокированы в его ожидании, он просто потеряет свое место в очереди, и ему придется взять новый талон по возвращении. В случае потоков исполнения токены позволяют решить главную проблему традиционных блокировок — deadlock.
Все это, конечно же, появляется не сразу, и еще долгое время DragonFly продолжает проигрывать в производительности на многоядерных платформах другим системам. Однако тесты, проведенные на DragonFly BSD 3.2, показали отличную производительность системы. Но достигнут такой результат был спустя почти десять лет после основания проекта, и фактически система так и не стала более масштабируемой и производительной, чем Linux (который, кстати, уже успел вобрать в себя некоторые идеи Диллона).
Учитывая, однако, что ее разработкой занимается совсем крошечная команда, а также что в ней нет костылей и хаков, применяемых в Linux для обеспечения производительности того же PostgresSQL на многоядерных системах, это очень неплохой результат.
Fun fact
В ходе работы над DragonFly BSD в декабре 2011 года Мэттью Диллон выявил неизвестную ошибку в процессорах AMD, которая могла приводить к краху приложений. Через три месяца инженеры AMD подтвердили наличие ошибки.
HAMMER
В рамках проекта DragonFly развивается также и собственная 64-битная файловая система, предназначенная для обслуживания очень больших объемов данных (до эксабайта). Концептуально HAMMER очень похожа на btrfs и обладает следующими особенностями:
- автоматическое восстановление ошибок без использования fsck;
- multi-volume — одна файловая система может быть «размазана» по 256 разделам;
- поддержка дедупликации данных (одинаковые блоки данных будут объединены в один);
- контрольные суммы для обеспечения целостности данных и метаданных;
- ведение истории изменений с возможностью отката к прошлым версиям файлов;
- поддержка неограниченного количества снапшотов;
- поддержка вложенных файловых систем (pseudo-filesystem), ФС внутри ФС.
Последняя особенность HAMMER может пригодиться для зеркалирования данных, в том числе на другие сетевые узлы, благодаря встроенной поддержке NFS. Очень удобно использовать ее и для бэкапа. Она же, в сочетании со встроенной функцией экспорта по NFS, наделяет HAMMER некоторыми свойствами кластерных ФС: настроить репликацию данных на множество узлов здесь очень легко.
Интересно, что HAMMER работает не по принципу copy-on-write, когда любая запись данных в существующий блок приводит к созданию нового блока, сохраняя предыдущий в целостности. Поэтому механизм создания снапшотов в ней реализован по-другому. При создании снапшота просто все данные замораживаются, а все изменения производятся уже в новых блоках.
В данный момент работа над файловой системой остановлена и все силы брошены на разработку HAMMER2, построенной по принципу copy-on-write с полной поддержкой кластеров, в том числе в режиме master-master.
Другие особенности DragonFly
- Поддержка исключительно платформы x86_64.
- Возможность запустить ядро как обычный пользовательский процесс.
- Механизм swapcache, который позволяет кешировать данные и метаданные файловой системы в swap-разделе твердотельных дисков.
- Возможность заморозки и восстановления состояния процессов. Чтобы заморозить приложение, нажимаем Ctrl + E, для разморозки выполняем команду checkpt -r file.ckpt.
- Вариантные символические ссылки — то, куда указывает симлинк, контролируется переменной окружения.
- DNTPD — DragonFly Network Time Daemon, NTP-демон, написанный специально для DragonFly.
- DMA — DragonFly Mail Agent, простой SMTP-сервер.
- dm_target_crypt — полностью совместимая с Linux (LUKS) система прозрачного шифрования дисков.
- Утилита tcplay для работы с разделами и образами, зашифрованными с помощью TrueCrypt.
Выводы
История DragonFly пока не тянет на success story, это правда. Производительность ядра с реализацией всех инновационных идей Диллона хоть и намного выше, чем ядра FreeBSD, но уступает Linux. HAMMER при всей своей функциональности проигрывает btrfs и ZFS в скорости чтения/записи данных, а задуманная кластерная функциональность так и не была реализована. Однако стоит иметь в виду, что некоторые из обкатанных в DragonFly идей все-таки воплотились в жизнь, в том числе в ядре Linux и других системах.