プロセスによって開かれたファイルは RAM にロードされますか?

プロセスによって開かれたファイルは RAM にロードされますか?

コマンド例えば、sedはプログラムであり、プログラムはファイル内にコード化されたロジックであり、これらのファイルはハードディスク上のどこかにあります。しかし、コマンドが実行されると、ハードディスクに入れられるラム、彼らは生き返り、何かをすることができ、プロセス

プロセスは他のファイルを使用したり、それらのファイルを読み込んだり、書き込んだりすることができます。その場合、それらのファイルはオープン ファイルと呼ばれます。実行中のすべてのプロセスによってオープンされているすべてのファイルを一覧表示するコマンドがあります: lsof

さて、私が疑問に思っているのは、コマンドの二重の寿命、つまり 1 つはハードディスク上、もう 1 つは RAM 上という寿命が、ロジックがプログラムされておらず、単にデータのコンテナであるような他の種類のファイルにも当てはまるかどうかです。

私の推測では、プロセスによって開かれたファイルも RAM にロードされます。それが本当かどうかはわかりませんが、単なる直感です。

誰か、それを理解してくれませんか?

答え1

いいえ、ファイルを開くと自動的にメモリに読み込まれるわけではありません。それは非常に非効率的です。sedたとえば、は他の多くの Unix ツールと同様に、入力を 1 行ずつ読み取ります。メモリに現在の行以上を保持する必要がある場合はほとんどありません。

awk同じです。記録一度に 1 行ずつ、デフォルトでは 1 行ずつです。入力データの一部を変数に格納すると、当然ながら1 行分が余分にかかります。

次のようなことをする習慣のある人もいます

for line in $(cat file); do ...; done

シェルはループ$(cat file)の最初の繰り返しを実行する前にコマンド置換を完全に展開する必要があるためfor意思全体をfileメモリに読み込む(ループを実行するシェルが使用するメモリに読み込むfor)。これは少々馬鹿げているし、エレガントでもない。代わりに、次のようにすべきである。

while IFS= read -r line; do ...; done <file

fileこれは行ごとに処理されます(ただし、「IFS= read -r line」を理解する)。

シェルでファイルを1行ずつ処理する必要はほとんどありません。ほとんどのユーティリティは行指向だからです(シェル ループを使用してテキストを処理するのはなぜ悪い習慣だと考えられるのでしょうか?)。

私はバイオインフォマティクスの分野で働いていますが、膨大なゲノムデータを処理する際には、絶対に必要なデータだけをメモリに保持しなければ、ほとんど何もできません。たとえば、VCF ファイル内の DNA 変異体を含む 1 テラバイトのデータセットから、個人を特定できるデータの一部を取り除く必要がある場合 (この種のデータは公開できないため)、簡単なプログラムで行ごとに処理しますawk(VCF 形式は行指向であるため、これが可能です)。しないでくださいファイルをメモリに読み込み、そこで処理して、再度書き戻します。ファイルが圧縮されている場合は、zcatまたはに渡しますgzip -d -cが、gzipデータのストリーム処理が行われるため、ファイル全体をメモリに読み込むこともできません。

ファイル形式がないJSON や XML のような行指向の場合、すべてのファイルを RAM に保存せずに巨大なファイルを処理できるストリーム パーサーがあります。

実行ファイルの場合、共有ライブラリがオンデマンドでロードされたり、プロセス間で共有されたりする可能性があるため、少し複雑になります(共有ライブラリの読み込みと RAM 使用量、 例えば)。

キャッシュについてはここでは触れていません。これは、頻繁にアクセスされるデータを保持するために RAM を使用するアクションです。小さなファイル (実行可能ファイルなど) は、ユーザーが何度も参照することを期待して、OS によってキャッシュされることがあります。ファイルの最初の読み取りを除き、その後のアクセスはディスクではなく RAM に対して行われます。入力と出力のバッファリングと同様に、キャッシュは通常、ユーザーに対してほとんど透過的であり、キャッシュに使用されるメモリの量は、アプリケーションなどによって割り当てられた RAM の量に応じて動的に変化することがあります。


1 技術的には、ほとんどのプログラムは、明示的なバッファリングを使用するか、標準 I/O ライブラリが行うバッファリングを通じて暗黙的に、入力データのチャンクを一度に読み取り、そのチャンクを 1 行ずつユーザーのコードに提示します。たとえば、一度に 1 文字ずつ読み取るよりも、ディスクのブロック サイズの倍数を読み取る方がはるかに効率的です。ただし、このチャンク サイズが数キロバイトを超えることはほとんどありません。

答え2

しかし、コマンドが実行されると、ハードディスクからそのファイルのコピーがRAMに格納されます。

これは(一般的には)間違いです。プログラムが実行されると(実行(2)...) プロセス(そのプログラムを実行している)は仮想アドレス空間カーネルは再構成していますMMUその目的のために。仮想メモリアプリケーションプログラムは、仮想アドレス空間を次のように変更できることに注意してください。mmap(2)& munmap&mプロテクト(2)、また、ダイナミックリンカー(見るld-linux(8))。参照マッドアドバイス(2)posix_fadvise(2)エムロック(2)

未来ページフォールトカーネルによって処理され、実行ファイルからページを(遅延的に)ロードします。殴打

カーネルは大きなページキャッシュ. こちらもご覧くださいコピーオンライト参照先読み(2)

さて、私が疑問に思っているのは、コマンドの二重の寿命、つまり 1 つはハードディスク上、もう 1 つは RAM 上という寿命が、ロジックがプログラムされておらず、単にデータのコンテナであるような他の種類のファイルにも当てはまるかどうかです。

のためにシステムコールのように読む(2)書く(2)ページ キャッシュも使用されます。読み取るデータがページ キャッシュ内にある場合、ディスク IO は実行されません。ディスク IO が必要な場合、読み取ったデータはページ キャッシュに格納される可能性が高くなります。したがって、実際には、同じコマンドを 2 回実行すると、2 回目にはディスクへの物理 I/O が実行されない可能性があります (SSD ではなく古い回転ハード ディスクを使用している場合は、その音が聞こえることがあります。または、ハード ディスクの LED を注意深く観察してください)。

次のような本を読むことをお勧めしますオペレーティング システム: 3 つの簡単な部分(無料でダウンロード可能、1章につき1つのPDFファイル)でこれらすべてが説明されています。

参照Linux が RAM を食い尽くすxosview、、topまたはhtopなどcat /proc/self/mapsのコマンドを実行しますcat /proc/$$/mapsプロセス(5))。

PS. 私は Linux に焦点を当てていますが、他の OS にも仮想メモリとページ キャッシュがあります。

答え3

いいえ。今日ではギガバイト単位の RAM があるのは素晴らしいことですが、RAM が非常に限られたリソースだった時代もありました (私は 2MB の RAM を搭載した VAX 11/750 でプログラミングを学びました)。RAM にあるのはアクティブな実行可能ファイルとアクティブなプロセスのデータ ページ、およびバッファ キャッシュにあるファイル データだけでした。
バッファ キャッシュはフラッシュされ、データ ページはスワップ アウトされました。そして、頻繁にスワップ アウトされました。読み取り専用の実行可能ページは上書きされ、ページ テーブルがマークされたため、プログラムがそれらのページに再度アクセスすると、ファイル システムからページインされました。データはスワップからページインされました。前述のように、STDIO ライブラリはデータをブロック単位で取り込み、必要に応じてプログラムによって取得されました (fgetc、fgets、fread など)。mmap を使用すると、共有ライブラリ オブジェクトや通常のファイルで行われるのと同様に、ファイルをプロセスのアドレス空間にマップできます。はい、RAM にあるかどうかをある程度制御できます (mlock) が、それは限界があります (mlock のエラー コード セクションを参照)。

関連情報