実行中のバイナリの修正

実行中のバイナリの修正

a.out開発時にバイナリが長いタスクを実行するため、バックグラウンドでバイナリを実行する状況に頻繁に直面します。その過程でa.out生成され、再コンパイルされたCコードを変更しましたa.out。これまで何の問題もありませんでした。実行中のプロセスはa.out競合なしに通常どおり実行され、最初に開始された以前のコードを常に実行します。

しかし、a.outこれがおそらくRAMサイズほどの巨大なファイルであるとします。この状況ではどうなりますか?共有オブジェクトファイルにリンクされていると仮定すると、実行時に変更するとどうlibblas.soなりますか?libblas.soどうしたの?

私の主な質問は - OSがa.out元のコードを実行するときに元のコードが常に正しく実行されることを保証することです。オリジナルバイナリ(リンクされたバイナリやファイルのサイズに関係なく、そのファイルとファイルが実行時に変更されても.so.o.so

私は次の質問が同様の問題に対処することを知っています。 https://stackoverflow.com/questions/8506865/when-a-binary-file-runs-does-it-copy-its-entire-binary-data-into-memory-at-once 実行中にスクリプトを編集するとどうなりますか? プログラムの実行中にどのようにリアルタイムアップデートを行うことができますか?

これは私がこれをよりよく理解するのを助けましたが、彼らは私に正確に私が望むものが何であるかを尋ねていたようではありません。これが結果の一般的な規則です。実行中のバイナリの修正

ベストアンサー1

最初はスタックオーバーフローの質問が適切に見えましたが、あなたの意見でそれについてまだ質問がある理由を理解しています。私にとっては、これが正確に見えるものです。緊急の瞬間2つのUNIXサブシステム(プロセスとファイル)が通信するときに関連します。

ご存知のように、UNIXシステムは通常、ファイルサブシステムとプロセスサブシステムの2つのサブシステムに分けられます。カーネルは、システムコールを介して他に指示しない限り、これら2つのサブシステムが互いに対話することを許可しないでください。しかし、1つの例外があります。実行可能ファイルをプロセスにロードします。テキストエリア。もちろん、この操作もexecveシステムコール()によって引き起こされると言うことができますが、通常これは一つプロセスサブシステムがファイルサブシステムに暗黙的に要求する状況。

プロセスサブシステムは基本的にファイルを処理できないため(そうでなければ全体を2つの部分に分割することは意味がありません)、ファイルにアクセスするためにファイルサブシステムが提供するすべてを使用する必要があります。これはまた、プロセスサブシステムがファイルの編集/削除に関してファイルサブシステムによって実行されたすべての操作に従うことを意味します。この時点で私は読むことをお勧めしますザイルズの答え到着このU&Lの質問。私の答えの残りの部分は、Gilesのより一般的な答えに基づいています。

最初に注目すべき点は、内部的にファイルを介してのみ渡すことができることです。インデックスノード。カーネルにパスが与えられたら、最初のステップはそれを他のすべての操作のためのinodeに変換することです。プロセスが実行可能ファイルをメモリにロードすると、パス変換後にファイルサブシステムによって提供されるinodeを介してロードされます。インデックスノードは複数のパス(リンク)に関連付けることができ、プログラムはリンクのみを削除できます。ファイルとそのinodeを削除するには、ユーザースペースはinodeへの既存のリンクをすべて削除し、完全に使用されないようにする必要があります。これらの条件が満たされると、カーネルは自動的にディスクからファイルを削除します。

見たら実行ファイルの置き換えGillesの答えのいくつかによって異なることがわかります。編集/削除する方法ファイルの場合、カーネルは常にファイルサブシステム内に実装されたメカニズムを介してさまざまな方法で応答/適応します。

  • 最初の戦略を試してみると(開く/0に切り取り/書き込みまたは新しいサイズで開く/書き込み/切り取り)、カーネルが要求処理を妨げないことがわかります。エラー26が発生します。テキストファイルが使用中です。ETXTBSY)。結果はありません。
  • ストラテジー2を試している場合、最初のステップは実行可能ファイルを削除することです。ただし、プロセスで使用されているため、ファイルサブシステムが起動し、ファイル(および対応するinode)が本当にディスクから削除します。この時点から古いファイルの内容にアクセスする唯一の方法は、inodeを介することです。これは、プロセスサブシステムが新しいデータをロードする必要があるたびに実行する操作です。テキスト部分(内部的にパスをinodeに変換しない限り、パスを使用することは意味がありません。)あなたが持っていても接続解除ファイル(すべてのパスを削除)を削除しても、プロセスは何もしていないかのようにそのファイルを引き続き使用できます。古いパスを使用して新しいファイルを作成しても、何も変更されません。新しいファイルにはまったく新しいinodeがあり、実行中のプロセスはこれについて何も知りません。

戦略2と3は実行可能ファイルにも安全です。実行中の実行可能ファイル(および動的にロードされたライブラリ)は、ファイル記述子があるという点で開かれたファイルではありませんが、非常に似た方法で動作します。プログラムがコードを実行している間は、ディレクトリエントリがなくてもファイルはディスクに残ります。

  • mv戦略3は、作業が原子的であるという点で非常に似ています。これにはシステムコールを使用する必要がありrename、プロセスはカーネルモードで中断することができないため、完了するまで(成功かどうか)この操作を妨げることはありません。同様に、古いファイルの inode は変更されません。新しいファイルが作成され、すでに実行されているプロセスは、以前のinodeへのリンクの1つとすでに関連付けられていても、これについては知りません。

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

ファイルの再コンパイル:これを使用するとgcc(他の多くのコンパイラでも同様に動作できます)、戦略2を使用します。straceコンパイラプロセスを実行すると、これを確認できます。

stat("a.out", {st_mode=S_IFREG|0750, st_size=8511, ...}) = 0
unlink("a.out") = 0
open("a.out", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
chmod("a.out", 0750) = 0
  • コンパイラはstat、システムコールを介してlstatファイルがすでに存在するかどうかを検出します。
  • ファイルは接続解除。ここでは name からアクセスできなくなりますが、すでにa.out実行中のプロセスでその inode とコンテンツを使用している限り、ディスクに残ります。
  • 新しいファイルを作成し、名前で実行権限を付与しますa.out。これは、すでに実行中のプロセスで気にする必要がない新しいinodeで新しいコンテンツです。

これで共有ライブラリにも同じ動作が適用されます。プロセスがライブラリオブジェクトを使用している限り、その接続をどのように変更してもディスクから削除されません。何かをメモリにロードする必要があるたびに、カーネルはファイルのinodeを介してこれを行うので、リンクへの変更(新しいファイルとの関連付けなど)を無視します。

おすすめ記事