開発中にバイナリファイルを実行している状況によく遭遇します。バックグラウンドで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実行中にスクリプトを編集するとどうなりますか?プログラムの実行中にライブ更新を実行するにはどうすればよいですか?
これは私がこれについてもう少し理解するのに役立ちましたが、彼らが私が何を望んでいるかを正確に尋ねているとは思いません、これは実行中にバイナリを変更する結果の一般的なルールです
スタックオーバーフローの質問は最初は十分であるように見えましたが、あなたのコメントから、なぜこれについてまだ疑問があるのか理解できます。私にとって、これはまさに2つのUNIXサブシステム(プロセスとファイル)が通信するときに発生する重大な状況の一種です。
ご存知のように、UNIXシステムは通常、ファイルサブシステムとプロセスサブシステムの2つのサブシステムに分かれています。さて、システムコールを通じて他の方法で指示されない限り、カーネルはこれらの2つのサブシステムが互いに対話するべきではありません。ただし、例外が1つあります。実行可能ファイルをプロセスにロードするテキスト領域です。もちろん、この操作はシステムコール(execve
)によってもトリガーされると主張する人もいますが、これは通常、プロセスがoneの場合であることがわかっています。サブシステムは、ファイルサブシステムに対して暗黙的な要求を行います。
プロセスサブシステムには当然ファイルを処理する方法がないため(そうでない場合、全体を2つに分割しても意味がありません)、ファイルサブシステムが提供するものを使用してファイルにアクセスする必要があります。これはまた、プロセスサブシステムが、ファイルの編集/削除に関してファイルサブシステムがとる措置に提出されることを意味します。この点で、私は Gillesの回答 から このU&Lの質問 を読むことをお勧めします。私の残りの答えは、Gillesからのこのより一般的なものに基づいています。
最初に注意する必要があるのは、内部的に、ファイルにはinodesを介してのみアクセスできることです。カーネルにパスが指定されている場合、その最初のステップは、カーネルをiノードに変換して、他のすべての操作に使用することです。プロセスが実行可能ファイルをメモリにロードするときは、パスの変換後にファイルサブシステムによって提供されるiノードを介して実行されます。 iノードは複数のパス(リンク)に関連付けられている場合があり、プログラムはリンクのみを削除できます。ファイルとそのiノードを削除するには、ユーザーランドはそのiノードへの既存のリンクをすべて削除し、完全に未使用であることを確認する必要があります。これらの条件が満たされると、カーネルは自動的にファイルをディスクから削除します。
Gillesの回答の置換実行可能ファイルの部分を見ると、編集方法に応じて、/deleteファイル。カーネルは、常にファイルサブシステム内に実装されたメカニズムを介して、異なる反応/適応を行います。
ETXTBSY
)。結果はまったくありません。戦略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
システムコールによってファイルがすでに存在することを検出します。a.out
という名前ではアクセスできなくなりましたが、すでに実行中のプロセスで使用されている限り、そのiノードとコンテンツはディスクに残ります。a.out
という名前で実行可能になります。これはまったく新しいiノードであり、すでに実行中のプロセスでは問題にならないまったく新しいコンテンツです。さて、それが共有ライブラリになると、同じ動作が適用されます。ライブラリオブジェクトがプロセスによって使用されている限り、そのリンクをどのように変更しても、ディスクから削除されません。何かをメモリにロードする必要があるときはいつでも、カーネルはファイルのiノードを介してそれを行うので、リンクに加えた変更(それらを新しいファイルに関連付けるなど)を無視します。
私の理解では、実行中のプロセスのメモリマッピングにより、カーネルはマップされたファイルの予約済み部分を更新できません。プロセスが実行中の場合、そのファイルのすべてが予約されているため、ソースの新しいバージョンをコンパイルすると、実際に新しいiノードのセットが作成されるため、更新されます。つまり、古いバージョンの実行可能ファイルは、ページフォールトイベントを通じてディスク上でアクセス可能なままです。したがって、巨大なファイルを更新しても、プロセスが実行されている限り、ファイルshouldは引き続きアクセス可能であり、カーネルshouldはそのままのバージョンを表示します。元のファイルのiノードすべきではないプロセスが実行されている限り、再利用できます。
もちろんこれは確認する必要があります。
.jarファイルを置き換える場合、これは常に当てはまるわけではありません。 JARリソースと一部のランタイムリフレクションクラスローダーは、プログラムが明示的に情報を要求するまでディスクから読み込まれません。
Jarはメモリにマップされる単一の実行可能ファイルではなく単にアーカイブであるため、これは単なる問題です。これは少し話題から外れていますが、それでもあなたの質問と私が自分の足で撃った何かの派生物です。
実行ファイルの場合:はい。 jarファイルの場合:多分(実装によって異なります)。