プログラムの実行中にライブ更新を実行する方法を教えてください。


15

ThunderbirdやFirefoxなどのキラーアプリケーションは、実行中にシステムのパッケージマネージャーを介してどのように更新できるのでしょうか。更新中の古いコードはどうなりますか?実行中にそれ自体を更新するプログラムa.outを作成する場合、何をする必要がありますか?



@derobert正確にはそうではありません。そのスレッドは実行可能ファイルの特性には入りません。関連する背景を提供しますが、重複するものではありません。
ジル 'SO-悪であるのをやめる' 14年

@Gillesさて、残りのものを残すと広すぎます。ここには2つの(少なくとも)質問があります。そして、もう1つの質問は非常に近いものです。更新するためにファイルを置き換えることがUnixで機能する理由を尋ねています。
デロバート14年

本当に自分のコードでこれを実行したい場合は、「ホットな」コード更新が重要な機能であるプログラミング言語Erlangをご覧ください。learnyousomeerlang.com/relups
mattdm

1
@Gilles BTW:あなたが返信として追加したエッセイを見たので、私は投票を取り消しました。この質問は、更新がどのように機能するかを知りたい人を指すのに適した場所になりました。
デロバート14年

回答:


19

一般的なファイルの置き換え

まず、ファイルを置き換えるいくつかの戦略があります。

  1. 既存のファイルを書き込み用に開き、長さ0に切り捨てて、新しいコンテンツを書き込みます。(あまり一般的ではない方法として、既存のファイルを開き、古いコンテンツを新しいコンテンツで上書きし、ファイルが短い場合は新しい長さに切り捨てます。)シェルの用語では:

    echo 'new content' >somefile
    
  2. 古いファイルを削除し、同じ名前で新しいファイルを作成します。シェル用語で:

    rm somefile
    echo 'new content' >somefile
    
  3. 一時的な名前で新しいファイルに書き込んでから、新しいファイルを既存の名前に移動します。移動すると、古いファイルが削除されます。シェル用語で:

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

戦略間のすべての違いをリストするのではなく、ここで重要なものをいくつか挙げます。stategy 1では、プロセスがファイルを現在使用している場合、プロセスは更新中の新しいコンテンツを確認します。プロセスがファイルの内容が同じままであることを期待している場合、これにより混乱が生じる可能性があります。これは、ファイルを開いているプロセス(lsofまたはで表示されている、ドキュメントを開いている(たとえば、エディターでファイルを開く)対話型アプリケーション)についてのみであり、通常はファイルを開いたままにせず、 「ドキュメントを開く」操作で、「ドキュメントを保存する」操作中に(上記の戦略の1つを使用して)ファイルを置き換えます。/proc/PID/fd/

戦略2および3では、一部のプロセスがファイルをsomefile開いている場合、コンテンツのアップグレード中に古いファイルが開いたままになります。戦略2では、ファイルを削除する手順は、実際にはディレクトリ内のファイルのエントリのみを削除します。ファイル自体は、それにつながるディレクトリエントリがなく(同じファイルに対して複数のディレクトリエントリがある場合がありますプロセスが開いていない場合にのみ削除されます。これを確認する方法を次に示します。sleepプロセスが強制終了されたときにのみファイルが削除されます(rmディレクトリエントリのみが削除されます)。

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

戦略3では、新しいファイルを既存の名前に移動するステップにより、古いコンテンツにつながるディレクトリエントリが削除され、新しいコンテンツにつながるディレクトリエントリが作成されます。これは1つのアトミック操作で行われるため、この戦略には大きな利点があります。プロセスがいつでもファイルを開く場合、古いコンテンツまたは新しいコンテンツが表示されます。混合コンテンツやファイルが取得されないリスクはありません既存。

実行可能ファイルの置き換え

Linuxで実行中の実行可能ファイルを使用して戦略1を試行すると、エラーが発生します。

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つのファイルのアップグレードはアトミックですが、アプリケーションが複数のファイル(プログラム、ライブラリ、データなど)で構成されている場合、アプリケーション全体のアップグレードはそうではありません。次の一連のイベントを検討してください。

  1. アプリケーションのインスタンスが開始されます。
  2. アプリケーションがアップグレードされます。
  3. 実行中のインスタンスアプリケーションは、そのデータファイルの1つを開きます。

手順3では、アプリケーションの古いバージョンの実行中のインスタンスは、新しいバージョンからデータファイルを開いています。これが機能するかどうかは、アプリケーション、ファイルの種類、ファイルの変更量によって異なります。

アップグレード後、古いプログラムがまだ実行されていることに気付くでしょう。新しいバージョンを実行する場合は、古いプログラムを終了して新しいバージョンを実行する必要があります。パッケージマネージャーは通常、アップグレード時にデーモンを強制終了して再起動しますが、エンドユーザーアプリケーションはそのままにします。

いくつかのデーモンには、デーモンを強制終了して新しいインスタンスが再起動するのを待たずにアップグレードを処理する特別な手順があります(これによりサービスが中断します)。これはinitの場合に必要です。これはkillできません。initシステムは、実行中のインスタンスexecveが新しいバージョンで自分自身を置き換えるために呼び出すことを要求する方法を提供します。


「新しいファイルを既存の名前に移動します。移動すると古いファイルが削除されます。」unlink後で説明するように、これはちょっとした混乱です。「既存の名前を置き換える」かもしれませんが、それでもやや混乱します。
デロバート14年

@derobert「リンク解除」という用語に重点を置きたくありませんでした。ディレクトリエントリを参照して「削除」を使用していますが、これについては後で説明します。その段階で混乱しますか?
ジル 'SO-悪であるのをやめる' 14年

おそらく、リンク解除を説明するために余分な段落または2つ上の説明を正当化するほど混乱しない。私は、混乱を招かないだけでなく技術的に正しい表現を期待しています。たぶん「remove」を再度使用するだけかもしれませんが、それはすでに説明するためのリンクを張っていますか?
デロバート14年

2

アップグレードはプログラムの実行中に実行できますが、表示される実行中のプログラムは実際には古いバージョンです。古いバイナリは、プログラムを閉じるまでディスクに残ります。

説明: Linuxシステムでは、ファイルは単なるiノードであり、ファイルへの複数のリンクを持つことができます。例えば。/bin/bashあなたが見るにはちょうどリンクであるinode 3932163私のシステムで。リンクls --inode /path上で発行することにより、どのiノードが何かにリンクしているのを見つけることができます。ファイル(inode)は、それを指すリンクがゼロであり、どのプログラムでも使用されていない場合にのみ削除されます。パッケージマネージャーがアップグレードするとき。/usr/bin/firefox、最初にリンクを解除(ハードリンクを削除/usr/bin/firefox)して/usr/bin/firefoxから、別のiノード(新しいfirefoxバージョンが含まれるiノード)へのハードリンクであるという新しいファイルを作成します。古いiノードは現在空きとしてマークされており、新しいデータを保存するために再利用できますが、ディスク上に残ります(iノードはファイルシステムを構築するときにのみ作成され、削除されることはありません)。次の開始時にfirefox、新しいものが使用されます。

実行中に「アップグレード」するプログラムを作成する場合、考えられる唯一の解決策は、独自のバイナリファイルのタイムスタンプを定期的にチェックし、プログラムの開始時間よりも新しい場合は、それ自体をリロードすることです。


1
実際、これはUnixでファイルを削除(リンク解除)する方法が原因です。unix.stackexchange.com/questions/49299/…を参照してくださいまた、少なくともLinuxでは、実行中のバイナリに実際に書き込むことはできません。「テキストファイルビジー」エラーが表示されます。
デロバート14年

奇妙な...そしてどのように。Debianのaptアップグレード作業?IceweaselFirefox)を含む問題なく実行中のプログラムをアップグレードできます。
プシモン14年

2
APT(または、むしろdpkg)はファイルを上書きしません。代わりにそれらのリンクを解除し、同じ名前で新しいものを配置します。説明については、リンク先の質問と回答をご覧ください。
デロバート14年

2
まだRAMにあるだけでなく、ディスクにもあります。ファイルは、プログラムの最後のインスタンスが終了するまで実際には削除されません。
デロバート14年

1
このサイトでは、編集を提案することはできません(担当者が十分に高ければ、編集を行うだけで、提案はできなくなります)。したがって、コメントとして:Unixシステム上のファイル(inode)には通常1つの名前があります。ただし、ln(ハードリンク)を使用して名前を追加すると、より多くの名前を使用できます。rm(リンク解除)で名前を削除できます。実際にファイルを直接削除することはできません。名前のみを削除してください。ファイルに名前がなく、さらに開いていない場合、カーネルはそのファイルを削除します。実行中のプログラムには開いている状態で実行されているファイルがあるため、すべての名前を削除した後でも、ファイルは残っています。
デロバート14年

0

ThunderbirdやFirefoxなどのキラーアプリケーションは、実行中にシステムのパッケージマネージャーを介してどのように更新できるのでしょうか。 まあ、これは本当にうまく機能しないことを伝えることができます...パッケージの更新の実行中にFirefoxを開いたままにしておくと、Firefoxがひどく壊れてしまいました。ときどき強制的に殺し、再起動しなければなりませんでした。なぜなら、それはあまりにも壊れていて、適切に閉じることさえできなかったからです。

更新中の古いコードはどうなりますか? 通常、Linuxではプログラムがメモリに読み込まれるため、プログラムの実行中にディスク上の実行可能ファイルは必要ありません。実際、実行可能ファイルを削除することもできますが、プログラムは気にする必要はありません...ただし、一部のプログラムは実行可能ファイルを必要とし、特定のOS(Windowsなど)は実行可能ファイルをロックし、削除を防止したり、名前の変更や移動を行います。プログラムが実行されています。Firefoxは実際には非常に複雑であり、GUI(ユーザーインターフェイス)の構築方法を指示する多数のデータファイルを使用しているため、壊れています。パッケージの更新中にこれらのファイルは上書き(更新)されるため、(メモリ内の)古いFirefox実行可能ファイルが新しいGUIファイルを使用しようとすると、奇妙なことが起こります...

実行中にそれ自体を更新するプログラムa.outを作成する場合、何をする必要がありますか? あなたの質問にはすでにたくさんの答えがあります。これをチェックしてくださいhttps : //stackoverflow.com/questions/232347/how-should-i-implement-an-auto-updater ところで、StackOverflowではプログラミングに関する質問の方が適しています。


1
実行可能ファイルは、実際にはデマンドページング(スワップ)です。それらは完全にメモリにロードされるわけではなく、システムが他の何かのためにRAMを必要とするときはいつでもメモリから削除されるかもしれません。古いバージョンは実際にはディスクに残っています。unix.stackexchange.com/questions/49299/…を参照してください。少なくともLinuxでは、実行中の実行可能ファイルに実際に書き込むことはできません。「text file busy」というエラーが表示されます。rootでもできません。(ただし、Firefoxについてはかなり正しいです)。
デロバート
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.