Как можно выполнить обновление в реальном времени во время работы программы?

Как можно выполнить обновление в реальном времени во время работы программы?

Интересно, как можно обновить такие крутые приложения, как Thunderbird или Firefox, через системный менеджер пакетов, пока они еще работают. Что происходит со старым кодом, пока они обновляются? Что мне делать, если я хочу написать программу a.out, которая обновляется сама по себе, пока она работает?

решение1

Замена файлов в целом

Во-первых, существует несколько стратегий замены файла:

  1. Открытьсуществующий файл для записи, обрезать его до нулевой длины и записать новое содержимое. (Менее распространенный вариант — открыть существующий файл, перезаписать старое содержимое новым, обрезать файл до новой длины, если он короче.) В терминах оболочки:

    echo 'new content' >somefile
    
  2. Удалятьстарый файл и создайте новый файл с тем же именем. В терминах оболочки:

    rm somefile
    echo 'new content' >somefile
    
  3. Запишите в новый файл под временным именем, затемдвигатьсяновый файл к существующему имени. Перемещение удаляет старый файл. В терминах оболочки:

    echo 'new content' >somefile.new
    mv somefile.new somefile
    

Я не буду перечислять все различия между стратегиями, я просто упомяну некоторые, которые здесь важны. В stategy 1, если какой-либо процесс в данный момент использует файл, процесс видит новое содержимое по мере его обновления. Это может вызвать некоторую путаницу, если процесс ожидает, что содержимое файла останется прежним. Обратите внимание, что это касается только процессов, у которых файл открыт (как видно в lsofили в ; интерактивные приложения, у которых открыт документ (например, открытие файла в редакторе), обычно не держат файл открытым, они загружают содержимое файла во время операции «открыть документ» и заменяют файл (используя одну из стратегий выше) во время операции «сохранить документ»./proc/PID/fd/

При использовании стратегий 2 и 3, если какой-либо процесс открыл файл somefile, старый файл остается открытым во время обновления контента. При использовании стратегии 2, шаг удаления файла фактически удаляет только запись файла в каталоге. Сам файл удаляется только тогда, когда у него нет записи каталога, ведущей к нему (в типичных файловых системах Unix может бытьболее одной записи каталога для одного и того же файла)иНи один процесс не открыл его. Вот способ это наблюдать — файл удаляется только тогда, когда процесс sleepзавершается ( rmудаляется только его запись в каталоге).

echo 'old content' >somefile
sleep 9999999 <somefile &
df .
rm somefile
df .
cat /proc/$!/fd/0
kill $!
df .

При использовании стратегии 3 шаг перемещения нового файла в существующее имя удаляет запись каталога, ведущую к старому контенту, и создает запись каталога, ведущую к новому контенту. Это делается одной атомарной операцией, поэтому эта стратегия имеет большое преимущество: если процесс в любой момент откроет файл, он увидит либо старый контент, либо новый контент — нет риска получить смешанный контент или несуществующий файл.

Замена исполняемых файлов

Если вы попробуете применить стратегию 1 с запущенным исполняемым файлом в Linux, вы получите ошибку.

cp /bin/sleep .
./sleep 999999 &
echo oops >|sleep
bash: sleep: Text file busy

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

В Linux вы можете перезаписать код динамически загружаемой библиотеки. Это, скорее всего, приведет к сбою программы, которая ее использует. (Вы можете не заметить этого, sleepпоскольку она загружает весь необходимый ей код библиотеки при запуске. Попробуйте более сложную программу, которая делает что-то полезное после сна, например perl -e 'sleep 9; print lc $ARGV[0]'.)

Если интерпретатор запускает скрипт, файл скрипта открывается интерпретатором обычным способом, поэтому нет защиты от перезаписи скрипта. Некоторые интерпретаторы читают и анализируют весь скрипт, прежде чем начать выполнять первую строку, другие читают скрипт по мере необходимости. СмотритеЧто произойдет, если отредактировать скрипт во время его выполнения?иКак Linux справляется со скриптами оболочки?Больше подробностей.

Стратегии 2 и 3 безопасны и для исполняемых файлов: хотя исполняемые исполняемые файлы (и динамически загружаемые библиотеки) не являются открытыми файлами в смысле наличия файлового дескриптора, они ведут себя очень похожим образом. Пока какая-то программа запускает код, файл остается на диске даже без записи каталога.

Обновление приложения

Большинство менеджеров пакетов используют стратегию 3 для замены файлов из-за ее главного преимущества, упомянутого выше: в любой момент времени открытие файла приводит к получению его действительной версии.

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

  1. Запускается экземпляр приложения.
  2. Приложение обновлено.
  3. Запущенный экземпляр приложения открывает один из своих файлов данных.

На шаге 3 запущенный экземпляр старой версии приложения открывает файл данных из новой версии. Работает это или нет, зависит от приложения, от того, какой это файл и насколько сильно файл был изменен.

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

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

решение2

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

Объяснение:в системах Linux файл — это просто inode, на который может быть несколько ссылок. Например, вы /bin/bashвидите, это просто ссылка на inode 3932163в моей системе. Вы можете узнать, какой inode что-то ссылается на, выполнив команду ls --inode /pathна ссылке. Файл (inode) удаляется только в том случае, если на него не указывает ни одна ссылка, и он не используется какой-либо программой. Когда менеджер пакетов обновляется, например, /usr/bin/firefox, он сначала отсоединяется (удаляет жесткую ссылку /usr/bin/firefox), затем создает новый файл с именем /usr/bin/firefox, который является жесткой ссылкой на другой inode (тот, который содержит новую firefoxверсию). Старый inode теперь помечен как свободный и может быть повторно использован для хранения новых данных, но остается на диске (inode создаются только при построении файловой системы и никогда не удаляются). При следующем запуске firefoxбудет использоваться новый.

Если вы хотите написать программу, которая «обновляется» во время работы, то единственное возможное решение, которое я могу придумать, — это периодически проверять временную метку ее собственного двоичного файла, и если она новее времени запуска программы, то перезагружать себя.

решение3

Интересно, как можно обновлять такие крутые приложения, как Thunderbird или Firefox, через системный менеджер пакетов, пока они все еще работают? Ну, я могу сказать, что это не очень хорошо работает... У меня Firefox ужасно ломался, если я оставлял его открытым во время обновления пакета. Иногда мне приходилось принудительно убивать его и перезапускать, потому что он был настолько сломан, что я даже не мог его нормально закрыть.

Что происходит со старым кодом во время обновления? Обычно в Linux программа загружается в память, поэтому исполняемый файл на диске не нужен и не используется, пока программа работает. Фактически, вы даже можете удалить исполняемый файл, и программе это не должно быть важно... Однако некоторым программам может понадобиться исполняемый файл, и определенные ОС (например, Windows) блокируют исполняемый файл, предотвращая удаление или даже переименование/перемещение, пока программа работает. Firefox ломается, потому что он на самом деле довольно сложен и использует кучу файлов данных, которые сообщают ему, как строить свой GUI (пользовательский интерфейс). Во время обновления пакета эти файлы перезаписываются (обновляются), поэтому, когда старый исполняемый файл Firefox (в памяти) пытается использовать новые файлы GUI, могут происходить странные вещи...

Что мне нужно сделать, если я хочу написать программу a.out, которая обновляется во время работы? На ваш вопрос уже есть много ответов. Посмотрите это: https://stackoverflow.com/questions/232347/how-should-i-implement-an-auto-updater Кстати, вопросы по программированию лучше задавать на StackOverflow.

Связанный контент