実行中の実行可能ファイルの上書きまたは1つ以上の実行可能プログラムで使用されている共有ライブラリ(.so)ファイルの上書きに関する質問があります。
以前は、実行中の実行可能ファイルを上書きすることは明らかな理由で機能しませんでした。この状況に対処する特定のerrno値ETXTBSYもあります。
しかし、長い間、私は誤って実行中の実行可能ファイルを上書きしようとすると(たとえば、実行中のビルドの最後のステップを実行して)動作することがわかりましたcc -o exefile
!exefile
だから私の質問は、これがどのように機能するのか、どこに文書化されているのか、そしてそれに頼っても安全です。
ld
この場合、エラーを排除するために、誰かが出力ファイルのリンクを解除して新しいファイルを作成するように調整したようです。この作業を常に実行するのか、それとも必要なときに実行するのか(つまり、既存のファイルを上書きしようとし、ETXTBSYに遭遇した後)はわかりません。ld
マニュアルページにはこれに関する言及はありません。 (誰がld
今ハードリンクを破ったり、ファイルの所有権を変更したりする可能性などについて不平を言わないのだろうか?)
cc
付録:この質問は/に関するものではありませんld
(最終的には答えの重要な部分になりましたが)。質問は実際に「なぜこれ以上ETXTBSYを見ることができないのですか?これはまだバグですか?」です。はい、まだバグです。実際には珍しいバグです。 (私の質問に投稿した明確な答えも参照してください。)
答え1
これはカーネルによって異なり、一部のカーネルでは実行可能ファイルの種類によって異なりますが、すべての最新システムはETXTBSY( "テキストファイルbusy") 書き込み用に実行中の実行可能ファイルを開こうとしているか、書き込み用に開いているファイルを実行しようとすると、ドキュメントに記載されています。BSDでは常にこのようなことがありました。、しかし初期のソラリスではそうではありませんでした。(以降のバージョンでは、この保護機能を実装しました。)、私の記憶と一致します。 Linuxでは常にそうです。最小1.0。
実行可能ファイルに適用されるものは、動的ライブラリに適用されても適用されなくてもよい。動的ライブラリを上書きすると、実行可能ファイルを上書きするのとまったく同じ問題が発生します。コマンドは、まったく異なる内容を含む可能性がある新しいファイルの同じ古いアドレスから突然ロードされます。しかし、実際にはどこでもそうではありません。特に、Linuxでは、プログラムはopen
すべてのデータファイルと同じフラグを使用して内部的に動的ライブラリを開くためにシステムコールを呼び出します。Linuxでは、実行中のプロセスがいつコードをロードしてもライブラリファイルをオーバーライドできます。
ほとんどのカーネルでは、ファイルの開いたり書き込んだりしてファイルを削除したり名前を変更したりするのと同じように、実行中にファイルを削除して名前を変更できます。開いたファイルと同様に、実行中に削除されたファイルは使用中、つまり実行可能ファイルの最後のインスタンスが終了するまで実際には記憶媒体から削除されません。 Linuxおよび* BSDではこれを許可しますが、SolarisおよびHP-UXでは許可しません。
ファイルを削除し、同じ名前で新しいファイルを作成することは非常に安全です。ロードするコードとそのコードを含む開いている(または実行する)ファイルとの間の関連付けは、ファイル名ではなくファイル記述子を介して行われます。rename
一時ファイルに書き込んだ後、そのファイルを所定の位置に移動する(既存のターゲットファイルをソースファイルにアトミックに置き換えるシステムコール)、これを自動的に実行できるという利点があります。無効または部分的に作成された実行可能ファイルを一時的に配置しないため、削除してから書き込み用に開くよりもはるかに優れています。
cc
出力ファイルを上書きするか、削除して新しいファイルを作成するかは、実装ld
によって異なります。 GCC(最小最新バージョン)とClangの両方がこれを行います。どちらの場合も、unlink
ターゲット(存在する場合)を呼び出してopen
新しいファイルを作成します。 (なぜ一時書き込み後に名前を変更しないのか疑問に思います。)
保護措置を除いて、この動作に依存しないことをお勧めします。なぜなら、この動作はすべてのシステムでは機能せず(おそらく実行可能ファイルについてはすべての最新のシステムで動作しますが、共有ライブラリでは動作しません)、一般的なツールチェーンでは動作しないからです。可能な限り最善の方法です。ビルドスクリプトは常に一時ファイルの下にファイルを生成し、デフォルトのツールがこれを行うかどうかわからない場合は、その場所に移動します。
答え2
ファイルハンドルが開いている間にファイルが削除されると、通常、最後のファイルハンドルが閉じられると、ファイルは削除の対象として表示されます。この時点で、ファイルはディレクトリリストに表示されなくなります(たとえば、出力には削除済みとしてマークされていますが、lsof
使用中のファイルとして表示されます)。
lsof
簡潔さと明確さのために、以下の内容が切り捨てられました。
$ cat - >> foo &
[1] 30779
$ lsof | grep 30779
cat 30779 ghoti 1w REG 252,0 0 262155 /home/ghoti/foo
[1]+ Stopped cat - >> foo
$ rm foo
$ ls foo
ls: cannot access 'foo': No such file or directory
$ lsof | grep 30779
cat 30779 ghoti 1w REG 252,0 0 262155 /home/ghoti/foo (deleted)
その場合は、実行中のfg
(削除された)ファイルに書き込むことができます。 (実際には、必要に応じてファイルを回復できます。foo
cat
/proc/30779/fd/1
cat
ファイルはまだ開いていますが、)。
答え3
私自身の質問に答えるには(または実際に質問を少し明確にしてから、明確な質問に対する答えを説明してください):
私の質問は、「再びETXTBSYを見ることができません。まだバグですか?それとも、最新のカーネルを使用すると、実行中の実行可能ファイルを文句なしに(何らかの方法で)実行するために削除せずに実行中の実行可能ファイルを上書きできますか? 「です。
私は実行中の実行可能ファイルに書き込むと、最新のカーネルがクールな書き込み時にコピーセマンティクスを実装していると真剣に疑い始めました。
しかしそれは真実ではない。 ETXTBSYはまだ間違いなくバグです。
私の混乱に対する答えは、実行中の実行可能ファイルへの書き込みが実際にはほとんど発生しないことです。新しい実行可能ファイルを所定の位置に移動すると(既存の実行可能ファイルはまだ実行中です)、実際に上書きすることはほとんどなく、常に削除して置き換えます。使用中の場合はmv
削除後に交換いたします。install
、または同様のものを使用する場合は、dpkg -i
削除して交換します。何らかの理由cp
でそれを使用しようとした場合にのみ上書きしようとします。以前のバージョンがまだ実行されているとETXTBSYが発生する危険性があります。
その後、静かな変更のおかげで、実行中の実行可能ファイルの上で作業をld
試みることも、「削除と置換」カテゴリに属します。cc -o
答え4
あなたの推測は正しいです。 ldは利用可能な新しいファイルに書き込みますls -li
。この-i
オプションは、各コンパイル後に変更される inode 番号を表示します。私は多くの人がハードリンクを壊すことに興味がないと思います。プログラムは通常コンパイルされないため、実行可能ファイルはldによって最終宛先として作成されます。