読書メモ:試して理解Linuxのしくみ

#第2章
###sarコマンド
プロセスがどのコアでどのようなモードで動いているかを表示するコマンド。
sarコマンドを実行すると、各CPUごとに、user/system/niceなどの割合が表示される。

###strace
straceでプログラムを実行すると、対象のプログラムが何のシステムコールを呼んでいるかがわかる。
-Tオプションをつけると、各システムコールの所要時間がわかるため、プログラムのパフォーマンスに課題があるときに見てみるといい。

#第3章
###fork()関数
同じプログラムの処理を複数のプロセスにわけて処理する目的の時にfork関数を使う。
fork()関数を呼び出した人が親関数の時には1以上の値が返り、子プロセスの場合は0が返るようになっている。
処理の流れとしては

- 子プロセス用メモリ領域を作成して、親プロセスのメモリをコピーする。
- 親プロセスと子プロセスで違うコードを実行するように分岐する。分岐する判断材料としては、fork()の戻り値が親プロセスでの実行と子プロセスでの実行で違うことを利用する。
- 子プロセスは親プロセスと全く同じプログラムを実行し始めるが、上記の分岐条件で分けられるようにしておけば、同じ処理を何回もするようなことはない。

###execve()関数
全く別のプロセスを生成する。
別のプロセスを新たに別のメモリ領域へ生成するわけではなく、実行中のプロセスを別のプロセスで置き換える。
処理の流れとしては、

- 実行ファイルを読みだして、プロセスのメモリマップに必要な情報を読み出す
- 現在のプロセスのメモリを新しいプロセスのメモリで上書きする
- 新しいプロセスのメモリから実行していく

メモリマップに必要な情報としては以下がある。

- コードを含むデータ領域のファイル上オフセット、サイズ、及びメモリマップ開始アドレス
- コード以外の変数等の上記と同じ情報
- 最初に実行する命令のメモリアドレス(エントリポイント)

尚、Linuxの実行ファイルはELF(Executable Linkable Format)というフォーマットを使用している。
Linuxの場合はreadelfというコマンドで必要な情報を見ることができる。

また、プログラム実行時に作成されるメモリマップは、/proc/{pid}/mapsというファイルから得られる。

####全く別のプロセスを新規に作成するには
親となるプロセスからfork()を発行し、復帰後にexecve()を呼ぶ。

#第4章
##プロセススケジューラ
- 同時に何個のプロセスが実行していようとも、ある瞬間に論理CPU上で動作できるプロセスは1つだけ
- 論理CPU上では、複数プロセスを順番に1つずつ動かして、1週したらまた一番目のプロセスから動かすラウンドロビン方式で動作している
- 各プロセスはおおよそ等しい長さのタイムスライスを持つ
- プロセス終了までのけいかじかんは、プロセス数に比例して増加する

##コンテキストスイッチ
論理CPU上でプロセスが切り替わること。
複数プロセスの場合、タイムスライスごとにコンテキストスイッチが発生し、プロセスが切り替わる。
プロセスがいかなるコード実行中でも、タイムスライスが切れると容赦なくコンテキストスイッチは発生する。

##プロセスの状態

|状態|意味|
|:-----------|:------------|
|実行状態|現在論理CPUを使っている|
|実行待ち状態|CPU時間が割り当てられるのを待っている|
|スリープ状態|何らかのイベントが発生するのを待っている。イベント発生までCPU時間は使わない|
|ゾンビ状態|プロセスが終了した後に親プロセスが終了状態を受け取るのを待っている|

状態遷移は以下、
![state_machine.png](https://qiita-image-store.s3.amazonaws.com/0/294245/f71a7e8f-52ef-de41-c120-f0dbf38e41e5.png)

プロセスが複数ある場合、CPU上で行われている処理は以下のようになっている。
![scheduling.png](https://qiita-image-store.s3.amazonaws.com/0/294245/9b1ea7ec-1c7d-413c-0267-a3a75bf238a5.png)
ポイントは以下の2つ、

- CPU上で実行できるプロセスは1つだけ
- スリープ中はCPU時間を使わない

###スループットとレイテンシ
- スループット:単位時間当たりの総仕事量。単位時間にどれだけのプロセスを実行できるか。
- レイテンシ:それぞれのプロセスの処理の終了までの経過時間。

###優先度
- 基本的には各プロセスに平等にCPUの実行権が回ってくる
- が、優先度を変えることにより、特定のプロセスにCPUを握る時間を長く与えることができる

#第5章
###メモリ割り当て
- メモリの割り当ては以下のタイミング
    - プロセス生成時
    - プロセス生成後に、追加で動的にメモリを割り当てるとき
- 動的メモリ割り当てには以下の問題がある
    - メモリの断片化
        - プロセス生成後にメモリの獲得と開放を繰り返すと発生する問題
        - 連続した領域ではなく、ばらばらの位置に空き領域ができてしまう
    - 別用途のメモリにアクセスできてしまう
        - アドレスさえ知ってしまえば他のプロセスから簡単にアクセスできてしまい、壊してしまう可能性がある
    - マルチプロセスの扱いが困難
        - 各プログラムが同じアドレ配置で動くようなプログラムの場合に、複数プログラムを同時に動かそうとすると、互いのメモリを壊してしまう
- 上記のような問題を解決するために仮想記憶という仕組みを持っている

####仮想記憶
- 物理的なメモリに直接アクセスさせるのではなく、仮想的なアドレスを見せて、その仮想アドレスから間接的にアクセスさせる仕組み

####ページテーブル
- 仮想アドレスと物理アドレスの変換に使うテーブル
- カーネルが持っている
- すべてのメモリをページという単位で持っていて、変換はページ単位で行われる
- 仮想アドレスと物理アドレスの対応情報をページテーブルエントリというデータで持っている

####ページフォルト
- ページテーブルエントリで管理していない仮想アドレスにアクセスしようとしたときに発生する例外
- CPU上に割り込みが発生する
- ページフォルト発生後にハンドラでページテーブルと書き換えることで新たに物理アドレスを仮想アドレスに割り当てる

####スワップ
- ストレージの一部を一時的にメモリとして使えるようにする機能
- 物理メモリが枯渇したときに使用中の物理メモリを一旦ストレージに保存する

#第6章 記憶階層
###キャッシュメモリ
- メモリを直接アクセスすることにより、レイテンシが大きくなってしまうため、一時的にメモリ上のデータを保存する場所
- 一度メモリから呼び出したデータをキャッシュメモリに保存することにより、CPUは直接メモリアクセスをする必要がなくなる
- 書き換えるときもCPUからはキャッシュ上のデータを書き換えれば、あとは良しなにメモリに書き出される
- 書き換えたキャッシュメモリ上のデータは実メモリとのデータと値が変わっているので、ダーティというフラグのついたデータとなる
- 特定のタイミングでダーティなデータはメモリに書き戻される

###階層型キャッシュメモリ
- L1、L2、L3キャッシュが存在する
- L1キャッシュが最も高速でL3が最も低速

###Translation Lookaside Buffer(TLB)
- ページテーブル自体はメモリ上に存在するので、キャッシュメモリを使えない
- ページテーブルのような仮想/物理変換表をキャッシュメモリと同様に高速にアクセスできるようにする領域のこと


#第7章 ファイルシステム
###ジャーナリング
- ファイルシステム内にジャーナル領域というユーザーが認識できないメタデータを保存
- そのデータ上にファイルシステムの更新に必要なアトミックな処理を書き出し
- 実際にその情報の通りファイルシステムの更新を行う

###コピーオンライト
- ファイルを更新するときに一旦別の場所に保存
- そのあとにリンクを張り替える

###cgroupfs
- プロセスごとに制限をかけるcgroupという機能があり、その機能を操作するために使用される
- CPU使用率やメモリの使用量をグループごとに制限をかける
- 仮想マシンに諸々の制限をかけるときに使われている仕組みである