У нас есть пакет Debian, который мы хотим обновить. Он устанавливает набор java-файлов, которые запускаются как служба systemd.
Можно ли обновить пакет, пока он еще работает? Насколько я понимаю, да, это возможно.
Как это работает? Загружается ли весь пакет в память, а затем заменяются банки, пока он все еще работает из памяти?
Почему я могу обновить пакет, пока он еще работает?
В мире Unix файловые системы разделяют имена файлов и их содержимое, и удаление имени файла всегда разрешено, если у вас есть соответствующие разрешения. Содержимое файла удаляется, когда больше не остается ссылок (имен файлов и дескрипторов файлов).
В dpkg
программа не знает и не заботится о запущенных службах. Обновление пакета работает следующим образом:
.dpkg-new
к именам файлов.dpkg-old
суффикс (это создает дополнительную ссылку).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 необходимо перезапустить службу, но не если она уже запущена.