Linuxのしくみ
第1章 コンピュータシステムの概要
OSとハードウェアの関係、およびOSとはなにか
Linuxとハードウェアの関係
- 外部デバイスを操作する処理はデバイスドライバのプログラムにまとめられている
- プロセスはデバイスドライバを介してデバイスにアクセスする。ユーザモードで直接アクセスすることはできない
プロセスはユーザモードで、OSの処理をまとめた機能はカーネルモードでそれぞれ動作する。プロセスがカーネルの機能を使いたければシステムコールを使って依頼する
第2章 ユーザモードで実現する機能
ユーザモードで動作するOS機能、システムコールについて
システムコール
システムコールの種類
- プロセス生成、削除
- メモリ確保、解放
- プロセス間通信
- ネットワーク
- ファイルシステム操作
- ファイル操作(デバイスアクセス)
システムコールを発行すると、その間CPUはユーザモードからカーネルモードに切り替わり、終了すればユーザモードに戻る。
strace
コマンドでシステムコールの発行を確認できる。
CでHello Worldをプリントする単純なプログラムでも、システムコールが30回くらい発行されるらしい。ほとんどがmain関数の前後の初期化終了処理で、プリントの部分はwrite()
システムコールだけ
OSが提供するライブラリ
システムコールは、通常の関数呼び出しと違って、Cなどの高級関数から直接呼び出せない。アーキテクチャ依存のアセンブリコードを使って呼び出す必要がある。
標準Cライブラリであるglibcは、システムコールのラッパー関数が入っている
ldd
コマンドでリンクを確かめることができる
OSが提供するプログラム
init
, sysctl
, grep
, sar
, コンパイラ gcc
, シェル bash
などのコマンド
第3章 プロセス管理
カーネル内のプロセス生成および削除を担当するプロセス管理について
プロセス生成の目的
- 同じプログラムの処理を複数のプロセスに分けて処理する:
fork()
。Webサーバによる複数リクエストの受付など - まったく別のプログラムを生成する:
execve()
。bashから各種プログラムの新規生成など
fork()関数
プロセス生成の流れ
- 子プロセス用メモリ領域を作成して、親プロセスのメモリをコピーする
- 親プロセスと子プロセスは違うコードを実行するように分岐する
execve()関数
プロセス生成の流れ
- 実行ファイルを読み出して、プロセスのメモリマップに必要な情報を読み出す
- 現在のプロセスのメモリを新しいプロセスのデータで上書きする
- 新しいプロセスの最初の命令から実行開始する
プロセス数が増えるのではなく、あるプロセスを別のプロセスで置き換える。
親プロセスからfork()を発行して、復帰後に子プロセスがexec()を呼ぶ、「fork and exec」の流れになることが多い
Pythonではos.exec()
で呼び出せる。
終了処理
プログラム終了には_exit()
関数を使用する。内部的にはexit_group()
システムコール
第4章 プロセススケジューラ
CPUリソースを管理するプロセススケジューラについて
プロセスの状態と、ps ax
コマンドのSTATフィールド
- 実行状態: R
- 実行待ち状態: R
- スリープ状態: S or D
- ゾンビ状態: Z
ほとんどのプロセスはスリープ状態になっている
- スループット: 単位時間あたりの総仕事量
- レイテンシ: 処理の開始から終了までの時間経過
マルチコアCPU環境では、複数プロセスを同時に動かさないとスループットは上がらない。プロセス数を論理CPU数より多くしてもスループットは上がらない
第5章 メモリ管理
メモリリソースを管理するメモリ管理システムについて
freeのbuff/cacheフィールドは、バッファキャッシュおよびページキャッシュを利用するメモリ。システムの空きメモリが減少してきたらカーネルによって解放される
カーネルパラメータvm.panic_on_oom
のデフォルト0
はOOM発生時にOOM Killerを発動する。1
にするとOOM発生時にシステムを強制終了させる
カーネルがプロセスにメモリを割り当てるタイミング
- プロセス生成時
- プロセス生成後、追加で動的にメモリを割り当てるとき
この単純なメモリ割り当ての問題点
- メモリの断片化
- 別用途のメモリにアクセスできてしまう
- マルチプロセスの扱いが困難
仮想記憶
システムに搭載されているメモリにプロセスから直接アクセスさせるのではなく、仮想アドレスを用いて間接的にアクセスさせる方法
- 直接アクセスさせると、メモリの断片化や別用途のメモリにアクセスできてしまう
- プロセスから見えるメモリのアドレスを仮想アドレス、実際のアドレスを物理アドレスと呼ぶ
メモリはページと呼ばれる単位で区切って管理されている。
ページテーブルに仮想アドレスと物理アドレスの対応が書かれており、ページ単位で変換される。
ページフォールトは、アクセスしようとした仮想メモリ領域が物理メモリ上にないときに発生する処理
glibcのmalloc()
は、mmap()
システムコールを呼んでメモリ領域を獲得している
仮想記憶の応用
ファイルマップ
- ファイルの領域を仮想アドレス空間上にメモリマップする
- メモリアクセスと同じ方法でファイルにアクセスできる
デマンドページング
- アクセス要求があったときに物理ページを論理メモリに割り当てる方式
- 実際の物理メモリ割り当てはこの方法で行う
コピーオンライト
- 仮想記憶を使った、プロセス生成fork()システムコールの高速化の仕組み
- CoW(Copy on Write)とも呼ばれる
- 物理メモリを
fork()
システムコール発行時ではなく、遅れて書き込み時にコピーされる
スワップ
- ストレージデバイスの一部を一時的にメモリの代わりとして使用するしくみ
- スワップアウトは物理メモリの一部をスワップ領域に退避、スワップインは物理メモリに戻す、これらをまとめてスワッピング
- メモリ不足のときに起こる
階層型ページテーブル
- ページテーブルが多段になっているページテーブル。仮想アドレスに対する下位のページテーブル情報が書かれており、そのテーブルの中にページテーブルが書かれている
ヒュージページ
- 大きいサイズのページテーブル。ページテーブルに必要なメモリ量を減らすことができる
第6章 記憶階層
記憶装置を構成する記憶階層について
キャッシュメモリ
計算を行うレジスタ(CPU)とメモリの間のアクセスを高速化するためのメモリ。
通常はCPUに内蔵されているが、CPU外についているキャッシュメモリもある。
CPUからキャッシュメモリに書き込まれて、メモリに書き込まれていない場合、ダーティであると呼ぶ。
キャッシュメモリがいっぱい、かつ、すべてダーティである場合、キャッシュライン内のデータが激しく入れ替わるスラッシングが発生し、性能が劣化する。
ページキャッシュ
ストレージへのアクセスを高速化するために、ストレージのファイルデータをメモリにキャッシュする
バッファキャッシュは、ファイルシステムを使わずにデバイスファイルを使ってストレージデバイスに直接アクセスするときに使う領域。
ハイパースレッド
レジスタのなどの一部の資源を複数用意して、それぞれのシステムから論理CPUとして認識されるように分割する仕組み
データ転送待ち時間などのCPU資源を有効活用できる
第7章 ファイルシステム
通常のデバイスドライバによるアクセスを簡略化するファイルシステムについて
ファイルシステムは、どこにどんなデータがあるか、どこが空き容量かを管理するしくみ
ファイルシステムがないと、メモリからストレージへの書き込みアドレスやサイズなどを自分で指定しなければならない。
どのファイルシステムでも、ユーザからはファイルシステムはシステムコールの発行という統一したインターフェースでアクセスできる。
データの種類には、データとメタデータがある。
クオータは、用途ごとに使用できるファイルシステムの容量を制限する機能。
ファイルシステムのデータをストレージに読み書きしている最中にシステムの電源が落ちるようなときに不整合が生じることがある。
-> 不整合を防ぐ技術として、「ジャーナリング」や 「コピーオンライト」がある。
ジャーナリング
ファイルシステム内にジャーナル領域(ユーザが認識できないメタデータ)を用意して、まずジャーナル領域に処理を書いてからそれに従ってファイルシステムを更新する
ファイルシステム更新手順
- ジャーナルログ(更新に必要なアトミック処理の一覧)を、ジャーナル領域に書く
- ジャーナル領域の内容に基づいて、実際にファイルシステムの内容を更新する
ジャーナルログの更新中に電源断が発生した場合は、ジャーナル領域のデータをただ捨てるだけ。
実データの更新中に電源断が発生した場合は、ジャーナルログを最初から再生する。
ext4, XFSのファイルシステムが対象
コピーオンライト
更新されるデータを別の場所に全て書き込んでからリンクを張り替える。
リンクを張り替える前に電源断が発生しても、再起動後に作りかけのデータを削除すれば不整合は発生しない。
Btrfsのファイルシステムが対象
ファイルシステム不整合への対策
定期的にファイルシステムのバックアップをとっておき、不整合が発生した場合に復元するnogあ一般的な対策。
バックアップを取っていない場合は、fsck
コマンドなどの復旧ようコマンドで復元できることがあるが、時間がかかることがあるし失敗することもある。
デバイスファイル
ファイルには通常のファイル、ディレクトリ、デバイスファイルがある。
デバイスにファイルとしてアクセスする。/dev
以下に存在
キャラクタデバイス
- 読み書きはできるがシークはできない
- 例: 端末、キーボード、マウスなどの入出力デバイス
- permissionの表記が
crw-rw-rw-
のように先頭にc
がつく
ブロックデバイス
- 読み書き以外にランダムアクセスができる
- 例: HDD, SSDなどのストレージデバイス
- permissionの表記が
brw-rw----
のように先頭にb
がつく
さまざまなファイルシステム
tmpfs
- メモリベースのファイルシステム
ネットワークファイルシステム(nfs)
- リモートホスト上のファイルにアクセスするファイルシステム
仮想ファイルシステム
- procfs
- システムに存在するプロセスについての情報を得るためのファイルシステム
- sysfs
- カーネル情報にアクセスするためのファイルシステム
- cgroupfs
- リソース仕様の制限を書けるcgroupを操作するファイルシステム
Btrfs
- 複数のストレージデバイス/パーティションから大きなストレージプールを作り、その上にマウント可能なサブボリューム領域を作成する
- ファイルシステム+LVMのようなボリュームマネージャ
第8章 ストレージデバイス
ストレージデバイスの性能特性、その性能を引き出すためのカーネルの支援機能について
HDD
データを磁気情報で表現して磁気ディスクに記憶するストレージデバイス
データはバイト単位ではなくセクタと呼ばれる単位で読み書きする
性能を上げるための工夫
- ファイル内のデータを連続する
- 連続する領域へのアクセスは1回にまとめる
- ファイルにはなるべく大きなサイズでシーケンシャルアクセスする
ブロックデバイス層の上に構築されたファイルシステムを介して間接的にHDDにアクセスする
ブロックデバイス層のIOスケジューラは、ブロックデバイスへのアクセス要求を溜めておいて、マージソートをしてからデバイスドライバにIOリクエストをする
SSD
データへのアクセスに機械的な動作が一切なく、電気的な動作だけになる