View on GitHub

Today I Learned

Software Engineering Blog

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章 プロセス管理

カーネル内のプロセス生成および削除を担当するプロセス管理について

プロセス生成の目的

  1. 同じプログラムの処理を複数のプロセスに分けて処理する: fork()。Webサーバによる複数リクエストの受付など
  2. まったく別のプログラムを生成する: execve()。bashから各種プログラムの新規生成など

fork()関数

プロセス生成の流れ

  1. 子プロセス用メモリ領域を作成して、親プロセスのメモリをコピーする
  2. 親プロセスと子プロセスは違うコードを実行するように分岐する

execve()関数

プロセス生成の流れ

  1. 実行ファイルを読み出して、プロセスのメモリマップに必要な情報を読み出す
  2. 現在のプロセスのメモリを新しいプロセスのデータで上書きする
  3. 新しいプロセスの最初の命令から実行開始する

プロセス数が増えるのではなく、あるプロセスを別のプロセスで置き換える。

親プロセスからfork()を発行して、復帰後に子プロセスがexec()を呼ぶ、「fork and exec」の流れになることが多い

Pythonではos.exec()で呼び出せる。

終了処理

プログラム終了には_exit()関数を使用する。内部的にはexit_group()システムコール

第4章 プロセススケジューラ

CPUリソースを管理するプロセススケジューラについて

プロセスの状態と、ps axコマンドのSTATフィールド

ほとんどのプロセスはスリープ状態になっている

マルチコアCPU環境では、複数プロセスを同時に動かさないとスループットは上がらない。プロセス数を論理CPU数より多くしてもスループットは上がらない

第5章 メモリ管理

メモリリソースを管理するメモリ管理システムについて

freeのbuff/cacheフィールドは、バッファキャッシュおよびページキャッシュを利用するメモリ。システムの空きメモリが減少してきたらカーネルによって解放される

カーネルパラメータvm.panic_on_oom のデフォルト0はOOM発生時にOOM Killerを発動する。1にするとOOM発生時にシステムを強制終了させる

カーネルがプロセスにメモリを割り当てるタイミング

この単純なメモリ割り当ての問題点

仮想記憶

システムに搭載されているメモリにプロセスから直接アクセスさせるのではなく、仮想アドレスを用いて間接的にアクセスさせる方法

メモリはページと呼ばれる単位で区切って管理されている。

ページテーブルに仮想アドレスと物理アドレスの対応が書かれており、ページ単位で変換される。

ページフォールトは、アクセスしようとした仮想メモリ領域が物理メモリ上にないときに発生する処理

glibcのmalloc()は、mmap()システムコールを呼んでメモリ領域を獲得している

仮想記憶の応用

ファイルマップ

デマンドページング

コピーオンライト

スワップ

階層型ページテーブル

ヒュージページ

第6章 記憶階層

記憶装置を構成する記憶階層について

キャッシュメモリ

計算を行うレジスタ(CPU)とメモリの間のアクセスを高速化するためのメモリ。

通常はCPUに内蔵されているが、CPU外についているキャッシュメモリもある。

CPUからキャッシュメモリに書き込まれて、メモリに書き込まれていない場合、ダーティであると呼ぶ。

キャッシュメモリがいっぱい、かつ、すべてダーティである場合、キャッシュライン内のデータが激しく入れ替わるスラッシングが発生し、性能が劣化する。

ページキャッシュ

ストレージへのアクセスを高速化するために、ストレージのファイルデータをメモリにキャッシュする

バッファキャッシュは、ファイルシステムを使わずにデバイスファイルを使ってストレージデバイスに直接アクセスするときに使う領域。

ハイパースレッド

レジスタのなどの一部の資源を複数用意して、それぞれのシステムから論理CPUとして認識されるように分割する仕組み

データ転送待ち時間などのCPU資源を有効活用できる

第7章 ファイルシステム

通常のデバイスドライバによるアクセスを簡略化するファイルシステムについて

ファイルシステムは、どこにどんなデータがあるか、どこが空き容量かを管理するしくみ

ファイルシステムがないと、メモリからストレージへの書き込みアドレスやサイズなどを自分で指定しなければならない。

どのファイルシステムでも、ユーザからはファイルシステムはシステムコールの発行という統一したインターフェースでアクセスできる。

データの種類には、データとメタデータがある。

クオータは、用途ごとに使用できるファイルシステムの容量を制限する機能。

ファイルシステムのデータをストレージに読み書きしている最中にシステムの電源が落ちるようなときに不整合が生じることがある。

-> 不整合を防ぐ技術として、「ジャーナリング」や 「コピーオンライト」がある。

ジャーナリング

ファイルシステム内にジャーナル領域(ユーザが認識できないメタデータ)を用意して、まずジャーナル領域に処理を書いてからそれに従ってファイルシステムを更新する

ファイルシステム更新手順

  1. ジャーナルログ(更新に必要なアトミック処理の一覧)を、ジャーナル領域に書く
  2. ジャーナル領域の内容に基づいて、実際にファイルシステムの内容を更新する

ジャーナルログの更新中に電源断が発生した場合は、ジャーナル領域のデータをただ捨てるだけ。

実データの更新中に電源断が発生した場合は、ジャーナルログを最初から再生する。

ext4, XFSのファイルシステムが対象

コピーオンライト

更新されるデータを別の場所に全て書き込んでからリンクを張り替える。

リンクを張り替える前に電源断が発生しても、再起動後に作りかけのデータを削除すれば不整合は発生しない。

Btrfsのファイルシステムが対象

ファイルシステム不整合への対策

定期的にファイルシステムのバックアップをとっておき、不整合が発生した場合に復元するnogあ一般的な対策。

バックアップを取っていない場合は、fsckコマンドなどの復旧ようコマンドで復元できることがあるが、時間がかかることがあるし失敗することもある。

デバイスファイル

ファイルには通常のファイル、ディレクトリ、デバイスファイルがある。

デバイスにファイルとしてアクセスする。/dev以下に存在

キャラクタデバイス

ブロックデバイス

さまざまなファイルシステム

tmpfs

ネットワークファイルシステム(nfs)

仮想ファイルシステム

Btrfs

第8章 ストレージデバイス

ストレージデバイスの性能特性、その性能を引き出すためのカーネルの支援機能について

HDD

データを磁気情報で表現して磁気ディスクに記憶するストレージデバイス

データはバイト単位ではなくセクタと呼ばれる単位で読み書きする

性能を上げるための工夫

ブロックデバイス層の上に構築されたファイルシステムを介して間接的にHDDにアクセスする

ブロックデバイス層のIOスケジューラは、ブロックデバイスへのアクセス要求を溜めておいて、マージソートをしてからデバイスドライバにIOリクエストをする

SSD

データへのアクセスに機械的な動作が一切なく、電気的な動作だけになる