Администрирование → Переосмысление PID 1. Часть 2

перевод
houk 12 августа в 16:48 8,7k
Оригинал: Lennart Poettering


Распараллеливание служб сокетов


Этот вид синхронизации при загрузке приводит к опоследовательности (последовательный запуск служб) существенной части процесса загрузки. Не было бы круто если бы мы могли избавиться от цены синхронизации и опоследовательности? Что ж, мы можем на самом деле избавиться. Для этого, нам необходимо понять, что на самом деле службы (демоны) требуют друг от друга, и почему их запуск откладывается. Для традиционных демонов (служб) Unix, есть только один ответ на этот вопрос: они ждут до тех пор, пока демон предоставляющий свои службы не будет готов принимать соединения. Обычно это AF_UNIX сокет в файловой системе, но это может также быть AF_INET сокет. Для примера: клиенты D-Bus ждут /var/run/dbus/system_bus_socket, чтобы сконнектиться к нему, клиенты syslog ждут /dev/log, клиенты CUPS ждут /var/run/cups/cups.sock и NFS точки монтирования ждут /var/run/rpcbind.sock и порт IP портмаппера и т.д. А теперь задумайтесь об этом, на самом деле есть только одна вещь чего ждут остальные.

Так как это лежит в основе того, что следует далее, позвольте мне сказать это снова, но другими словами: если вы запускаете syslog и различных клиентов syslog одновременно, что произойдет в схеме обозначенной выше, так это то, что сообщения от клиентов будут добавлены в буфер /dev/log. До тех пор, пока буфер не переполнится, клиенты не должны ждать пока syslog закончит загрузку, они вытащат все сообщения из очереди и обработают их. Другой пример: мы запускаем D-Bus и несколько клиентов одновременно. Если отправлен синхронный запрос к шине, следовательно, будет ожидаться ответ, также синхронно, и произойдет то, что клиент будет заблокирован, тем не менее только один единственный этот клиент (пославший синхронный запрос) и только до тех пора пока D-Bus не отловит запрос и не обработает его.

В общем, буфер сокетов ядра помогают увеличить распараллеливание, и упорядочивание и синхронизация делается ядром, без какого-либо вмешательства юзерспейса! Но если все сокеты будут доступны до момента загрузки демонов, управление зависимостями также становится избыточным (по крайней мере второстепенным): если демон нуждается в другом демоне, он просто сконнектится с ним. Если другой демон уже запущен, это незамедлительно будет успешным. Но если другой демон еще не запущен, а находится в процессе запуска, первый демон даже не должен ждать его, пока он не сделает синхронный зарос. Даже если другой демон вообще не запущен, он может быть запущен автоматически. С точки зрения первого демона для него нет никакой разницы, следовательно, управление зависимостями становится практически ненужным или второстепенным, и все они оптимально распараллелены и опционально с загрузкой по требованию. На верхушке сего, все еще круче, потому что сокеты остаются доступны, тогда как на самом деле демон может стать временно недоступным (может быть вследствие «обрушения»). В действительности, вы можете запуститься, а затем остановиться (или упасть), и снова запуститься, и снова остановиться (и так далее), и все это без оповещения клиентов и потери каких-либо запросов с стороны клиентов.

Хороший момент, чтобы сделать паузу и пойти налить себе еще кофейка, и будьте уверены, что далее нас ждет материал еще более интересный.

Но в начале, давайте проясним несколько вещей: это какая-то новая логика? Нет, конечно же нет. Наиболее многообещающая система, которая работает как эта — это launchd от Apple: в MacOS прослушивание сокетов и запуск всех демонов делает launchd. Следовательно, все службы (демоны) могут запуститься параллельно и без необходимости в сконфигурированных зависимостях. И это в действительности изобретательное решение и главная причина почему MacOS предоставляет фантастическое время загрузки. Я крайне рекомендую это видео где ребята из launchd объясняют, что и как они делают. К сожалению, эта идея не была признана за пределами кампуса Apple.

Идея, на самом деле старее чем launchd. До launchd многоуважаемый inetd работал в схожем стиле: сокеты в основном создаются в демонах, которые запускает реальную службу (основной так сказать функционал), передавая дескриптор файла сокета функции exec(). Тем не менее, фокус inetd в основном был направлен не на локальные службы и демоны, а на службы Интернета (поздние реализации также поддерживают AF_UNIX сокет). inetd не был инструментом распараллеливания процесса загрузки системы или разрешения зависимостей.

Для сокетов TCP inetd в основном использовался следующим образом: для каждого входящего соединения создавался новый экземпляр демона. Это означает, что для каждого соединения инициализировался и создавался новый процесс, что не назовешь рецептом для высокопроизводительных серверов. Тем не менее, с самого начала inetd также поддерживал и другой режим работы, где единственный демон создавался на первом соединении, и этот единственный экземпляр принимал последующие соединения (вот для чего был опции wait/nowait в inetd.conf, и эта была очень плохо документированная опция, к сожалению). Запуск демона на каждое соединение плохо сказался на репутации inetd, заклеймив его слишком медленным. Но это не совсем справедливо.

Распараллеливание служб шины


В современных службах Linux прослеживается тенденция предоставлять службы через D-Bus взамен плоским AF_UNIX сокетам. Теперь, вопрос в следующем, можем ли мы применить ту же логику распараллеливания логики, как и для традиционных служб сокетов? Да, мы можем, D-Bus уже имеет все нужные механизм для этого: используя шину активации служба может быть запущена, когда в первый раз к ней обратятся. Шина активации также предоставляет нам минимальные функции синхронизации на каждый запрос, необходимый для запуска провайдеров и потребителей служб D-Bus одновременно: если мы хотим запустить Avahi одновременно с CUPS (отвлеченная заметка: CUPS использует Avahi чтобы обнаружить mDNS/DNS-SD принтеры), тогда мы можем запустить их одновременно, и если CUPS быстрее чем Avahi через логику активации шины, D-Bus поставит в очередь запрос до тех пора, пока Avahi занят, тем чтобы установить свое сервисное имя на шине.

Итак, таким образом: сокет-основанные активация службы и шина-основанная активация службы, вместе позволяют нам запустить все демоны параллельно, без какой-либо дальнейшей синхронизации. Активация также позволяет нам делать «ленивую» загрузку сервисов: если служба (демон) редко используется, мы можем просто запустить его на первое обращение к сокету или к имени на шине, вместо того чтобы запустить его во время загрузки системы.

И если это не великолепно, то тогда я не знаю, что великолепно!

Продолжение следует…
Проголосовать:
+12
Сохранить: