JUNのブログ

JUNのブログ

活動記録や技術メモ

「Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識」を読んだ

「[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識」を読んだ.

Linuxを日頃から触っているけど, 中身を全然知らなかったので読んだ.

Linuxの内部の仕組みを大まかに理解出来るよい本だった.

本書は実験プログラムと豊富な図解と丁寧な解説で自分のようなOSやコンピューターアーキテクチャについて殆ど知識が無いような人でも詰まる事無く理解出来た. (Linux触ったこと無い人には難しいかもしれん)

この本を読めばLinuxカーネルを完全に理解したとはならない気がする. どちらかというと, これからLinuxカーネルやOSをがっつり勉強していくという人の初めの第一歩に最適だと思った. (初手ヘネパタ本とか挫折しそうだし)

後はLinuxを日頃から使っているエンジニアなんかも読んでおくと日頃から触っているシステムについて詳しく慣れるので良いと思った.

手を動かしながら楽しく読める本だったのでおすすめです.

第1章 コンピュータシステムの概要

この章ではOSがコンピューターにおいてどのような役割を担っており, それによって何が嬉しいのかが書かれている.

OSが無いと個々のプログラムが各ハードウェアデバイスにアクセスするためにハードウェアデバイスごとにアセンブリなどでコード書かないといけないの大変だよねー. じゃあそれをまとめて共通のインターフェースとして使えるようにすれば楽じゃん. って感じか.

このあたりの内容は前から知っているものだったので, まぁ確かにそうだな って感じ.

第2章 ユーザーモードで実現する機能

ハードウェアの抽象化などの役割がOSにあるのは前から知っていたが, この章ではCPUのユーザーモードカーネルモードというのが出てきてこれは初めて知ったのでびっくりした.

自分はC言語でwrite, readなどのシステムコールを何度も使ったことがあったが, 実際のところそれらがどのように動いているのかは何も知らなかった. しかし, ここではCPUのモードという概念が登場し, システムコールが呼ばれた際にはCPUがユーザーモードからカーネルモードに移動して, システムコールを処理することがわかった.

システムコールにはプロセスの生成やメモリの確保,解法,プロセス間通信,ネットワーク, ファイルシステム操作, デバイスアクセスなどの色々な種類があり, それらの機能を使いたい時にシステムコールという形でカーネルに依頼する.

システムコールはCPUの特殊な命令を実行することで発行するらしい. プロセスは通常ユーザーモードで動作するが, システムコールが発行されるとCPUはカーネルモードに遷移して, 依頼内容に応じたカーネルの処理が動き, それが完了したらまたユーザーモードに戻る.

そしてユーザープロセスがシステムコールを使わず直接CPUのモードを変更するは無い. (もしあるならカーネルの意味がない)

第3章 プロセス管理

ここではカーネルによるプロセス生成,削除,管理などについて説明してる.

Linuxには fork()execve() という2つの関数がある.

  • fork(): 同じプログラムの処理を複数のプロセスに分けて処理する という目的で使う(Webサーバーによる複数リクエストの受付). 処理としては,
    1. 子プロセス用にメモリ領域を確保し, 親プロセスのメモリをコピーする.
    2. fork()関数の実行が完了したところからプログラムは再開する. (親プロセスのメモリを丸々コピーしているのでプログラムカウンタもコピーされてるって感じか?)
    3. 親プロセスと子プロセスは一般的に違うコードを実行する. これには fork() 関数の戻り値が親プロセスでは子プロセスのプロセスID, 子プロセスでは0が返ってくることを利用して分岐させる.
  • execve(): 全く別のプログラムを生成する時に使う(bashから各種プログラムの新規作成). 処理としては,
    1. 実行ファイルからプロセスに必要なメモリマップを読み出す.
    2. 現在のプロセスのメモリを新しいプロセスのデータで上書きする.
    3. 新しいプロセスの最初の命令から実行を開始する.

ちなみにメモリマップとは, 「データやコードがメモリ内にどのように配置されるか?」という情報のこと. (で合ってるはず...)

どの命令(のアドレス)から実行すればよいかは実行ファイル内に書かれている.

Linuxにおいては実行ファイルはELF(Executable Linkable Format)という形式になっており, プロセスに必要なメモリマップはELFファイルのプログラムヘッダの部分の情報を元に計算されるのかな?

ja.wikipedia.org

別のプロセスを新規作成する場合は, 親となるプロセスが fork() を読んで, 生成された子プロセスがexecve()を呼ぶ "fork and exec" によるプロセス生成が多いらしい.

github.com

あと, 勝手に勘違いしていたんだけど, 親プロセスが死んでも(終了しても)子プロセスが芋づる式に死ぬわけでは無いっぽい. そもそも親プロセスという言い方をしているが, 正確には 「親となるプロセス」であり, 親となるプロセスからfork()によって生成されたプロセスは別に親のことなんかどうでもいいっぽい.

親プロセスが死んだら, 子プロセスは initプロセス(initはsystemdのシンボリックリンク)の養子になるらしい.

子プロセスを残したまま親プロセスがkillされてしまった場合その子プロセスはどうなるのだろうか?

その場合Linuxでは「initプロセスの養子」となるようになっている。「親プロセスがいなくなったからといって子プロセスが停止するとは限らない」ところに気をつけてほしい。

しかし一般的なデーモンプログラムなどは自分が死ぬときは基本的に子プロセスをkillしてから寿命をまっとうするようになっている場合が多いだろう。

eng-entrance.com

自分はNginxのプロセスとかをイメージして勝手に親プロセスが死んだら子プロセスも死ぬと思っていたけど, これはただ単に多くのプログラムがそのように実装しているだけらしい.

プロセスの親プロセスをpsコマンドで見る方法は以下のリンクを参照.

www.atmarkit.co.jp

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

この章ではプロセススケジューラについて説明してる.

プロセススケジューラによってプロセスは一定時間(タイムスライス)ごとにプロセスが切り替わりながら実行される.

また1つのコアに付き同時に実行できるプロセスは1つなので(ハイパースレッドは無視してもろて...), 昨今のCPUのコア数増加のありがたさがわかった.

で, この章では論理CPU(OSが認識出来るCPUの数)とプロセスを変えると実行時間がどのようになるかという実験をした.

結論としては,

  • プロセス数とコア数が同じ: 1つのプロセスが1つのコアを独占して使えるので最速.
  • プロセス数がコア数より多い時: 1つのコアで複数プロセスを切り替えながら実行する(これをコンテキストスイッチと呼ぶ)ので, 1つのプロセスが1つのコアの時に比べて遅くなる. 単純に, 4プロセスを2コアで動かすと2プロセスを2コアで動かしたときよりも1プロセスにかかる時間が2倍になる.

ちなみにコンテキストスイッチはプロセスの切りの良いところで発生するものではなく, タイムスライスが切れるとそのプロセスが今何を実行していようが容赦なく次の別のプロセスの実行へと切り替わる.

プロセスには状態があり, 状態には以下のようなものがある.

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

とりあえずこの4つを覚えればいいっぽい.

イベントっていうのは, 所定時間(3秒とか)が経過するの待っていたり, キーボードなどのデバイスの入力待ちだったり, ストレージデバイスI/O待ちだったり, ネットワークの送受信待ちだったりいろいろある.

各プロセスの状態は ps ax のSTATフィールドを見ればわかる.

qiita.com

論理CPUが複数ある場合のプロセススケジューリングはプロセススケジューラの中にあるロードバランサによって複数の論理CPU間で公平にプロセスを分配するようになっている.

例えば論理CPUが2つあって, CPU0にはプロセスが2つあり, CPU1にはプロセスが1つある場合, ここで新しいプロセスを生成したら, そのプロセスはCPU1のほうで実行されることになる.

timeコマンドによって表示される各項目の意味はいかのようなものである.

real 11.12  # 実際の経過時間
user 11.09  # CPUがユーザーモードで動作した時間
sys 0.00    # システムコールを実行していた時間(CPUがカーネルモードで動作した時間)

ちなみにtimeコマンドの user と sys の部分は, 2つの論理CPUで動かした場合は2つのCPUで動作した時間を合計したものになるので, real の項目より user や sys の項目の値のが方が大きくなることがある.

プロセスには優先度(nice値)というものがあり, nice() というシステムコールによって-19から20までの間で変更が可能である. デフォルトは0. -19が一番優先度が高く, 20が一番優先度が低い. 優先度が高いプロセスほどCPU使用時間の割合が他のプロセスに比べて長くなる.

優先度を上げるのはroot権限が必要だが, 優先度を下げるのは誰でも出来る.

第5章 メモリ管理

Linuxは, システムに搭載されている全メモリを, カーネルのメモリ管理システムと呼ばれる機能を使って管理している.

で, 普通にプログラムが直にメモリを触れてしまうと, メモリの確保,解放の積み重ねでメモリの断片化が起きたり, あるプロセスが別のプロセスのメモリ領域にアクセス出来たり, メモリ配置がハードウェアアドレスの番地なのでマルチプロセスの際にいちいち他のプログラムに配慮しなくてはいけなかったりと大変なので, 仮想記憶という仕組みを使う.

仮想記憶とは, カーネルが使うメモリ内にページテーブルを保持し, そのページテーブルに仮想アドレスと物理アドレスの変換を保存しておき, プロセスがメモリにアクセスする際にはそのページテーブルを元に物理アドレスにアクセスするという感じ.

ページテーブルはメモリをページという単位で区切って管理していて, 変換はページ単位で行われる. ページテーブルの中の1つのページに対応するデータをページエントリと呼ぶ. ページサイズはCPUアーキテクチャごとに決まっていて, x86_64の時は4Kバイト.

仮想記憶(ページテーブル)のおかげで, メモリ断片化の問題は, 空きメモリを上手いこと仮想アドレスに紐付ければOK. あるプロセスが別のメモリ領域にアクセス出来る問題は, ページテーブルの仮想アドレスの範囲外にアクセスしようとしたらページフォールトというCPU割り込みが発生し, カーネルがそれを捉え, SIGSEGVというシグナルをプロセスに通知し, プロセスを強制終了させる. 仮想アドレスを使えば各プロセスは自分の好きなようにアドレスを指定出来る(物理アドレスを考えなくてもよい)ので他のプロセスと共存することが可能.

で, 実際のLinuxカーネルはページテーブルを使うだけではなく, デマンドページング と呼ばれる方法を使ってメモリを管理している.

この方法は, メモリ確保のシステムコールが呼ばれた際にはページテーブルに物理アドレスが未定のページエントリを作成し(仮想メモリの確保), その仮想アドレスに初めてアクセスが来た時に初めて実際にその仮想アドレスに物理アドレスを紐付ける(物理メモリの確保)というもの.

この流れとしては,

  1. プログラムが仮想メモリは割当済みだが物理メモリ未割り当ての領域にアクセス
  2. CPUがアクセスしようと試みるが, 未割り当てなのでページフォールトが発生.
  3. カーネルページフォールトを検知し物理メモリを仮想メモリに割り当てる
  4. ユーザーモードに戻ってプロセスの実行を継続する.

なので, 例えば8GBのメモリ搭載のシステムで100GBのメモリを確保(仮想メモリの確保)は可能であるが, 実際にアクセスしようとした時にエラーになる.

malloc() は実はメモリを毎回確保する処理をしているわけではなく, ある程度mmap()でメモリを確保し, glibcのメモリプールとして確保したメモリ(仮想メモリ)を保持しておき, malloc() が呼ばれた際にバイト単位で切り出してプログラムにかえしている. ちなみにメモリプールが足りなくなるとまた mmap() でメモリを確保する. ちなみに mmap() はページ単位でメモリを獲得するのでそのままだとバイト単位でもメモリ確保は出来ない.

実は fork() も仮想記憶によって高速化されている. fork() 実行時はメモリをまるごとコピーするのではなく, 親プロセスのページテーブルのみコピーしてくる. そして, 親か子プロセスのどちらか変更を加えたらそのページのコピーが作られ, 書き込み側のプロセスのページテーブルの仮想アドレスに対応する物理アドレスが更新され, メモリの共有が解除される. メモリが共有されたらもう一方のプロセスも読み書きを自由に出来るようなる. この仕組みをコピーオンライトという.

メモリが足りねぇーってなったら, 一部メモリ内のデータをスワップメモリに退避させてメモリに空きを作る. スワップメモリとはストレージデバイスを一時的にメモリとして扱うものである. この時退避先の領域のことをスワップメモリという. メモリからスワップ領域に退避させることをスワップアウトという. 退避させたメモリのページエントリの物理メモリにはスワップ領域のアドレスが書かれている.

逆にスワップ領域に退避させていたデータにアクセスしようとした場合には, スワップ領域からメモリ領域に戻す処理(スワップイン)が行われる.

スワップアウト, スワップイン合わせてスワッピングという.

スワップ領域は便利なものだが, ストレージデバイスはメモリに比べて格段に遅いので, スワッピングが多発する場合にはシステムの速度が低下することがある. このようなスワッピングが繰り返されてシステムの速度が低下することをスラッシングと呼ぶ.

ページテーブル自体が肥大化しないように階層型ページテーブルやヒューページとかいう仕組みを使ってうまいことしているらしい.

第6章 記憶階層

CPUからレジスタへのアクセス時間に比べてメモリへのアクセス時間は極端に遅い.

なので, CPU内にはキャッシュメモリと呼ばれるメモリから読み出した情報を貯めておく記憶領域があり, メモリへのアクセスを減らす工夫がなされている. キャッシュメモリはメモリアクセスに比べて数倍から数十倍早い.

一度目の特定のメモリへのアクセスはメモリからキャッシュメモリに読み出して, そんでキャッシュメモリから読み出す. 2回目以降はキャッシュメモリからの読み出しのみで済むので高速になる.

キャッシュメモリのデータを変更した場合には即時にメモリに変更が反映されるのではなく, バックグラウンド処理としてメモリに書き戻される.

キャッシュメモリが足りなくなったら既存のキャッシュライン(キャッシュの1単位)を破棄する. その時, ダーティフラグが立っていたらメモリに書き戻してから破棄する.

キャッシュメモリは階層構造になっていてL1,2,3キャッシュというのがある.

で, キャッシュメモリ内にデータがあれば高速化するけど, 実際に上手く働かくかというのは, 多くの場合YES. 多くのプログラムは参照の局所性と呼ばれる次のような特徴がある.

  • 時間的局所性: ある時点でアクセスされたデータは近い将来再びアクセスする可能性が高い. (ループの処理のコード領域とか)
  • 空間的局所性: ある時点であるデータにアクセスされると, それに近い場所にアクセス可能性が高い. (配列要素の全走査など)

あと, 今のままだとページテーブルの参照が結局物理メモリにアクセスしていて遅いので, Translation Lookaside Bufferというページテーブルをキャッシュメモリと同様に高速アクセス可能な領域があるらしい.

ja.wikipedia.org

で, ストレージデバイスへのアクセスはメモリに比べてもさらに激遅なので, メモリキャッシュのストレージ版であるページキャッシュという機能がある.

これはファイルからデータを読み出す時に, 直接データをプロセスのメモリ空間内にコピーするのではなく, 一度カーネル用メモリ内にページキャッシュとして保存し, それをプロセスのメモリ空間内にコピーするというものである.

書き込みについても同様で, 直接ストレージデバイスを書き換えるのではなく, ページキャッシュを書き換え, 書き換えられた箇所はバックグラウンドでストレージデバイスに反映されるようになっている.

ちなみに, ページキャッシュはカーネル内のメモリにあるので, 別のプロセスが同じファイルを読み出そうとした場合にも効果を発揮する.

ページキャッシュにもダーティフラグ的なものがあり, 考え方はキャッシュメモリのものとほぼ同じ.

ちなみにダーティフラグが立ったページが存在した状態(ストレージデバイスにまだ反映されていないページがある状態)で電源を消すとその書き換わったページの部分のデータは消滅する.

消滅してほしくなかったら同期的な書き込みを有効にするフラグ O_SYNC フラグを open() 時に指定して上げれば良い. こうすると書き込んだ時にページキャッシュだけでなくストレージデバイスにも同時に書き込まれる.

ここまで述べた通り, CPUは常に計算している訳ではなく, CPUの計算速度に比べてメモリアクセスやストレージデバイスはとても時間がかかるのでCPUには割と暇な時間(データ転送待ち)がある. そこで, ハードウェア側で認識できる論理CPUの数を2倍にして, 一定の条件下で2つのCPUがあるかのように動作させるハイパースレッドという機能がある. ただし, 必ずしも性能が向上するわけではなく, 性能が向上したりしなかったりするので, そのへんは注意が必要.

7章 ファイルシステム

ストレージデバイスを直接扱うということは各ファイルがどのメモリ番地から何バイトにあたって存在するかなどを記録しておかねばならないのでめんどくさすぎる.

なのでファイルシステムというものを使う.

ファイルシステムには ext4, XFS, Brtfs など色々種類があるが全て統一的なインターフェースでアクセス出来るようになっており, ユーザープログラム側はファイルシステムの差異を気にしなくても良いように出来ている.

それぞれのファイルシステムの違いとしてはファイルシステムの不整合時の修復方法の違いなど色々あるっぽいので用途に合わせて選ぶのが良さそう. Linuxだとext4がデフォルトっぽい?

ファイルには2つのデータがある

  • データ: 普通にデータ. 動画とかテキストとか写真とか.
  • メタデータ: ファイルの名前やストレージデバイス上での位置, サイズなどの補助的な情報. 情報としては以下のようなものがある.
    • 種類: ファイルかディレクトリかその他か
    • 時刻情報: 作成した時間, 最後にアクセスした時間, 最後に内容を変更した時間
    • 権限情報: どのユーザーがファイルにアクセス出来るか.

で, こっからおもしろくて, ファイルとディレクトリ以外にもデバイスファイルというものがある. Linuxは自分が動作しているハードウェア上のデバイスをほぼ全てファイルとして表現している. (Everything is a file)

unix.stackexchange.com

各デバイス/dev 以下に存在する.

バイスファイルには2種類ある

  • キャラクタデバイス: 端末, キーボード, マウスなどの読み書きは出来るがシークは出来ないもの.
  • ブロックデバイス: HDDやSSDなどのストレージデバイス.

例えば端末のデバイスファイルは wirte() システムコールでデータを端末に出力でき, read()システムコールでデータを端末から読み取れる.

$ ps
    PID TTY          TIME CMD
2873558 pts/10   00:00:00 ps
4186478 pts/10   00:00:03 zsh
$ echo "HELLO" > /dev/pts/10 #別端末から実行すると pts/10 に対応する端末に文字が出力される

qiita.com

そして実はファイルシステムを介さずに直接ストレージデバイスの対応する番地のデータを書き換えることも出来る. ただし, これはあくまで実験用であって, 普通に使うとシステムが壊れる可能性があるのでしないほうが良い.

ファイルシステムには他にメモリベースのtmpfsという /tmp/var/run などの再起動したら消えるディレクトリで使わるものもある.

$ mount | grep tmpfs
udev on /dev type devtmpfs (rw,nosuid,noexec,relatime,size=8048832k,nr_inodes=2012208,mode=755)
tmpfs on /run type tmpfs (rw,nosuid,nodev,noexec,relatime,size=1615600k,mode=755)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)
tmpfs on /run/lock type tmpfs (rw,nosuid,nodev,noexec,relatime,size=5120k)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
tmpfs on /run/snapd/ns type tmpfs (rw,nosuid,nodev,noexec,relatime,size=1615600k,mode=755)
tmpfs on /run/user/1000 type tmpfs (rw,nosuid,nodev,relatime,size=1615596k,mode=700,uid=1000,gid=1000)

他にもネットワークデバイス用とかプロセス管理用(/procで使われる)とか色々種類がある.

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

ここではHDDやSSDがセクタという単位で読み出している的な話がされていた.

で, HDDはアクセスの際に機械的な動作であるスイングアームの調整とかでめっちゃ時間がかかると.

なので機械的な動作をなるべく減らすために, 連続したセクタを一度に読み出すようにした方が早い. なので, カーネル側のブロックデバイス層という部分のI/Oスケジューラという機能が連速したセクタにアクセスする場合には一度にまとめて取得するとか読み出す順番をソートするとか色々最適化してくれている.

また, 先読みという機能(これはプログラムの局所性に基づくものである)もある.

以下読んでる時に書いた雑なメモ

p7
プロセスが直接デバイスに触れないようにするためにハードウェアレベルで制限を行う.
具体的にはCPUには カーネルモードユーザーモードがあり, カーネルモードの時のみデバイスへアクセス出来る.
プロセスはユーザーモードで動作し, カーネル(デバイスドライバやメモリ管理システムなどのプロセスに触られたくないもの)はカーネルモードで動作する.
プロセスがデバイスアクセスなどのカーネルモードで管理している部分を利用したいときはシステムコールを使ってOSに依頼する.
システムコールカーネルとプロセスとのインターフェース.

キーワード
- POSIX: UNIX系OSがが備えるべき各種機能を定めた規格

p25
OSが提供するプログラム例の中に ウィンドウシステム: X がある!

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

p110
- mmap() という関数でLinuxカーネルに対して新規にメモリを要求するシステムコールを呼ぶ. - mmap() はページ単位(通常は4KB)でメモリ領域を獲得する. - malloc() は内部的にmmap()を呼び出している. - malloc() のバイト単位でのメモリ確保を実現するために, 事前にmmap()システムコールによってカーネルから大きなメモリ領域を確保してプールして貯めておき, プログラムからのmalloc()発行時にその領域から必要な量をバイト単位で切り出してプログラムに返すという処理をしている. - プールしているメモリ領域がなくなればサイドmmap()を発行して新たなメモリ領域を獲得する.

p122: デマンドページング
- やはりmalloc()mmap() でメモリを獲得しても, 実際に物理メモリが確保されるのはそのメモリアドレスにアクセスした瞬間らしい. - 原理としては, mmap() などでメモリを確保する. (正確には 仮想メモリを確保する と言うらしい) - ページテーブル(仮想アドレスと物理アドレスを紐付けるやつ)にエントリを追加. ただしこの時点では物理アドレスの方は決まっていない. - プログラムが確保した領域にアクセスするとCPUがページフォールトを起こす. それをカーネルが検知して, 物理メモリを確保し, ページテーブルを書き換えて, プログラムの処理を続行する. (これを 物理メモリを確保した と表現する) - この時プログラムはページフォールトが発生したことに気づかない. - ちなみに2回目以降のアクセス時にはページフォールトは発生しない. なぜなら物理メモリが既に確保済みだから.

p132: メモリの枯渇
- メモリの枯渇には2種類ある - 仮想メモリの枯渇 - 仮想アドレス空間がいっぱいの状態でメモリを確保しようとすると失敗する. - これは32bit環境ではアドレス空間(ページングテーブル)のサイズが約4GBしか無かったので昔は大規模ソフトウェアでこの問題が発生していた. - いまは64bit環境で, 128TBくらいのアドレス空間があるので大体大丈夫. - 物理メモリの枯渇 - 物理メモリに空きが無かったら発生する.

スワッピング
- 物理メモリが足りなくなった際に, 一部メモリのデータをスワップ領域に移動させることをスワップアウトという. - スワップ領域に退避させていたデータにプログラムがアクセスした際にはページフォールトが発生し, スワップ領域から物理メモリにデータが移動される. これをスワップインという. - この2つのことを含めてスワッピングと呼ぶ. - スワッピングはページ単位で行われるので, ページングとも呼ばれたりする. その際はページアウト, ページインとなる.

p148 2種類のページフォールト
- メジャーフォールト: スワッピングのようにストレージデバイスへのアクセスが発生するページフォールトのこと - マイナーフォールト: ストレージデバイスへのアクセスが発生しないページフォールトのこと

p229: procfs : システムに存在するプロセスに関する情報を得るためのファイルシステム
- cat /proc/{pid}/maps : プロセスのメモリマップ - cat /proc/{pid}/cmdline : プロセスのコマンドライン引数 - cat /proc/{pid}/stat: プロセスの状態やこれまでに使用したCPU時間, 優先度, 使用メモリ量など - procfsではプロセス以外にも以下のような情報も得られる - /proc/info: システムに搭載しているCPUに関する情報 - /proc/diskstats: システムに搭載しているストレージデバイスに関する情報 - /proc/meminfo: システムに搭載しているメモリに関する情報 - /proc/sys/: カーネルの各種チューニングパラメータに対応するファイルが入っている. sysctl コマンドと/etc/sysctl.conf によって変更するパラメータに1対1で対応 - ps, sar, top, freeなどのOSがが提供する各種情報を表示するコマンドは procfs から情報を取得している. - 各種ファイルやディレクトリの詳細は man proc を参照.

p230: cgroup: 各種リソースの使用量に制限を設けるcgroupという機能があり, cgroupfsを通して操作する.
- CPUやメモリの使用量を制限することが出来る. - この機能はDockerなどのコンテナ管理ソフトウェアや, 複数の仮想マシンが動作するシステムで使われている. - 特に1つのシステム上に複数の顧客のコンテナや仮想マシンが共存するマルチテナント構成のシステムにおいて使われている.

使ったコマンド
- strace: strace ./a.out 呼ばれたシステムコールを表示する. - sar: sar -P ALL 1 1 とすると1秒間のCPUがどのモードになっているか割合が見れる. - sarコマンド自体はCPUやメモリ, ネットワーク, ディスクIOなどの負荷が見れるコマンド - ldd: ldd /bin/echo プログラムがどのようなライブラリをリンクしているかを見れる. - readelf: readelf -h /bin/sleep #エントリポイントアドレスの取得 Linuxの実行ファイルのフォーマットであるELF(Executable Linkable Format)の情報を見る. - readelf -S /bin/sleep # コードとデータのファイル内オフセット, サイズ, 開始アドレスを取得 - taskset: taskset -c 0 ./a.out コマンドライン引数で指定したプログラムを-cオプションで指定した論理CPU上で動作させる - ps ax でプロセスの状態を見る psコマンドまとめ - /proc/cpuinfo にCPUの情報が載っている - ps -eo pid,comm,time,etime: プロセスID, コマンド名, CPU使用時間, 経過時間 を表示する - free システムが搭載するメモリの量と使用中のメモリの量を見る. (出力はkB単位) - sar -r 1 でシステムの状態を1秒毎に表示 - sar -B 1でページングに関する情報を1秒毎に表示 - swapon --show システムのスワップ領域を表示 (free でも確認可能) - sar -W 1 でスワッピング(ページング)に関する情報を1秒毎に表示

関数
- system()C言語からシェルコマンド実行出来る (man 3 system)