【Linuxの仕組み⑤】仮想記憶とコンテナについて

Linux

本記事は、書籍『Linuxの仕組み』を読んで学んだことを、要点を絞ってアウトプットするために作成しました。Linuxカーネルの根幹を成す「仮想記憶」と、現代の開発・運用に不可欠な「コンテナ」「仮想環境」の仕組みについて、5分程度で読めるように解説します。

この記事で学べること

  • CPUレジスタからページキャッシュまで、記憶階層の仕組み
  • メモリ不足を補うスワップとスラッシングの問題点
  • コンテナ(Docker)と仮想環境(Hyper-V)の仕組みと違い
  • コンテナを支える技術:Namespaceとcgroup

記憶階層:CPUとメモリ、ディスクの速度差を埋める

CPU、メモリ(RAM)、ディスク(HDD/SSD)には、それぞれアクセス速度と容量に大きな差があります。この差を埋めるために、Linuxカーネルは「記憶階層」という仕組みを駆使しています。

メモリ階層とキャッシュメモリ

CPUに近いほど高速で容量が小さく、遠いほど低速で容量が大きくなります。CPUレジスタが最速ですが、その容量は極小です。CPUとメインメモリの間には「キャッシュメモリ(L1, L2, L3キャッシュ)」があり、頻繁にアクセスされるデータを一時的に保持することで、メインメモリへのアクセス回数を減らします。

キャッシュメモリの書き込み方式には、主に以下の2つがあります。

  • ライトスルー方式: データを書き込む際、キャッシュメモリとメインメモリの両方を同時に更新します。データの一貫性は保ちやすいですが、書き込み速度は遅くなります。
  • ライトバック方式: データを書き込む際、まずはキャッシュメモリのみを更新します。後でまとめてメインメモリに書き戻します。書き込み速度は速くなりますが、予期せぬシャットダウン時にデータが失われるリスクがあります。

ページキャッシュとバッファキャッシュ

ディスクへのアクセスは、メモリへのアクセスと比べて非常に低速です。この速度差を緩和するために、Linuxカーネルはメモリの一部をディスクデータのキャッシュとして利用します。

  • ページキャッシュ: ファイルの内容をメモリ上にキャッシュします。ファイルを読み書きする際、まずはページキャッシュを探し、あればディスクアクセスを回避します。ページキャッシュ上で修正されたデータは「ダーティページ」と呼ばれ、後でカーネルによってディスクに書き戻されます。
  • バッファキャッシュ: ファイルのメタデータ(ファイル名、権限、場所など)や、ファイルシステムの情報などをキャッシュします。

ページキャッシュとバッファキャッシュは、ディスクI/Oのパフォーマンスを向上させるために不可欠な仕組みです。電源を急に落とした場合、ダーティページ(修正されたがディスクに書き込まれていないデータ)が失われる可能性があります。

また、意図的にキャッシュを利用せずに直接ディスクに書き込む「Direct I/O」という仕組みも存在します。

スワップ:メモリ不足を補うが、使いすぎに注意

メモリが不足した場合、カーネルは利用頻度の低いメモリページをディスクに書き出して、空きメモリを確保します。この仕組みを「スワップ」と呼びます。ディスクの一部をメモリのように見せかけることができますが、ディスクアクセスは低速なため、多用するとパフォーマンスが劇的に低下します。

ページインとページアウト

スワップ処理で行われるメモリページとディスクのやり取りは、以下のようになります。

  • ページアウト: メモリ上のページをスワップ領域(ディスク上の特定の場所)に書き出すこと。
  • ページイン: スワップ領域にあるページを、再度メモリ上に読み込むこと。

ページフォールト

プログラムが特定のメモリアドレスにアクセスしようとした際、そのページがメモリ上に存在しない場合に「ページフォールト」という例外が発生します。カーネルはページフォールトを検知すると、必要なページをディスク(またはスワップ領域)からメモリに読み込み、プログラムの実行を継続します。

ページフォールトには、主に2つの種類があります。

  • マイナーフォールト: アクセスしたページがメモリ上に存在する(ページキャッシュなどにある)が、ページの割り当てが行われていなかった場合。ディスクI/Oは発生しません。
  • メジャーフォールト: アクセスしたページがメモリ上に存在せず、ディスク(またはスワップ領域)から読み込む必要がある場合。ディスクI/Oが発生するため、パフォーマンスへの影響が大きいです。

スラッシングの問題

メモリ不足が深刻になり、ページインとページアウトが頻繁に繰り返される状態を「スラッシング」と呼びます。CPUがページフォールトの処理に追われ、本来のプログラムの処理が進まくなるため、システムの処理速度が著しく低下します。

スワップはメモリ不足の緊急避難的な措置であり、常時発生するような状態はメモリ不足であることを意味します。

スワップ情報の参照

以下のコマンドでスワップに関する情報を確認できます。

sar -B: ページイン/ページアウトの状況を時系列で確認できます。
swapon --show: 利用しているスワップ領域の情報を確認できます。

コンテナ:ホストカーネルを共有する軽量な仮想化

コンテナ(Docker、Kubernetesなど)は、ホストOSのカーネルを共有しつつ、アプリケーションごとの実行環境(プロセス、ファイルシステム、ネットワークなど)を隔離する技術です。仮想環境(Hyper-Vなど)に比べて軽量で、起動が高速という特徴があります。

アプリケーションコンテナとシステムコンテナ

コンテナには、大きく分けて2つの種類があります。

  • アプリケーションコンテナ: 1つのアプリケーションを動かすための最小限の環境。Dockerが代表例です。
  • システムコンテナ: 複数のアプリケーションやサービスを動かす、OSに近い環境。LXDなどが代表例です。

コンテナを支える技術

コンテナの隔離とリソース制限を実現するために、Linuxカーネルは以下の機能を提供しています。

1. Namespace

Namespaceは、システムリソース(プロセスID、ネットワーク、マウントポイントなど)を、プロセスグループごとに隔離する機能です。これにより、異なるコンテナ間で同じプロセスIDやポート番号を使用しても、互いに干渉しなくなります。

2. cgroup (control groups)

cgroupは、プロセスグループに対して、CPU時間、メモリ量、ディスクI/O、ネットワーク帯域などのリソースの使用量を制限・管理する機能です。これにより、特定のコンテナがホストシステムのリソースを独占するのを防ぐことができます。また、OOM killer(Out of Memory killer)が発生する前に、cgroupの制限によって特定のプロセスのメモリ使用量を制御することも可能です。

コンテナ起動時の引数で、CPUやメモリの制限を指定することで、内部的にcgroupが設定されます。

※cgroupには、現在主流になりつつある「cgroup v2」という新しいバージョンがあります。v1では各リソースごとに階層構造を管理していましたが、v2では単一の階層構造でリソースを管理できるようになり、設定が簡素化されています。

コンテナのセキュリティリスク

コンテナはホストOSのカーネルを共有するため、カーネルの脆弱性が全コンテナに影響する可能性があります。仮想環境のようにカーネルがエミュレーションされていないため、コンテナからホストOSのカーネルに直接アクセスできてしまい、悪意のあるコンテナがホストシステム全体に被害を及ぼすリスクがあります。

そのため、コンテナのセキュリティ対策(脆弱性スキャン、特権の制限、ネットワーク分離など)が重要になります。セキュリティを強化した別のコンテナシステムも存在します(gVisor, Kata Containersなど)。

仮想環境:カーネルをエミュレーションする重厚な仮想化

仮想環境(Hyper-V、KVM、VMwareなど)は、ハードウェアをエミュレーションし、その上で別のOS(ゲストOS)を動かす技術です。ゲストOSごとに独立したカーネルが動作するため、コンテナよりも高い隔離性とセキュリティを提供します。

仮想環境の仕組み

仮想環境は、「ハイパーバイザ」というソフトウェアがハードウェアを管理し、仮想的なハードウェアリソースを仮想マシン(VM)に割り当てます。ハイパーバイザには、ホストOS上で動作する「ホストOS型」と、ハードウェア上で直接動作する「ハイパーバイザ型」があります。Hyper-Vはハイパーバイザ型に分類されます。

ハイパーバイザは、物理ハードウェアとゲストOSの間でハードウェアアクセス権限のやり取りを仲介します。

メモリ効率とパフォーマンス

仮想マシンごとにOSが起動するため、ホストOSとゲストOSの両方でカーネルが動作し、多くのメモリを消費します。また、ハードウェアのエミュレーション(特にI/O処理)にはオーバーヘッドが伴うため、ホストOSに直接アクセスするよりもパフォーマンスは低下します。

ストレージデバイスへの同時書き込みはできないため、複数の仮想マシンがストレージに負荷をかけると、処理速度が低下します。

準仮想化によるパフォーマンス向上

エミュレーションによるパフォーマンス低下を軽減するために、「準仮想化(Paravirtualization)」という技術があります。ゲストOSにハイパーバイザが存在することを認識させ、特別なインターフェース(API)を使ってハードウェアアクセスを要求することで、エミュレーションを一部回避し、I/Oパフォーマンスを向上させることができます。

準仮想化は、ホストOSほどではないにせよ、完全仮想化(エミュレーション)よりも高速な処理が可能です。

まとめ

最後まで読んでいただきありがとうございました。

Linuxの仕組みはこれでひと通り自分が役に立って皆さんも知識としてあった方がいいと思った個所が切り上げ終えましたので、言語関連の記事を上げたいと思います!

最後になりますが、Linuxの仕組みが気になった方は購入を検討してみてください!とても面白い内容でした!

コメント

タイトルとURLをコピーしました