Назад | Перейти на главную страницу

Можете ли вы обновить пакет, пока он еще работает?

У нас есть пакет Debian, который мы хотим обновить. Он устанавливает набор java-файлов, которые запускаются как служба systemd.

Можно ли обновить пакет, пока он еще работает? Насколько я понимаю, да, это возможно.

Как это работает? Загружается ли весь пакет в память, а затем заменяются банки, пока он все еще работает из памяти?

Почему я могу обновить пакет, пока он еще работает?

В мире Unix файловые системы разделяют имена файлов и их содержимое, и удаление имени файла всегда разрешено, если у вас есть соответствующие разрешения. Содержимое файла удаляется, когда больше не остается ссылок (имен файлов и дескрипторов файлов).

В dpkg программа не знает и не заботится о запущенных службах. Обновление пакета работает следующим образом:

  1. Распаковать новые файлы, добавив .dpkg-new к именам файлов
  2. Создайте жесткие ссылки для старых файлов, добавив .dpkg-old суффикс (это создает дополнительную ссылку)
  3. Переименуйте новые файлы в имена собственные, заменив ссылку на старое имя файла.
  4. Удалите файлы с .dpkg-old суффикс. Любое содержимое файла, на которое в этом случае нет ссылок, удаляется здесь.

Это снижает вероятность ошибок, потому что имена файлов без суффиксов затрагиваются только на третьем шаге, а шаги 1 и 2 имеют наибольший потенциал для ошибок (например, диск заполнен), имена файлов без суффиксов всегда относятся к старым или новое имя и время несогласованности пакета сведено к минимуму.

Для работающих служб, сохраняющих файлы открытыми, это означает, что они будут продолжать использовать старое содержимое файла, но если они закроют и снова откроют файл, они получат новое содержимое.

Для традиционных служб Unix это не большая проблема, потому что они в основном состоят из исполняемых файлов и разделяемых библиотек, все из которых остаются открытыми, потому что они отображены в памяти в адресном пространстве процесса, поэтому обновление пакета не мешает им в наименее.

Для программ Java, будет ли это затронуто, зависит от того, закрывает ли интерпретатор Java и повторно открывает файлы JAR между ними. Обычно разработчики пакетов перестраховываются, закрывают службу перед обновлением и перезапускают ее после этого, не рискуя загружать классы службы из разных версий в один и тот же экземпляр службы.

Итак, ответ: это зависит от сервиса и JVM.

Код Java обычно загружается вместо отображения памяти, поэтому обычно нет необходимости держать дескрипторы файлов открытыми, и работающая служба будет использовать код, который она изначально загрузила, и не заботиться об измененных файлах JAR.

Обычно он не загружает весь архив JAR в память, а только копирует код для отдельных классов по мере их использования. Обычно этот код впоследствии запускается через JIT-компилятор, а служба запускается в скомпилированной версии, поэтому код в памяти в любом случае не похож на версию на диске.

В идеале JVM будут отображать файлы JAR, которые они используют, в свое адресное пространство, что позволит сохранить ссылку на файл, открытый и связанный с процессом, что позволит им загружать классы из согласованного набора файлов JAR, и это позволит сводка после обновления о том, какие службы используют для работы устаревшие файлы.

Этот отчет представляет собой список процессов, порожденных служебными модулями systemd, которые имеют дескрипторы файлов, у которых не осталось имен (поэтому их содержимое будет удалено после закрытия всех этих дескрипторов). Его точность зависит от процессов, которые фактически держат дескрипторы файлов открытыми (что для программ C дается, потому что файлы отображаются в адресное пространство процесса).

Если вы хотите увидеть, какие файлы в настоящее время открыты процессом службы (или любым другим процессом, если на то пошло), используйте ЛСОФ (8) команда для получения списка.

tl; dr: Для Java это в основном безопасно, если ваша служба не загружает и не выгружает классы все время (в этом случае, что не так с этой службой?).

При обновлении работающей в данный момент службы старая версия, находящаяся в памяти, также не обновляется. Старая версия будет продолжать работать в памяти, пока не будет остановлена ​​или перезапущена. В следующий раз, когда вы запустите службу, она загрузит обновленную версию в память.

Спросите, почему вам разрешено это делать, ну ... это вопрос разработчиков. Конечно, было бы хорошо, если бы dpkg / apt предупреждал пользователей о запущенных службах при обновлении пакетов. Я знаю, что он предупреждает пользователя, если dpkg необходимо перезапустить службу, но не если она уже запущена.