Ubuntuで実行中のプログラムを移動できるのはなぜですか?


24

実行中のアクティブなプログラムを別のディレクトリに移動できることに気付きました。私の経験では、MacOやWindowsでは不可能でした。Ubuntuではどのように機能しますか?

編集:Macでは不可能だと思っていましたが、コメントで確認できるように思われます。多分Windowsでは不可能です。すべての答えをありがとう。


2
かなりのクロスサイト・デュープ:stackoverflow.com/a/196910/1394393
jpmc26

1
rename(2)OS Xで実行中の実行可能ファイルはできませんか?何が起こりますEBUSYか なぜ機能しないのですか?rename(2)のマニュアルページにはETXTBUSY、そのシステムコールについては記載されておらずEBUSY、ディレクトリ名の変更が可能なことのみが説明されているため、POSIXシステムが実行可能ファイルの名前変更を禁止することさえ知りませんでした。
ピーターコーデス

3
macOSアプリは、実行中に移動することもできます。ゴミ箱に入れないでください。NSBundleなどを介してそのようなURLを生成する代わりに、バイナリまたはバンドルされたリソースにファイルURLを変数として保存する場合など、一部のアプリはその後エラーになる可能性があります。macOSのPOSIX準拠であると思われます。
コンスタンティノツァロウハス

1
実際にはLinuxが意図したとおりに動作するため、何をしているのかを知っておく必要があります。:P
userDepth

2
私はそれについて考える別の方法は、なぜそれが不可能だと思いますか?Windowsで許可されていないからといって、プロセスがどのように機能するかなどが原因で根本的に不可能というわけではありません。
トーマス

回答:


32

分解させてください。

あなたが実行可能ファイルを実行すると、システムコールのシーケンスは、最も顕著なのは、実行されているfork()execve()

  • fork()(ほとんど)親の正確なコピーである呼び出しプロセスの子プロセスを作成し、両方とも同じ実行可能ファイルを実行します(コピーオンライトメモリページを使用するため、効率的です)。2回返します。親では、子PIDを返します。子では、0を返します。通常、子プロセスはすぐにexecveを呼び出します。

  • execve()引数として実行可能ファイルへのフルパスを取り、呼び出しプロセスを実行可能ファイルに置き換えます。この時点で、新しく作成されたプロセスは独自の仮想アドレス空間、つまり仮想メモリを取得し、そのエントリポイントで実行が開始されます(プラットフォームABIの新しいプロセスのルールで指定された状態)。

この時点で、カーネルのELFローダーは、システムコールを使用したかのように、実行可能ファイルのテキストセグメントとデータセグメントをメモリにマップしましたmmap()(共有読み取り専用マッピングとプライベート読み取り/書き込みマッピングをそれぞれ使用)。BSSは、MAP_ANONYMOUSを使用する場合と同様にマップされます。(ところで、簡単にするためにここでは動的リンクを無視しています。動的リンカーは、メインの実行可能ファイルのエントリポイントにジャンプする前にすべての動的ライブラリをopen()sおよびmmap()sします。)

新しく実行された()が独自のコードの実行を開始する前に、実際にディスクからメモリにロードされるページはわずかです。プロセスがその仮想アドレス空間のそれらの部分に触れる場合/その場合、必要に応じて、さらにページが要求されます。(ユーザー空間コードの実行を開始する前にコードまたはデータのページをプリロードすることは、パフォーマンスの最適化にすぎません。)


実行可能ファイルは、下位レベルのiノードによって識別されます。ファイルの実行が開始された後、カーネルは、開いているファイル記述子やファイルバックアップメモリ​​マッピングの場合のように、ファイル名ではなくiノード参照によってファイルの内容をそのまま保持します。そのため、実行可能ファイルをファイルシステムの別の場所、または別のファイルシステムに簡単に移動できます。補足として、プロセスのさまざまな統計情報を確認するに/proc/PIDは、ディレクトリ(PIDは指定されたプロセスのプロセスID)を覗きます。実行可能ファイルは/proc/PID/exe、ディスクからリンクされていなくても開くことができます。


では、動きを掘り下げましょう。

同じファイルシステム内でファイルを移動するとrename()、実行されるシステムコールはで、ファイルの名前を別の名前に変更するだけで、ファイルのiノードは同じままです。

一方、2つの異なるファイルシステムの間では、2つのことが起こります。

  • ファイルの内容は、最初に新しい場所にコピーされread()write()

  • その後、ファイルはソースディレクトリからリンク解除され、unlink()明らかにファイルは新しいファイルシステム上で新しいiノードを取得します。

rm実際にはunlink()、ディレクトリツリーから特定のファイルを削除するだけです。そのため、ディレクトリに対する書き込み権限があると、そのディレクトリからファイルを削除する十分な権利が得られます。

楽しみのために、2つのファイルシステム間でファイルを移動しunlink()、ソースからのファイルへのアクセス許可がない場合に何が起こるか想像してみてください。

さて、ファイルは最初に宛先にコピーされ(read()write())、その後、unlink()権限が不十分なため失敗します。したがって、ファイルは両方のファイルシステムに残ります!!


5
あなたはやや紛らわしい仮想メモリと物理メモリです。プログラムが物理メモリにロードされる方法の説明は不正確です。execシステムコールは、実行可能ファイルのさまざまなセクションを物理メモリにコピーするのではなく、プロセスの開始に必要なセクションのみをロードします。しばらくすると、必要なページがオンデマンドでロードされますが、おそらくそれよりも後になります。実行可能ファイルのバイトはプロセス仮想メモリの一部であり、読み取られる可能性があり、プロセスの全ライフサイクル中に再び読み取られる可能性があります。
jlliagre

@jlliagre編集済み。これが明確になったことを願っています。ありがとう。
heemayl

6
「プロセスはもうファイルシステムを使用していません」ステートメントはまだ疑わしいです。
jlliagre

2
ファイルシステム内の特定のファイルがファイル名で直接識別されないという基本的な理解は、はるかに明確である必要があります。
トールビョーンラヴンアンデルセン

2
更新にはまだ不正確があります。mmapそしてunmapシステムコールが負荷に使用し、オンデマンドでページをアンロードされていないそれらにアクセスするとき、ページはカーネルによってロードされているOSは、RAMが良い何か他のもののために使用されるだろうと感じたときにページがメモリからアンロードされ、ページフォールトを生成します。これらのロード/アンロード操作にはシステムコールは関与しません。
jlliagre

14

まあ、それはかなり簡単です。/ usr / local / bin / whoopdeedooという名前の実行可能ファイルを取得しましょう。これは、いわゆるiノード(Unixファイルシステム上のファイルの基本構造)への参照にすぎません。「使用中」とマークされるのはiノードです。

ファイル/ usr / local / whoopdeedooを削除または移動すると、移動(またはワイプ)されるのはiノードへの参照のみです。iノード自体は変更されません。それは基本的にそれです。

確認する必要がありますが、Mac OS Xファイルシステムでもこれを実行できると考えています。

Windowsは異なるアプローチを取ります。どうして?知るか...?私はNTFSの内部に精通していません。理論的には、ファイル名の内部構造への参照を使用するすべてのファイルシステムがこれを実行できるはずです。

私は認めすぎましたが、過度に単純化しましたが、ウィキペディアの「影響」のセクションを読んでください。


1
Windowsでショートカットを使用して実行可能ファイルを起動する場合、ショートカットを消去することもできます。= 3
レイ

2
いいえ、それはシンボリックリンクを拭くようなものです。他のコメントのどこかに、この動作はFATファイルシステムのレガシーサポートによるものであると述べられています。それはおそらく理由のように聞こえます。
jawtheshark

1
これは、iノードとは特に関係ありません。NTFSはMFTレコードを使用してファイルの状態を追跡し、FATはこれにディレクトリエントリを使用しますが、Linuxはこれらのファイルシステムでもユーザーの観点から同じように動作します。
ルスラン

13

他のすべての回答から欠落しているように見えることの1つは、いったんファイルが開かれ、プログラムが開いているファイル記述子を保持すると、そのファイル記述子が閉じられるまでファイルはシステムから削除されません

参照先のiノードを削除しようとすると、ファイルが閉じられるまで遅延します。同じまたは異なるファイルシステムで名前を変更しても、名前変更の動作に関係なく、開いているファイルに影響を与えることはできません。ファイルを台無しにする唯一の方法は、ファイルの名前変更や削除などのディレクトリに対する操作ではなく、明示的にそのiノードを開いて内容を台無しにすることです。

さらに、カーネルがファイルを実行するとき、実行可能ファイルへの参照を保持します。これにより、実行中のファイルの変更が再び防止されます。

結局、実行中のプログラムを構成するファイルを削除/移動できるよう見えても、実際にはそれらのファイルの内容はプログラムが終了するまでメモリに保持されます。


1
これは正しくないです。execve()FDを返さず、単にプログラムを実行します。たとえば、実行したtail -f /foo.log場合、それらはFD(/proc/PID/fd/<fd_num>)に関連付けtailられますfoo.logが、実行可能ファイル自体でtailはなく、親でも関連付けられます。これは、単一の実行可能ファイルにも当てはまります。
heemayl

@heemayl言及しなかったexecveので、これがどのように関連するかわかりません。カーネルがファイルの実行を開始した後、ファイルを置き換えようとしても、カーネルがロードするプログラムを変更することはありません。実行中に実行可能ファイルを「更新」したい場合execve、ある時点で呼び出してカーネルにファイルを再読み込みさせることができますが、これがどのように重要なのかわかりません。重要なのは、「実行中の実行可能ファイル」を削除しても、実行可能ファイルが停止するまでデータの削除は実際にはトリガーされないということです。
バクリウ

私はこの部分について話していたプログラムは、単一の実行可能ファイルで構成されている場合は、あなたがプログラムは、ディレクトリの変更とは無関係に細かい実行されます実行を開始すると:同じまたは異なるファイルシステム内の名前を変更すると、影響を与えることはできませんオープンハンドラを、あなたは必ず話しています約execve()FDやFD、この場合には関与していないがある場合もございます。
-heemayl

2
ファイルへの参照を取得するためにファイルハンドルは必要ありません。ページをマップするだけでも十分です。
サイモンリヒター

1
Unixには「ファイルハンドル」がありません。 open()は、ファイル記述子を返します。これは、heemaylがここで話していることexecve()です。はい、実行中のプロセスにはその実行可能ファイルへの参照がありますが、それはファイル記述子ではありません。おそらくmunmap()、実行可能ファイルのすべてのマッピングを編集したとしても、iノードの解放を停止する参照(/ proc / self / exeに反映)が残っています。(これは、それが返されることはありませんことを、ライブラリ関数からこれをしなかった場合にクラッシュすることなく可能である。)ところで、切り詰めるかあなたを与える可能性がある使用中の実行可能ファイルを修正することETXTBUSYが、うまくいくかもしれません。
ピーターコーデス

7

Linuxファイルシステムでは、ファイルを移動すると、ファイルシステムの境界を越えない限り(読み取り:同じディスク/パーティションに留まる)、変更するのは..(親ディレクトリ)のiノードだけが新しい場所のiノードになります。実際のデータはディスク上でまったく移動せず、ファイルシステムがどこにあるかを知るためのポインタだけが移動しました。

これが、移動操作が非常に速い理由であり、実際にプログラム自体を移動していないので、実行中のプログラムを移動しても問題ない可能性があります。


あなたの答えは、バイナリ実行可能ファイルを別のファイルシステムに移動すると、そのバイナリから起動された実行中のプロセスに影響を与えることを暗示しているようです。
jlliagre

6

プログラムを移動しても、それを起動して開始された実行中のプロセスには影響しないため、可能です。

プログラムが起動されると、そのディスク上のビットは上書きされないように保護されますが、名前を変更したり、同じファイルシステム上の別の場所に移動したり、ファイルの名前を変更したり、移動したりするファイルを保護する必要はありません別のファイルシステムにコピーします。これは、ファイルを他の場所にコピーしてから削除するのと同じです。

プロセスがファイル記述子を開いているため、またはプロセスがそれを実行しているため、使用中のファイルを削除しても、ファイルiノードによって参照されたままで、ディレクトリエントリのみが削除されるファイルデータは削除されません。つまり、iノードに到達できるパス。

プログラムを起動しても、すべてが(物理)メモリに一度に読み込まれるわけではないことに注意してください。反対に、プロセスの開始に必要な厳密な最小値のみがロードされます。その後、必要なページは、プロセスの全期間を通じてオンデマンドでロードされます。これはデマンドページングと呼ばれます。RAMが不足している場合、OSはこれらのページを保持しているRAMを自由に解放できるため、プロセスが実行可能iノードからまったく同じページを複数回ロードする可能性が高くなります。

Windowsでそれができなかった理由は、もともとは、基礎となるファイルシステム(FAT)がディレクトリエントリとiノードのスプリットコンセプトをサポートしていなかったためです。この制限はNTFSにはもう存在しませんでしたが、OSの設計は長い間維持されており、新しいバージョンのバイナリをインストールするときに再起動しなければならないという不快な制約につながりました。


1
Windowsの新しいバージョンは、再起動せずに使用中のバイナリを置き換えることができると思います。
トールビョーンラウンアンデルセン

@ThorbjørnRavnAndersenなぜすべてのアップデートがまだ再起動を必要とするのだろうか:(
Braiam

1
@ブライアム彼らはしません。よく見てください。バイナリは更新できますが、カーネルは(私の知る限り)更新できず、新しいバージョンに置き換えるには再起動が必要です。これは、ほとんどのオペレーティングシステムカーネルに有効です。参照-私は実行中のLinuxカーネルにパッチを適用することができますLinux用のkpatchを書かれているよりも賢い人en.wikipedia.org/wiki/Kpatch
するThorbjörnRavnアンデルセン

@ThorbjørnRavnAndersen私は「すべてのWindowsアップデート」を意味しました
Braiam

@ブライアムはい-私もそうしました。詳しく見てください。
トールビョーンラヴンアンデルセン

4

基本的に、Unixとそのilkでは、ファイル名(ファイルへのディレクトリパスを含む)は、ファイルを開くときにファイルを関連付ける/見つけるために使用されます(ファイルを実行することは、ある方法でファイルを開く1つの方法です)。その瞬間の後、ファイルの識別情報(「inode」を介して)が確立され、質問されなくなります。ファイルを削除し、名前を変更し、アクセス許可を変更できます。プロセスまたはファイルパスがそのファイル/ inodeにハンドルを持っている限り、プロセス間のパイプが行うように(実際には、歴史的なUNIXでは、パイプサイズにぴったり合った名前のないiノードでした。 iノード内の「直接ブロック」ディスクストレージ参照、10ブロックなど)。

PDFファイルでPDFビューアーを開いている場合、そのファイルを削除して同じ名前で新しいファイルを開くことができ、古いビューアーが開いている限り、古いファイルにアクセスしても問題ありません(積極的に監視しない限り)ファイルが元の名前で消えたときに通知するためのファイルシステム)。

一時ファイルを必要とするプログラムは、何らかの名前でそのようなファイルを開き、開いたままですぐに削除します(またはディレクトリエントリ)。その後、ファイルは名前でアクセスできなくなりますが、ファイルへのオープンファイル記述子を持つプロセスは引き続きアクセスできます。その後プログラムの予期しない終了がある場合、ファイルは削除され、ストレージは自動的に再生されます。

そのため、ファイルへのパスはファイル自体のプロパティではなく(実際、ハードリンクは複数の異なるパスを提供できます)、ファイルを開くためにのみ必要であり、既に開いているプロセスによる継続的なアクセスには必要ありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.