web-dev-qa-db-ja.com

実行中にバイナリを変更する

開発中にバイナリファイルを実行している状況によく遭遇します。バックグラウンドでa.outと言うと、時間がかかるためです。その間、a.outを生成したCコードに変更を加え、a.outを再度コンパイルします。これまでのところ、問題はありません。 a.outを実行しているプロセスは通常どおり継続し、クラッシュすることはなく、常に最初に開始した元のコードを実行します。

ただし、a.outは巨大なファイルであり、RAMのサイズに匹敵する可能性があります。この場合はどうなりますか?そして、それが共有オブジェクトファイルlibblas.soにリンクしていると言います。実行中にlibblas.soを変更するとどうなりますか?どうなるの?

私の主な質問は-OSがa.outを実行すると、originalのように、元のコードが常に正常に実行されることを保証しますかバイナリ、またはリンクされている.soファイルのサイズに関係なく、それらの.oおよび.soファイルが実行時に変更された場合でも、

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

これは私がこれについてもう少し理解するのに役立ちましたが、彼らが私が何を望んでいるかを正確に尋ねているとは思いません、これは実行中にバイナリを変更する結果の一般的なルールです

10
texasflood

スタックオーバーフローの質問は最初は十分であるように見えましたが、あなたのコメントから、なぜこれについてまだ疑問があるのか​​理解できます。私にとって、これはまさに2つのUNIXサブシステム(プロセスとファイル)が通信するときに発生する重大な状況の一種です。

ご存知のように、UNIXシステムは通常、ファイルサブシステムとプロセスサブシステムの2つのサブシステムに分かれています。さて、システムコールを通じて他の方法で指示されない限り、カーネルはこれらの2つのサブシステムが互いに対話するべきではありません。ただし、例外が1つあります。実行可能ファイルをプロセスにロードするテキスト領域です。もちろん、この操作はシステムコール(execve)によってもトリガーされると主張する人もいますが、これは通常、プロセスがoneの場合であることがわかっています。サブシステムは、ファイルサブシステムに対して暗黙的な要求を行います。

プロセスサブシステムには当然ファイルを処理する方法がないため(そうでない場合、全体を2つに分割しても意味がありません)、ファイルサブシステムが提供するものを使用してファイルにアクセスする必要があります。これはまた、プロセスサブシステムが、ファイルの編集/削除に関してファイルサブシステムがとる措置に提出されることを意味します。この点で、私は Gillesの回答 から このU&Lの質問 を読むことをお勧めします。私の残りの答えは、Gillesからのこのより一般的なものに基づいています。

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

Gillesの回答の置換実行可能ファイルの部分を見ると、編集方法に応じて、/deleteファイル。カーネルは、常にファイルサブシステム内に実装されたメカニズムを介して、異なる反応/適応を行います。

  • 戦略1(open/truncate to zero/writeまたはopen/write/truncate to new size)を試してみると、カーネルがリクエストを処理する必要がないことがわかります。エラー26が表示されます:Text file busyETXTBSY)。結果はまったくありません。
  • 戦略2を試す場合、最初のステップは実行可能ファイルを削除することです。ただし、プロセスによって使用されているため、ファイルサブシステムは起動し、ファイル(およびそのiノード)がディスクから本当に削除されるのを防ぎます。この時点から、古いファイルのコンテンツにアクセスする唯一の方法は、そのiノードを介してアクセスすることです。これは、プロセスサブシステムが新しいデータをテキストセクションにロードする必要があるときに必ず実行することです (内部的には、パスをiノードに変換する場合を除いて、パスを使用しても意味がありません)。ファイルをリンク解除した(すべてのパスを削除した)場合でも、プロセスは何もしないかのようにファイルを使用できます。古いパスで新しいファイルを作成しても、何も変更されません。新しいファイルには、実行中のプロセスが認識していない完全に新しいiノードが付与されます。

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

  • mv操作はアトミック操作であるため、戦略3は非常に似ています。これには、おそらくrenameシステムコールを使用する必要があります。カーネルモードではプロセスを中断できないため、処理が完了するまで(成功したかどうかにかかわらず)この操作を妨げることはありません。繰り返しになりますが、古いファイルのiノードは変更されません。新しいファイルが作成され、古いiノードのリンクの1つに関連付けられている場合でも、すでに実行中のプロセスはそれを認識しません。

戦略3では、新しいファイルを既存の名前に移動するステップで、古いコンテンツにつながるディレクトリエントリが削除され、新しいコンテンツにつながるディレクトリエントリが作成されます。これは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システムコールによってファイルがすでに存在することを検出します。
  • ファイルはunlinkedです。ここでは、a.outという名前ではアクセスできなくなりましたが、すでに実行中のプロセスで使用されている限り、そのiノードとコンテンツはディスクに残ります。
  • 新しいファイルが作成され、a.outという名前で実行可能になります。これはまったく新しいiノードであり、すでに実行中のプロセスでは問題にならないまったく新しいコンテンツです。

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

11
John WH Smith

私の理解では、実行中のプロセスのメモリマッピングにより、カーネルはマップされたファイルの予約済み部分を更新できません。プロセスが実行中の場合、そのファイルのすべてが予約されているため、ソースの新しいバージョンをコンパイルすると、実際に新しいiノードのセットが作成されるため、更新されます。つまり、古いバージョンの実行可能ファイルは、ページフォールトイベントを通じてディスク上でアクセス可能なままです。したがって、巨大なファイルを更新しても、プロセスが実行されている限り、ファイルshouldは引き続きアクセス可能であり、カーネルshouldはそのままのバージョンを表示します。元のファイルのiノードすべきではないプロセスが実行されている限り、再利用できます。

もちろんこれは確認する必要があります。

2
user86969

.jarファイルを置き換える場合、これは常に当てはまるわけではありません。 JARリソースと一部のランタイムリフレクションクラスローダーは、プログラムが明示的に情報を要求するまでディスクから読み込まれません。

Jarはメモリにマップされる単一の実行可能ファイルではなく単にアーカイブであるため、これは単なる問題です。これは少し話題から外れていますが、それでもあなたの質問と私が自分の足で撃った何かの派生物です。

実行ファイルの場合:はい。 jarファイルの場合:多分(実装によって異なります)。

2
Zhro