As rookie

ルーキーインフラエンジニアがインフラのこと以外も結構書いてしまうブログ

『試して理解 Linuxのしくみ』を試してLinuxを理解する(PART.1)

前書き

こんにちは。

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

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

こちらの本を通して、Linuxを理解していきたいと思っています。
この記事はパート1です。

章ごとに記事を分けて投稿していこうと思っております。

本の内容をそのまま載せるのは良くないと思いますので、この本を読んで試したこと、理解したことのアウトプットが中心になると思います。
「わたしもやってみたい!」と思った人は、早速ポチりましょう。

本編

はじめに

この本によって、OSやハードウェアの理解を深めれば、

  • ハードウェアの特性を考慮した、良いソフトウェアを開発できるようになる。
  • どのような指標に基づいてシステムを設計すれば良いか分かるようになる。
  • OSやハードウェアに関するトラブルが起きた時に、冷静に対処できるようになる。

だそうです。

私の第一目標は、OSやハードウェアに関するトラブルが起きた時に、冷静に対処できるようになる。 です。 よく分からない問題を、分かる問題に変えていきたいところです。

実験環境はなるべく仮想マシンではなく実機上にシステムを構築してください。その理由は、仮想マシンを使用すると、一部の例については本書に記載したものと比べて挙動が変わることがあるからです。

私はUbuntu 16.04の実機を用いてい試していきます。

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

コンピュータは
インプット → 処理 → アウトプット
でできている。

1章は概要と章の説明でした。

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

OSはカーネル以外にも、ユーザモードで動作する様々なプログラムから構成される。 ユーザモードのプロセス処理から、システムコールを介してカーネルの処理を呼び出す。

システムコール

システムコールの種類

  • プロセス生成、削除
  • メモリ確保、下方
  • プロセス間通信
  • ネットワーク
  • ファイルシステム操作
  • ファイル操作

システムコールとCPUに関するメモ

  • システムコールを発行すると、CPUに割り込みというイベントが発生する。
  • ユーザプロセスからシステムコールを介さずに直接CPUのモードを変更する方法はない。

試す:システムコール

straceを使って、どんなシステムコールを発行するか確認する。 hello.c

#include <stdio.h>

int main(void)
{
    puts("hello world");
    return 0;
}
strace -o hello.log ./hello
cat hello.log
execve("./hello", ["./hello"], [/* 28 vars */]) = 0
.
. 略
.
write(1, "hello world\n", 12)           = 12
exit_group(0)                           = ?
+++ exited with 0 +++

プログラム的なシステムコール

write(1, "hello world\n", 12)     

pythonの例でも試す。内容は割愛。 1行に対して一つのシステムコールの情報が出力される。

man 2 <システムコール>

システムコールの内容を確認できる。

man 2 getdents
NAME
       getdents, getdents64 - get directory entries
.
.
.

過去に書いたbashスクリプトシステムコールを確認したくなった。

試す:ユーザモード or カーネルモード

# 1秒ごとにCPUの情報を取得
sar -P ALL 1
^C
平均値:      CPU     %user     %nice   %system   %iowait    %steal     %idle
平均値:      all      0.05      0.00      0.05      0.00      0.00     99.90
平均値:        0      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        1      0.20      0.00      0.40      0.00      0.00     99.40
平均値:        2      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        3      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        4      0.00      0.00      0.20      0.00      0.00     99.80
平均値:        5      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        6      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        7      0.00      0.00      0.00      0.00      0.00    100.00

8つのCPUについての情報が得られる。ほとんど idle

  • ユーザモードのCPU時間の割合
    • %user + %nice
  • カーネルモードのCPU時間の割合
    • %system

その他のフィールドは適宜説明してくれる。

無限ループするだけのC言語の例

無限ループの例でのsarの結果

平均値:      CPU     %user     %nice   %system   %iowait    %steal     %idle
平均値:      all     12.50      0.00      0.00      0.00      0.00     87.50
平均値:        0      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        1      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        2      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        3      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        4      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        5      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        6    100.00      0.00      0.00      0.00      0.00      0.00
平均値:        7      0.00      0.00      0.00      0.00      0.00    100.00

CPUコア6がユーザモード100%になっている。

親プロセスのプロセスIDを得るシステムコールを無限ループするプログラムの例

getppid() を無限ループさせる

平均値:      CPU     %user     %nice   %system   %iowait    %steal     %idle
平均値:      all      4.53      0.00      7.97      0.00      0.00     87.50
平均値:        0      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        1     36.09      0.00     63.91      0.00      0.00      0.00
平均値:        2      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        3      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        4      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        5      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        6      0.00      0.00      0.00      0.00      0.00    100.00
平均値:        7      0.00      0.00      0.00      0.00      0.00    100.00

CPUコア1をユーザモードで36.09%使い、カーネルモードで63.91%使っていたことが分かる。
ユーザモードの処理は「ループさせる」という処理。

%systemが大きい値になっている場合は、システムコールを発行させすぎたりしている事が考えれる。

試す:システムコールの所要時間

strace -T を用いる。hello.cでためす

strace -T -o hello.t.log ./hello
execve("./hello", ["./hello"], [/* 28 vars */]) = 0 <0.000614>
.
. 略
.
write(1, "hello world\n", 12)           = 12 <0.000031>
exit_group(0)                           = ?
+++ exited with 0 +++

0.000031 秒でwriteシステムコールが処理されていることがわかる。
だが、何秒か分からない。

変換 時, マイクロ秒

ここのWebサイトで変換してみることをオススメする。
31マイクロ秒だった。

読む:システムコールのラッパー関数

Linuxではプログラム作成を助けるために、様々ななライブラリ関数が用意されている。
システムコール高級言語から呼び出す事ができず、アーキテクチャ依存のアセンブリコードを使って呼び出す必要がある。

OSの助けがなければ、各プログラムはシステムコールを発行するたびにアーキテクチャ依存のアセンブリソースを書いて、高級言語から呼び出さなくてはなりません。

OSが助けてくれない世界は住みにくい世界です。

それを解決するのが、システムコールのラッパー関数。
これのおかげで、これを呼び出すだけで、システムコールを呼び出すことができる。

試す:C標準ライブラリ

標準Cライブラリに関するメモ

  • GNUプロジェクトが提供するglibc を標準ライブラリとして使用する。
    • Cプログラムはほとんど、glibc をリンクしている。
  • glibcシステムコールのラッパー関数を含む

プログラムがどのようなライブラリをリンクしているかをldd コマンド を用いて確認する。

$ ldd /bin/ls
    linux-vdso.so.1 =>  (0x00007ffc953b9000)
    libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007ff9b917a000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff9b8db0000)
    libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007ff9b8b40000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ff9b893c000)
    /lib64/ld-linux-x86-64.so.2 (0x00007ff9b939c000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ff9b871f000)

libcが標準ライブラリだそう。

親プロセスのプロセスIDを得るシステムコールを無限ループするプログラムのライブラリ

$ ldd ppidloop
    linux-vdso.so.1 =>  (0x00007fff431cc000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f63a90ac000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f63a9476000)

pythonなどでも libcをリンクしていて、pythonからひ標準Cライブラリを使っているそう。

GOはこんな感じ。

ldd /usr/local/go/bin/go
    linux-vdso.so.1 =>  (0x00007ffd76709000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f3abe62f000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3abe265000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f3abe84c000)

1章、2章終了。 次回は来週

トップへ
なし PART.1 PART.2