ページキャッシュ ページが変更されたらダーティで表示され、書き直さなければならないことがわかりますが、次のような場合はどうなりますか?
想像する: /apps/EXEファイルは、ページキャッシュに完全にページングされ(すべてのページがキャッシュ/メモリにある)、プロセスPによって実行される実行可能ファイルです。
その後、連続リリースは/apps/EXEを新しい実行可能ファイルに置き換えます。
仮定1: 私はプロセスP(そして古い実行可能ファイルを参照するファイル記述子を持つ他のすべての人)が問題なく既存のメモリ/ apps / EXEを使い続け、そのパスを実行したいすべての新しいプロセスが新しい実行可能ファイルを取得することを前提としています。 。
仮定2: ファイル内のすべてのページがメモリにマップされていない場合は、ページエラーが発生するまですべてが問題ないと仮定します。置き換えられたファイルのページとセグフォルトが必要な場合がありますか?
質問1: vmtouchのようなものを使用してファイルのすべてのページをロックすると、状況は変わりますか?
質問2: /apps/EXEがリモートNFSにある場合、違いはありますか? (そうではありません)
私の仮定のうち2つを修正または確認し、2つの質問に答えてください。
これは、ある種の3.10.0-957.el7カーネルを備えたCentOS 7.6システムであると仮定します。
更新:もう少し考えてみると、この状況が他のダーティーページの状況と異なるのではないかと思います。
私は新しいバイナリを作成するプロセスが読み取りを実行し、すべてのキャッシュされたページを取得すると推測します。なぜなら、すべてのページがページアウトされたからです。その後、そのページはすべてダーティで表示されます。 mlockedされると、参照カウントが0になった後、コアメモリを占有する無駄なページになります。
現在実行中のプログラムが終了すると、他のプログラムは新しいバイナリを使用すると考えられます。これがすべて正しいと仮定すると、ファイルの一部だけがページングされている場合にのみ意味があると思います。
答え1
その後、連続リリースは/apps/EXEを新しい実行可能ファイルに置き換えます。
これが重要な部分です。
新しいファイルを公開するには、新しいファイル(例:)を作成し/apps/EXE.tmp.20190907080000
、コンテンツを作成し、権限と所有権を設定し、ついに(2) 最後の名前に名前を変更し、古い/apps/EXE
ファイルを変更します。
結果は新しい inode 番号を持つ新しいファイルです。つまり、事実上別のファイルです。
そして古いファイルには独自のinode番号があります。まだそこにいるファイル名がもはやその項目を指していなくても(またはそのinodeを指すファイル名はもうありません)。
したがって、ここで重要なのは、Linuxで「ファイル」について話すとき、通常実際に言うのは「inodes」ということです。なぜなら、ファイルが開いたら、inodeは私たちが保持しているファイルへの参照であるからです。
仮説1:プロセスP(および以前の実行可能ファイルを参照するファイル記述子を持つ他の誰も)は問題なく古いメモリ/ apps / EXEを引き続き使用し、そのパスを実行したいすべての新しいプロセスは新しい実行可能ファイルを取得すると仮定します。
正しい。
仮説2:ファイル内のすべてのページがメモリにマップされていない場合は、ページエラーが発生するまですべてが問題ないと仮定します。ファイルで置き換えられたページが必要で、セグフォルトも発生する可能性がありますか?
間違っています。古い inode がまだ存在するため、古いバイナリを使用しているプロセスのページエラーがディスク上のそのページを探すことができます。
以前のバイナリを実行しているプロセスの/proc/${pid}/exe
シンボリックリンク(またはそれに対応する出力)を見ると、それに伴ういくつかの効果を確認できます。その後、名前はもう存在しませんが、inodeはまだ存在することがわかります。lsof
/app/EXE (deleted)
また、バイナリで使用されるディスク領域は、プロセスが終了した後にのみ解放されることがわかります(対応するinodeが開いている唯一のプロセスであると仮定)。df
プロセス終了前後の出力を確認すると、もはや存在しないと思っていた以前のバイナリと同じくらいサイズが小さくなったことを確認できます。
ただし、これはバイナリファイルにのみ適用されるのではなく、開いているすべてのファイルに適用されます。プロセスでファイルを開いてファイルを削除すると、プロセスがファイルを閉じるか終了するまで、そのファイルはディスクに残ります。ハードリンクがディスクのinode名を指すカウンタを保持するのと同様に、ファイルシステムドライバ(Linuxカーネル)はそのinodeへの参照数のカウンタを保持します。記憶の中、実行中のシステムでそのinodeへのすべての参照が解放された後にのみ、inodeはディスクから解放されます。
質問1:vmtouchなどのツールを使用してファイル内のすべてのページをロックすると、状況が変わりますか?
この質問は、ページをロックしないとセグフォルトが発生するという誤った仮定2に基づいています。しないでしょう。
質問2:/apps/EXEがリモートNFSにある場合、違いはありますか? (そうではありません)
それ意味はほとんどの場合、同じように動作しますが、NFSにはいくつかの「問題」があります。
時々、NFSでまだ開いている削除されたファイルを見ることができます(そのディレクトリに隠しファイルとして表示されます)。
NFSサーバーの再起動時にデバイス番号が「シャッフル」されないように、NFSエクスポートにデバイス番号を割り当てることもできます。
しかし、主なアイデアは同じです。 NFS クライアントドライバはまだ inode を使用し、inode が引き続き参照されている間 (サーバ上で) ファイルを保存しようとします。
答え2
前提2:ファイル内のすべてのページがメモリにマップされていない場合は、ページエラーが発生するまですべてが問題ないとします。ファイルから置き換えられたページが必要で、セグフォルトも発生する可能性がありますか?
いいえ、カーネルは現在実行中のファイル内のどの項目も開いて置き換えることを許可しないため、これは発生しません。そのような操作は失敗しますETXTBSY
[1]:
cp /bin/sleep sleep; ./sleep 3600 & echo none > ./sleep
[9] 5332
bash: ./sleep: Text file busy
dpkgなどがバイナリを更新するときにそれを上書きするのではなく、代わりにそれを使用してrename(2)
ディレクトリエントリをまったく別のファイルにポイントします。質問は表示されません。
[1]保護は、ETXBUSY
「テキスト」(=ライブコード/実行ファイル)と見なすことができる他のファイルには拡張されません。共有ライブラリ、Javaクラスなどは、他のプロセスによってマッピングされている間に変更されます。〜するプロセスがクラッシュします。 Linuxでは、動的リンカーは忠実にMAP_DENYWRITE
フラグをに渡しmmap(2)
ますが、間違えないでください。効果はありません。例:
$ cc -xc - <<<'void lib(){}' -shared -o lib.so
$ cc -Wl,-rpath=. lib.so -include unistd.h -xc - <<<'
extern void lib();
int main(){ truncate("lib.so", 0); lib(); }
'
./a.out
Bus error
答え3
継続的なリリースプロセスが合格すると仮定すると、filbrandenの答えは正しいですrename
。そうではありませんが、ファイルが内部で変更されると、状況は異なります。しかし、あなたの考え方はまだ間違っています。
ディスクの内容を変更したり、ページキャッシュと一致しないようにすることはできません。ページキャッシュは標準バージョンです。そして修正されたもの。ファイルへのすべての書き込みはページキャッシュを介して行われます。すでに存在する場合は、既存のページが変更されます。まだ存在しない場合は、ページの一部を変更しようとすると、ページ全体がキャッシュされ、すでにキャッシュされているかのように変更されます。ページ全体にわたる書き込みはページング読み取りステップを最適化でき、ほぼ確実に最適化できます。いずれにせよ、存在するファイルの修正可能な標準バージョン(*)は1つだけです。つまり、ページキャッシュのバージョンです。
(*)私は少し嘘をついた。 NFSやその他のリモートファイルシステムには複数のものがあり、使用されるファイルとマウントとサーバーサイドのオプションによっては、書き込みのアトミックと順序付けの意味を正しく実装しないことがよくあります。これが、私たちの多くが彼らが根本的に壊れていると考え、同時に書かれたときに使用を拒否する理由です。