試して理解 Linuxのしくみを読んだ時のメモ
「試して理解Linuxのしくみ」を読んだ時のメモです。 実際にコードを実行しながら学ぶことで、プロセス、メモリ、ストレージ周りのしくみの理解が深まりました。しくみがわかることでシステムのトラブルシューティングなどに役立つのかなと思います。
2章 ユーザーモードで実現する機能
- CPUにはユーザーモードとカーネルモードがある。
- ユーザーモードで動くプログラムには、プロセスに固有のプログラムやOS外のライブラリやOSのライブラリなどがある。
- カーネルモードで動くプログラムは、「プロセスの生成や削除、メモリの確保開放、プロセス間通信や、ネットワーク、ファイルシステムの操作、ファイル操作(デバイスアクセス)」がある。これらの処理を実現したいときには、ユーザーモードで動くプロセスがシステムコールを介してカーネルに処理を依頼する。この時、CPUの状態がカーネルモードに遷移して処理が実行される。
sarコマンドでプロセスがユーザーモードとカーネルモードどちらで実行しているかの割合を確認できる。sarコマンドは、システムの負荷状況を確認するコマンドで、過去に遡って情報を取得することができる。%systemがカーネルモード。%userと%niceがユーザーモード。%niceはnice値の変更により、優先度を変更されたプロセスが実行された割合。
- システムコールを呼び出す際には、各言語に用意されたシステムコールラッパー関数を利用する。もしもラッパー関数が存在しなかったら、アセンブリ言語でシステムコールを呼び出すプログラムを自分で書かなければいけなくなり、大変。
- lddコマンドで、プログラムがリンクしているライブラリを確認できる。
glibcは標準Cライブラリであり、システムコールラッパー関数も含んでいる。pythonは内部的に、libcをリンクしている。
straceコマンドでプログラムが呼び出すシステムコールを確認できる。
3章 プロセス管理
Linuxにおいて、プロセス生成には2つの目的がある
fork関数とexecve関数が使われる。
fork関数は、親プロセスをもとに子プロセスを生成する。親プロセスのメモリから子プロセスのメモリを複製し、親プロセスと子プロセスが違うコードを実行するように分岐する。
execve関数は、全く別のプログラムを生成するのに使われる。execve関数を発行したプロセスのメモリの内容を、実行ファイルのデータで上書きして新規のプロセスを生成する。このようにexecve関数では、プロセスの数は変わらず、あるプロセスを別のプロセスに置き換える。
_exit関数は、プログラム終了時に利用される。プロセスに割り当てていたメモリをすべて回収する。
4章 プロセススケジューラー
- プロセススケジューラーとは、複数のプロセスを同時に動作させるためのLinuxカーネルの機能(実際には順番に一定時間CPU時間を割り当てられて実行する)
- 一つのCPU上で同時に処理するプロセスは一つだけ
- 複数プロセスが実行可能な場合、個々のプロセスを適当な長さの時間(タイムスライス)ごとにCPU上で順番に処理する
プロセスの状態
- 実行状態(現在論理CPUを使っている)
- 実行待ち状態(CPU時間の割当を待っている)
- スリープ状態(何らかのイベントが発生するのを待っている。イベント発生までCPU時間は使わない。)
- ゾンビ状態
CPUのアイドル状態とは、アイドルプロセスという特殊なプロセスが割り当てられてCPUの動作が休止している状態。実行待ち、実行状態のプロセスが存在しない時にアイドル状態になる。sarコマンドのidleの部分。
- コンテキストスイッチとは、論理CPU上で動作するプロセスが切り替わること。頻繁に発生するとオーバーヘッドにより性能が低下する。
- スループットは、単位時間あたりの総仕事量。高いほどよい
- レイテンシは、それぞれの処理開始から終了までの経過時間。短いほどよい。
- 複数の論理CPUが存在する場合には、ロードバランサー、グローバルスケジューラー機能によって、プロセスを公平に分配する。
- timeコマンドで、プロセスの開始から終了までの経過時間と、プロセスが実際に論理CPUを使用した時間を確認できる。
- nice()システムコールでプロセスの実行優先度を変更できる。0がデフォルト。-19~20で数値が低いほど優先度が高い。rootユーザーだけ優先度を高くできる。システムに存在するプロセスは平等にCPU時間を得るが、nice値を変更することで特定のプロセスのCPU時間を増減させることができる。
わからないこと
- ゾンビ状態
ゾンビプロセスをなかなか見ることができない理由 | NHN Cloud Meetup
5章 メモリ管理
- Linuxカーネルのメモリ管理システムでシステムに搭載されている全メモリを管理している。
- Out Of Memoryとは、開放可能なメモリ領域もない状態でメモリ使用量がいっぱいになること。この時、メモリ管理システムはOOM killerという機能により適当なプロセスを選んで強制終了する。この挙動はOOM発生時にシステムを強制終了に変更することができる。
- Linuxではプロセスにメモリを割り当てる際に、物理メモリのアドレスを直接割り当てるのではなく、仮想メモリアドレスを割り当てる。プロセスは仮想メモリアドレスを介して間接的に物理メモリにアクセスする。
- 仮想メモリアドレスと物理メモリアドレスのマッピングの情報は、カーネルのメモリのページテーブルに保持される。ページテーブルの一つの対のデーターをページテーブルエントリーと呼ぶ。
- 仮想メモリアドレスを利用するメリット
- メモリの断片化を防ぐ
- 別用途のメモリへのアクセスを防ぐ
- マルチプロセスの扱いを簡単に
- ファイルマップは、ファイルの領域を仮想アドレス空間上にメモリマップする機能。ファイルの内容を物理メモリに読み出して仮想メモリアドレスにマッピングする。マップされた領域から読み出しや書き込みができる。その後、所定のタイミングでストレージデバイスのファイルに書き戻される。普通は、ファイルを開いてread()やwrite()のシステムコールでストレージ上のファイルを操作する。
- デマンドページングは、プロセスに仮想アドレスを割り当てるが、物理メモリの割当ては、仮想アドレス空間のページに最初にアクセスした時に実施する機能。これにより、無駄なメモリ消費を避ける。
- コピーオンライトは、プロセス生成のfork()システムコールを高速化する。親プロセスから子プロセスを生成時には、ページテーブルだけをコピーして親プロセスと子プロセスが共有のメモリを利用する。プロセスに書き込みを行う時に、実際にプロセスのメモリのコピーを作成する。
6章 記憶階層
記憶装置 (アクセス速度は上から順に速い) |
---|
レジスタ(CPU内) |
キャッシュメモリ(CPU内or外) |
メモリ |
ストレージデバイス |
キャッシュメモリは、CPUのレジスタ上での計算速度とメモリへのアクセス速度の差による遅延をなくすためのもの。メモリへのアクセス速度は、レジスタ上の計算に比べてとても遅い。そのため、計算が早くてもメモリへの読み書きの速度がボトルネックになり処理全体が遅延してしまう。キャッシュメモリからレジスタへのアクセスは、メモリからレジスタへのアクセスの数十倍高速である。メモリからレジスタへデータを読み出す際に、まずはキャッシュメモリがデータを読み込み、保存する。レジスタはキャッシュメモリからデータを読み込む。再度同じデータをCPUが読み出したい場合、キャッシュメモリから高速に読み出すことができる。
書き込みの場合は、CPUはレジスタで書き換えたデータをキャッシュメモリに書き込む。変更された(ダーディーである)という印をキャッシュライン(キャッシュメモリ上のデータ)につける。ダーティーなキャッシュラインはすぐにメモリに書き込まれず、所定のタイミングで書き込まれる。これをライトバックと呼ぶ。
ページキャッシュは、ストレージ上のファイルデータをメモリにキャッシュすること。プロセスがファイルデータを読み出す時、まずはカーネルのメモリ領域にファイルデータをページキャッシュとしてコピーする。そして、ページキャッシュをプロセスのメモリ領域にコピーする。再度、同じデータを読み出すときには、ページキャッシュからデータを高速に読み出すことができる。 書き込みの場合は、プロセスのメモリ上のデータを書き換えた後、ページキャッシュのデータだけを書き換えて、ダーティーマークをつけておく。ダーディなページは所定のタイミングでストレージに書き込まれる。これをライトバックと呼ぶ。
ページキャッシュはメモリに空き容量がある場合、作成され続ける。メモリの空き容量がいっぱいになってくると、ページキャッシュが解放される。解放の時は、ダーディーではないキャッシュを優先して解放する。足りない場合は、ダーディーなページをストレージに書き込んでから解放する。この時ストレージアクセスが生じるため、システム性能が劣化する。
sar -d -p コマンドで、ストレージデバイスごとにI/Oの統計情報を確認できる。
- sar -B コマンドでページインとページアウトの量の統計情報を確認できる。
- ページアウトは、ストレージからページキャッシュにデータを読み込むこと
- ページインは、ページキャッシュからストレージにデータを書き出すこと
7章 ファイルシステム
ファイルシステムとは、ストレージデバイス上のデータを名前、位置、サイズなどの情報を付加ファイルという単位で管理するもの。ストレージデバイス上のデータを扱いやすくする。
Linuxは、自身が動作しているハードウェア上のデバイスを、ほぼ全てファイルとして表現している。デバイスファイルは/dev以下にある。デバイスファイルも、通常のファイルと同様に、open(),read(),write()などのシステムコールを介してアクセスできる。デバイスファイルにはキャラクタデバイスとブロックデバイスの二種類がある。
fdiskコマンドでパーティションの作成、削除、情報表示ができる。
- tmpfsはメモリ上に作成するファイルシステム。ext4などのように好きなディレクトリにマウントできる。メモリ上にあるので電源を切るとファイルは消える。ストレージへのアクセスがなく高速にアクセス可能。再起動後に残る必要のない/tmpや/var/runにマウントされることが多い。
ネットワークファイルシステムは、リモートホスト上のファイルにアクセスする。windowsホスト上のファイルにはcifs。UNIX系OSホスト上のファイルにはnfsというファイルシステム。
procfs は、通常/proc以下にマウントされているファイルシステム。システムに存在するプロセスの情報やCPU、デバイス、リソース情報が保存されている。
df -a オプション サイズが 0 ブロックのファイルシステムや タイプがignore'または auto'のファイルシステムも リスト表示に含める (デフォルトでは省かれる)。例えば、procファイルシステムを表示できる。