Поскольку устройство мы выпустили и даже отпраздновали вчера, можно рассказывать байки. А может и не байки,
а страшилки, но это уж как кому подумается. Сегодня рассказ о том, как на N900
используется виртуализация.
Обычно виртуализацию используют для того, чтобы экономить. Экономят на электроэнергии, на
расходах на покупку серверов, "уплотняют" нагрузку на единицу серверной мощности и
повышают ARPU в расчете на одного хостера. В N900 виртуализация используется для
реализации идей Робина Гуда -- отнять у богатых и раздать бедным. Или, как говорилось, у
нас все для человека и мы знаем имя этого человека -- Пользователь.
В ядре Linux поддерживаются несколько методов виртуализации, среди которых выделяется
виртуализация методом контейнеров. Для контейнеров характерна так называемая "легкая
виртуализация" -- все контейнеры работают под управлением одного и того же ядра, а вот
доступ к дисковому пространству, к пространству процессов и памяти у них разграничен. Один
контейнер не видит, что творится в другом контейнере. Из знакомых решений такого типа --
OpenVZ (и коммерческий вариант Virtuozzo от Parallels), Linux Vserver и еще несколько альтернатив.
Накладные расходы при такой виртуализации невелики, но в основном ядре Linux в чистом виде
контейнерная виртуализация не прижилась -- слишком много разных подходов, чтобы отдать
предпочтение какому-то одному. Вместо этого, разработчики разных контейнерных технологий,
наступив на горло собственной песне, смогли разделить свои решения на отдельные части и
выработать общие подходы. Два базовых подхода к контейнерам -- это учет ресурсов и
разделение пространств имен. Если с ресурсами все более-менее понятно, для каждого
контейнера ядро должно отдельно считать потребляемую память, выделяемые квоты на
использование процессора и так далее, то с пространствами имен ситуация более
интересна. Пространства имен важны не только для структуры хранения файлов на диске. Для
того, чтобы отдельные процессы в разных контейнерах не знали о существовании друг друга,
нужно пространство имен процессов. Для того, чтобы на одной машине могли работать
несколько сетевых приложений с потенциально пересекающимися, но независящими друг от друга
сетевыми стеками, нужно сетевое пространство. Да и дисковое пространство тоже должно быть
независимым.
Независимое дисковое пространство умеют в POSIX-совместимых системах уже довольно
давно. Изоляция "в chroot" -- нормальная практика с 1982 года, когда системный вызов
chroot(2) появился в том, что стало через 17 месяцев версией 4.2BSD -- для тестирования
установки и сборки системы. В девяностые в Plan 9 идея изоляции пространств имен была
доведена до логического конца, с изолированными сетевым пространством для каждого
процесса. В ядре Linux адаптация изолрованных пространств продолжается до сих пор,
отдельные типы пространств можно использовать довольно стабильно с 2.6.25.
А вот с подсчетом ресурсов долгое время было не очень. Фактически, для контейнерной
виртуализации нужно обсчитывать потребление всего, что только можно -- потребление памяти,
использования процессора, создаваемый сетевой трафик, дисковое пространство и так
далее. Идея привязать к каждому типу ресурса счетчик вылилась в довольно сложную на
практике задачу, так что самодельные подходы со временем были приведены к общему
знаменателю -- управляющим группам.
Control groups или cgroups, представляют собой как раз такой общий знаменатель. Тот или
иной вариант ресурса описывается определенным типом управляющей группы, а процессы в
системе могут быть добавлены в "пространство" соответствующей управляющей группы. В этом
случае использование ресурсов этими процессами будет подсчитано согласно и учтено.
Управляющие группы не дают еще возможность полностью реализовать контейнер, они позволяют
сгруппировать процессы и влиять на их поведение. Контейнер получается, когда вся группа
процессов, входящая в cgroup, помещается в отдельное пространство имен, а вокруг этого
выстраивается разнообразная инфраструктура, превращающая группу процессов в отдельную
систему, со своими терминалами, корневой файловой системой и сетевыми адаптерами.
Это очень схематичное описание, для приблизительного представления.
k001
может рассказать более подробно, а у меня совсем иная цель.
Вернемся к управляющим группам и ресурсам. В ядре Linux определено несколько типов
контролеров ресурсов, позволяющих обсчитывать их потребление. Разработка новых контролеров
ведется постоянно, как и усовершенствование существующих. С точки зрения системы с
ограниченными ресурсами, которую представляет из себя мобильное устройство, интересны
контролеры памяти, процессора и сети.
У системы с ограниченными ресурсами основная головная боль состоит в том, чтобы научиться
вовремя изыскивать ресурсы для тех, кому они нужны и отбирать их у тех, кто сейчас ими
пользоваться не должен. Крайне опасная в жизни, но очень полезная в мобильном телефоне
способность. Хорошо, что нет в телефоне демократии и оппозиции, а то бы Крылов с "а вы,
друзья, как ни садитесь..." наверняка был бы на каждой коробке невышедшего в продажу
устройства.
В случае с N900 все исполняемые процессы классифицируются определенным образом на тех, кто
помогает приложению, с которым сейчас работает Пользователь, и на тех, кто может и
подождать. Помощники получают "подпитку", в виде дополнительных памяти и циклов
процессора, а также приоритетов при планировании процессов на исполнение. Классификация --
дело довольно сложное, политики классификатора описаны в виде базы знаний на языке
Пролог. Неудобный для реализации "типичных алгоритмов" других задач, Пролог пришелся к
месту для классификации приоритетов.
На самом деле, этот классификатор в N900 также обслуживает и распределение звуковых
потоков между приложениями, определяя, кому и в какой момент времени можно звучать и куда
-- в колонки, наушники, радио-передатчик FM-диапазона и так далее. Расширение области его
применения для управление перебрасыванием процессов из одной приоритетной управляющей
группы в другую теперь уже кажется делом совершенно логичным, а вот еще полгода назад...
Впрочем, в этом месте можно и остановиться. Когда приходит телефонный звонок, все
незадействованные в его обработке процессы умирают -- на доли секунды у них отбирают
всякую возможность быть запланированными на исполнение, чтобы Пользователь смог увидеть и
услышать сигнал звонка. А когда Пользователь не смотрит на экран, потому что он выключен,
все незадействованные в обслуживании Пользователя процессы получают минимальный
приоритет. В результате, все ресурсы отдаются проигрывателю или тому, кому они в этот
момент нужны -- например, браузеру, который транслирует поток из интернет-радио. Все равно
кроме звука или отображения на телевизор пользователь больше делать ничего не будет --
экран-то выключен.
Все это сказывается положительным образом на энергопотреблении. Ядро бы и радо отключать
определенные аппаратные устройства и уходить в режимы сниженного энергопотребления, да
хорошо бы, чтобы приложения не заставляли невовремя из этих режимов выходить. Я теперь не
могу смотреть на скринсейверы на десктопах, "как же они расходуют-то энергию!". Так и
представляются толпы замызганых прерываний, в поте лица тягающие сигналами неповоротливых
и прожорливых приложения в топку Переключателя Контекста и Господина Батарейкина.
В результате, удается не только звонок вовремя доставлять, но и экономить батарейку,
причем существенно. В великом деле мелочей не бывает.