Linuxのしくみ
===
## 1.Linuxの概要
Linuxにはカーネルと呼ばれるコア部分がある。カーネルはCPU、メモリやデバイスなどのハードとのやりとりを管理してくれており、ユーザーはカーネルを経由しないと、上記などへの直接的なアクセスを行うことは出来ない。
カーネルはユーザーモードとカーネルモードを備えており、ユーザーからシステムコールを受け付けるとカーネルモードに推移して、処理を実行する。
### コマンド
strace: システムコールを出力するコマンド
taskset: バックグラウンドで指定したプログラムを動かす
sar -P 0 1: cpu0コアを1秒に1回ずつデータを取得する
### 静的ライブラリと共有ライブラリ
リンクを貼って、ライブラリを全プロジェクトで共通のコードを使うか、プロジェクト単位で個別のライブラリファイルを使うかどうか。
近年では、go言語が静的ライブラリを使用しており、ファイルサイズが増えてしまうが、バージョンによる依存性の解消をすることが可能
## 2.プロセス管理(基礎編)
ps aux: プロセス一覧の出力
pstree -p: プロセスの親子関係を出力
### プロセスの生成
方法は2つある。
- fork関数: 現在、稼働中のプロセスをコピーして別のメモリ領域にコピーして展開する
- evecve関数: 現在、稼働中のプロセスのメモリを置き換える
forkの方はコピーを行うだけなので、execuveと比べて非常にエコ。
### プロセスの親子関係
全てのプロセスは(例外あり)はpid=1のinitから生み出されている
### プロセスの終了
プロセスは実行を終えるとゾンビ状態になる。親は子プロセスが終了したことを検知してプロセスを終了させる。
ゾンビプロセスが放置されると定期的に強制終了させられる(シグナルを送る)
### セッションとプロセスグループ
sshなどの端末を通して実行される処理はセッションという単位で管理されている。またセッションには関連するプロセスをグループ化したプロセスグループという単位が存在している。
// session ------------
// bash -> vi -> go...
// --------------------
## 3.プロセススケジューラー
CPUのプロセスへの割り当てはタイムスライスという単位で順番に割り当てられている。
例えば、CPUが1つしかない場合は、順番にCPUを使えるプロセスを切り替えて(コンテキストスイッチ)、処理を実行している。
## 4.メモリ管理システム
free: メモリの使用状況を確認する
sar -r 1 1: メモリの使用状況を継続的に確認する
カーネルは終了したプロセス、ページキャッシュ領域など不要になったメモリを回収している。メモリが枯渇した状態(OutOfMemory)になると、カーネルは強制的に適当なプロセスを終了させて、メモリを回収する。
### 仮想メモリ
物理的にメモリに直接アクセスすることはできず、仮想メモリという概念が存在する。
仮想メモリはプロセス単位で生成されて、カーネルのメモリ領域に記録される。
仮想メモリは物理メモリへの対応を記録したページテーブル(ハッシュのようなもの)によって管理される。
仮想メモリが存在することで3つの問題を回避出来るとのこと。
- メモリの断片化
- プロセス単位でのメモリアクセス領域の管理
- アクセスさせたくないメモリ領域の保護
存在しない領域へアクセスするとページフォールトが発生する。
また、フラットなページテーブルでは管理のための容量が枯渇するため、階層化したヒューページというデータ構造を使うことがある。
### メモリ領域の確保
システムコールのmmap関数を呼び出すことでメモリ領域を確保することが出来る。ただし、ページテーブルに仮想メモリアドレスが記録されるものの、実際の物理メモリアドレスは初めて、仮想メモリアドレスにアクセスがあった時に取得される。 -> デマントページングたる機能(賢い)
## 5.プロセス管理(応用編)
fork関数がなぜ早いのか: コピーオンライトでページテーブルだけをコピーしているため。その後、書き込み(wirte)が必要になった領域だけ別のアドレス空間を用意して、割り当てる。
### プロセス間通信
複数のプロセスで共通の領域を使いたい場合は共有メモリやプロセス間でシグナルを送ったり、パイプ、ソケットを使うことで実現することが出来る。
その際には排他制御の問題がつきまとうため、並行処理は難しい。。。
## 6.デバイスアクセス
デバイスへのアクセスはデバイスファイル(/dev)の使用してデバイスドライバが行う。プリンタなどを購入するとデバイスドライバをインストールするあれ。
デバイスには2つの種類がある。
- キャラクタデバイス: 入出力だけを行う。キーボードやマウス...
- ブロックデバイス: シークが可能なためHDDやSDD...
デバイスはレジスタを内部に持っている。MMIO(メモリマップトI/O)によって、デバイスはアドレス空間にメモリとレジスタの2つの領域を持つ。
カーネルはアドレスにアクセスして、処理が完了したかどうかを判定する。2つの判定方法がある。
- ポーリング: 一定周期ごとに終了しているかどうかを確認
- 割り込み: 処理が終了した時点で強制的に終了時の処理を実行
## 7.ファイルシステム
ストレージにアクセスする場合は、デバイスドライバでHDD、SSDにアクセスせずにファイルシステムを介している。
ファイルシステムがないと、どの領域にどれだけの容量を確保すれば良いか、他のプロセスから使用されていないかなどを自分で制御しなければならず、大変手間。
メモリマップトファイルという機能で、ストレージのデータをメモリ上にコピーしている。またメモリに書き込まれたファイルをストレージに書き戻す。
### ファイルシステムの種類
様々なファイルシステムが存在しており、機能が異なる。
容量制限があったり、整合性の保持の仕方などの違いがあり、どれを選ぶかはユーザーの目的に合わせて変えるべき。
### ジャーナリング
ファイルに不整合が生じないようにする仕組み。実際にはファイルのコピーなどの実行中にマシンの電源が強制OFFになった場合などが考えられる。
コピーをする前に何をするかをログファイルとして書き出し、このファイルを元にコピーを行う。電源OFF後、ログファイルを読み込み、どこまで終了したかを確認して、処理を継続して行う。
## 8.記憶階層
アクセス速度の速さの順(値段は逆になる)
```
レジスタ -> キャッシュメモリ -> メモリ -> ストレージデバイス
```
キャッシュメモリを使用することで、アクセス速度の問題を回避している。
キャッシュすれば速度は当然、早くなるがダーティーなデータを許すことになるため、ダーティーとなったデータはフラグがつけられて、再度、読み込みが行われる。
キャッシュメモリの有効性についてはアクセスが局所的であるほど、効果が期待出来る。(8:2の法則≒パレードの法則)
また、メモリ領域だけではなくストレージデータのキャッシュもされている。これはページキャッシュと呼ばれ、データをページ単位でキャッシュする。
### スワップ
メモリが足りなくなった際に、ストレージにスワップ領域を用意しておくことでOOMを防ぐことが出来る。ただし、ストレージへのアクセス速度はメモリと比べて非常に遅いため注意が必要。
## 9.ブロック層
HDDはデータを円盤にセクタという単位で分割している。
このセクタをスイングアームを使うことで、データの読み書きをしている(レコードと原理は同じ?)
なので、どの順番でセクタを読み込むか、複数のアクセス依頼があった際にまとめて読み込むか...といったスケジューリングが重要になるためI/Oスケジューラーが用意されている。
I/Oスケジューラーの仕事: 依頼をまとめる、ソートする
またreadheadという0~3セクタの読み込み依頼があった際に、まとめてセクタ6まで読み込むといった機能もある。
### 指標
- スループット: 1秒間にどれだけ〇〇が可能か
- レイテンシ: 1レコードのデータを取得するまでにどれだけ時間がかかるか
### SSDの登場
SSDの登場によってセクタは必要なくなり、従来のI/Oスケジューラーでは十分な性能が出せなくなった。
## 10.仮想化機能
1台のマシンに複数のOSをインストールするために仮想化という機能が使われる。仮想化することで1台のマシンのCPU、メモリ、ストレージを仮想化したOSにリソースを分割する。
またホストOSからは1プロセスにしか見えず、仮想化したOSからホストOSへはアクセスすることは出来ない。
## 11.コンテナ
仮想マシンとは異なり、CPU、メモリ、ストレージのリソースを分割して分け与えられる訳ではなく単一のプロセスとして動作する。そのため軽量で立ち上げが非常に高速。
コンテナには種類がありシステムコンテナとアプリケーションコンテナがある。システムコンテナは実行環境とアプリケーションをまるごと用意したもの。仮想マシンに似ている。
アプリケーションコンテナはアプリケーションを動作だけるだけの環境で軽量。
### namespace
様々なリソースを分割してグループ化するためのカーネルの仕組み。pidやuserなど様々なnamespaceがある。docekerのコンテナは様々なnamespaceを分割して作成した1つのプロセスもしくは、複数のプロセスによって実現されている。
docker コンテナ: A -> 独立したnamespaceを作成する
- pid ns: A
- usr ns: A
- mount ns: A
## 12.cgroup
CPUやメモリなどの使用率を自由に設定することが出来る機能。プロセスAはCPUを50%まで。プロセスBはCPUを50%までのような設定が可能。
laasなどでは、VPSを構築する際に、一部のユーザーがリソースを独占しないようにするためにcgroupを使っているとのこと。