カテゴリー別アーカイブ: linux

makefileおさらい

概要

linuxシステムのカーネル触る機会があるんだかないんだかで、改めてmakefileについておさらい。
今回はほとんど自分のメモみたいなものです。

makefileファイル

今回取り上げるのは

PREFIX = /usr/local
DEST_HEADER = $(PREFIX)/include/

TARGET = librpi_gpio.a
SRCS = rpi_gpiolib.c
OBJS = $(subst .c,.o,$(SRCS))
HEADERS = rpi_gpiolib.h rpi_gpio.h

RM := rm
CC := gcc
AR := ar

CPPFLAGS = -g -fPIC -O2 -l./
LDFLAGS = -g -fPIC
ARFLAGS = cr

$(TARGET): $(OBJS)
	$(AR) $(ARFLAGS) $@ $^ $(LOADLIBES)

install: $(TARGET) $(HEADERS)
	mkdir -p $(PREFIX)/lib/
	install -m644 $(TARGET) $(PREFIX)/lib/$(TARGET)
	mkdir -p $(DEST_HEADER)
	install -m644 $(HEADERS) $(DEST_HEADER)

.PHONY: clean

clean:
	$(RM) $(OBJS) $(TARGET)

出典は下記の書籍を読み進めていて出てきました。話はそれますが低レイヤの知識を得る際にオススメです。興味のある方は是非どうぞ。

さて私はそんなにmakefileをガッツリ触ったことがあるわけではないので、分からない点をひとつひとつ掘り下げていきます。

subst関数

まずここsubst関数について

SRCS = rpi_gpiolib.c
OBJS = $(subst .c,.o,$(SRCS))

Makefileで使えるユーザ関数であり、substは3つの引数を受け取ります。
c言語ライクにかくならば subst(from, to, text); のようなイメージになります。

この関数は text 文字列を対象として from を to に置換します。
したがって上記のMakefileの中で言うと “.c” という文字列を “.o” という文字列に置換することになります。
最終的に OBJS変数には rpi_gpiolib.o という文字列が格納されることになります。
文字通りコンパイル時のオブジェクトファイルの文字列を格納しているようです。

ビルド

AR := ar
ARFLAGS = cr

$(TARGET): $(OBJS)
	$(AR) $(ARFLAGS) $@ $^ $(LOADLIBES)

まずは細かいメタ変数の部分から確認すると
$@ はターゲット名なので librpi_gpio.a
$^ は全依存関係のリストなので rpi_gpiolib.c
を指すことになります。

その後いきなり arコマンドが展開されているので、.oを生成するルールが無いように見えるのですが
makefileにはデフォルトルールがありそこに .oターゲットが定義されており .cを依存ファイルとしてコンパイルするルールが存在します。
このあたりは make -p で確認できるようです。

下記のようなルールを確認しました。

make -p
...
%.o: %.c
#  commands to execute (built-in):
        $(COMPILE.c) $(OUTPUT_OPTION) $<
...

$(LOADLIBES)に関しては定義されていないようなので、この場合は特に意味を成さないですが、通常はリンクするライブラリのパスを入れます。

さて最後に ar コマンドに関してですが、アーカイブを作成します。
現在ではもっぱらライブラリの作成時にのみ使用されるコマンドで、複数オブジェクトをまとめてライブラリを作成する際の典型的なパターンです。
これによって複数の .oファイル(この例に関しては1角ファイルですが)から .aファイルを作成します。

インストール

install: $(TARGET) $(HEADERS)
	mkdir -p $(PREFIX)/lib/
	install -m644 $(TARGET) $(PREFIX)/lib/$(TARGET)
	mkdir -p $(DEST_HEADER)
	install -m644 $(HEADERS) $(DEST_HEADER)

installコマンドにより作成したライブラリファイルを適切な場所にコピーします。
cpコマンドと何が違うのかというと、コピーと同時に属性を指定することができます。
こちらは特に難しいことはないですね。

以上となります。
はじめはよくわからなかったですが分解して見たらかなりしっかりした構造であることがわかりました。
また調べていてこちらのページを見つけたのですが、リンカ/ローダの仕組みを歴史的な側面からも見つつ体系的に非常によくまとめられていたのでオススメです。

参考ページ

http://qiita.com/chibi929/items/b8c5f36434d5d3fbfa4a
http://0xcc.net/blog/archives/000107.html
http://linuxjf.osdn.jp/JFdocs/Program-Library-HOWTO/shared-libraries.html


yumとかリポジトリとかとりまとめ

yumコマンドを用いてよく新しいモジュールをインストールすることって多いと思います。
このコマンド、あまり細部まで理解していなくても検索して出てきたコマンドをそのまま実行してだまってyをタイプしておけば大体の場合動くと思います。

ここで「よし動いた大丈夫」って思っている方は多分この記事を見る必要はないと思います。

実際内部でどういうことが起こっているのか、レポジトリってなに?(epel?remi?)なぜいっぱいあるの?
とか結構疑問はいっぱい出てきます。

今日はその辺のyumを取り巻く環境について調べたいと思います。

yumコマンド概要を理解する

yumとはYellowdog Updater Modifiedの略であり(それはどうでもいいか)パッケージを管理するメタパッケージ管理システムである。
FedoraやCentOSなどRPMベースのディストリビューションの多くでよく利用されている。
Wikipediaから抜粋

レポジトリとは動作の確認できているコンパイル済みのバイナリファイルを集めたストレージのことで、自分でわざわざソースコードの入手・コンパイルなどを行わなくても簡単に導入できます。
またソフトウェアが必要としているソフトウェアの依存性も自動的に検出して、必要な物も一緒にインストールしてくれます。

レポジトリを理解する

CentOSでよくみるレポジトリepelやらremiについて調べましょう。

Red Hat Enterprise Linux (RHEL)向けのパッケージであり、RHELから分岐したディストリビューション(CentOSもここに含まれます)と互換性のあるパッケージです。
目的はFedoraで提供されているパッケージをRHEL系のディストリビューションでも互換性を提供することにあるようです。
Fedoraで提供されている高品質なパッケージをそのまま互換性をのと同時に、プロジェクトに関してもFedoraプロジェクトと同じガイドライン、ルール、ポリシーに従うことを順守しているようです。

またremiについても同様に、有志によって管理されているプロジェクトになります。
公式ページを参照するとremiの目的ですが、最新バージョンのPHPモジュールをFedoraやRHEL系のディストリビューションに提供することとあります。それぞれ目的が異なるんですね。

yumコマンドを理解する

yumでのよく使うコマンドの一例を紹介します。

コマンド 動作
install パッケージをインストールする
update パッケージを更新する
check-update アップデート可能なパッケージ一覧を表示する
remove パッケージをアンインストールする
list インストール可能なパッケージ一覧を表示する
search 指定したキーワードでパッケージを検索する
info パッケージの情報を表示する

リポジトリの追加の仕方を理解する

remiリポジトリを例に順をおって説明していきます。

  • rpmファイルをダウンロードする
  • ネットで検索したりしてremiレポジトリのrpmファイルをダウンロードします。rpmファイルのパスは頻繁に更新されるようなので注意が必要です。

    wget http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
    

    ここでrpmファイルとは。これまた(RPM Package Manager)とよばれ、Redhatにより開発されたこれまたパッケージ管理システムになります。
    yumは実際にはrpmのラッパーとして動作しており、実際にパッケージのインストールに関してはrpmにより行われます。
    rpmはパッケージをcpio形式で圧縮し、その中にspecファイル・バイナリまたはソースコードが含まれます。またパッケージを管理するためにBerkeley DBを採用していて、これによりローカルのパッケージ情報の管理がなされる。
    specファイルにはパッケージの名前や、概要、依存するパッケージ、バイナリパッケージのインストールパス、インストール前後に実行するスクリプトなどが書かれていて、これによってユーザが意識しなくてもインストールを円滑に行うことができます。

    rpmでは依存するパッケージは提供されますが、その実行はユーザに任せています。この辺りを自動的に解決するのがyumということになります。

  • rpmファイルをインストールする
  • rpm --upgrade --verbose --hash remi-release-6.rpm
    

    インストールされたかどうかを確認するためには下記のコマンドを利用します。

    # rpm -qa | grep remi-release-6
    remi-release-6.5-1.el6.remi.noarch
    

    実際にリポジトリを使用するにはenablerepoと明示的にオプションに指定することや、設定ファイルに優先順位などを記述することで可能になります。
    ここでは試しにremiリポジトリが提供しているphpに関するパッケージをかくにしてみましょう。

    # yum --enablerepo=remi list | grep php
    php.x86_64                                 5.4.32-1.el6.remi            @remi
    php-cli.x86_64                             5.4.32-1.el6.remi            @remi
    php-common.x86_64                          5.4.32-1.el6.remi            @remi
    php-mysql.x86_64                           5.4.32-1.el6.remi            @remi
    php-pdo.x86_64                             5.4.32-1.el6.remi            @remi
    ...
    

    結局内部で何をしているかを理解する(つもりだったがまたの機会に)

    大雑把なことわかったんですが、中身が気になりますので追っていきます。
    確認環境はCentOS6.6になります。

    vi /usr/bin/yum
    --
    #!/usr/bin/python
    import sys
    try:
        import yum
    except ImportError:
        print >> sys.stderr, """\
    There was a problem importing one of the Python modules
    required to run yum. The error leading to this problem was:
    
       %s
    
    Please install a package which provides this module, or
    verify that the module is installed correctly.
    
    It's possible that the above module doesn't match the
    current version of Python, which is:
    %s
    
    If you cannot solve this problem yourself, please go to
    the yum faq at:
      http://yum.baseurl.org/wiki/Faq
    
    """ % (sys.exc_value, sys.version)
        sys.exit(1)
    
    sys.path.insert(0, '/usr/share/yum-cli')
    try:
        import yummain
        yummain.user_main(sys.argv[1:], exit_code=True)
    except KeyboardInterrupt, e:
        print >> sys.stderr, "\n\nExiting on user cancel."
        sys.exit(1)
    

    python。
    ちなみにですがpythonは結構コマンドラインと親和性が高いように思っています。
    デプロイツールの一種にもpythonを拡張したfabricというライブラリがあり、コマンドラインなんかを用意に実行できます。

    そしてyumスクリプトを見ると実態はpythonのラッパーとして動作していることがわかります。

    この続きについてはまた機会があるときに追跡しようと思います。

    本日はyumの周りをざっくり理解してもらえたでしょうか。


    stat系コマンドさわり

    概要

    よくわからないけどPCが重い。アプリケーションが応答しなくなったなど今PC内部で何が起こっているのかざっくり把握したい。
    今回はそんな時に役に立つ統計情報を確認できるstat系コマンドについて解説する。

    ちなみに今回解説する内容はCentOS6.6でのものに限定し、他のディストリビューションで同じような解釈ができるかどうかは保証しません。

    vmstat

    vmstatのvmはvirtual memoryであり、仮想メモリの統計情報について表示することのできるプログラムである。

    manコマンドをかいつまんで概要を把握する。

    vmstatはプロセス、メモリ、ページング、ブロッキングIO、CPU使用率に関するレポート情報を表示する。
    最初のレポートに関しては最後に再起動された瞬間からの平均値を表示し、追加で表示されるレポートに関しては特定の時間を定めその間に抽出したデータを元に統計を表示する。
    プロセスとメモリの統計情報に関しては常にその時の状態が表示される。

    VMモードで表示される項目

  • Procs
  • r : 実行を待っているプロセスの数
    b : 割り込み不可能なスリープ状態にあるプロセスの数

  • Memory
  • swpd : 使用されている仮想メモリの総量
    free : 使用されていないメモリの総量
    buff : バッファとして使用されているメモリの総量(カネールバッファ)
    cache : キャッシュとして使用されているメモリの総量(ページキャッシュ)
    inact : 非アクティブなメモリの総量
    active : アクティブなメモリの総量

  • Swap
  • si : ディスクからスワップインされたメモリの総量
    so : ディスクへとスワップアウトされたメモリの総量

  • IO
  • bi : ブロックデバイスから取得した秒間ブロック数
    bo : ブロックデバイスへと送信した秒間ブロック数

  • System
  • in : 秒間の割り込み数
    cs : 秒間のコンテキストスイッチ数

  • CPU
  • us : user time. アプリケーションコードが消費した時間
    sy : system time. カーネルコードが消費した時間
    id : アイドル時間。linux 2.5.41以上ではio待ちも含む
    wa : io待ちの時間。linux 2.5.41以上ではidに含まれる
    st : linux2.6.11以上ではvirtual machineに消費した時間

    vmstatという名前の割にはわりかしCPUの状態からシステムのボトルネックの場所を把握することに用いられるように思う。

    iostat

    CPUの使用状況やデバイス、パーティション、NFSごとに入出力の統計情報を表示する。
    vmstatと比較するとCPUの情報が出力されるところは似たようなところがあるが、入出力に関しては複数のデバイスごとに表示が確認できるため
    より詳細なIOボトルネックを確認したい場合には有効に働きそうである。

    またちょっとmanコマンドの説明を確認する。
    デバイスごとにその平均転送レートから入出力状況についてモニタリングを行う。
    これによって物理的なディスクの入出力とバランスをとるためにシステムの設定を最適化することができる。
    iostatを実行して初めに表示される項目はシステムを再起動してからの統計情報を表示する。
    その後のそれぞれの出力に関しては前回のレポート集計後について集計を行う。

    特にvmstatから特筆して異なるような使い方はありません。

    mpstat

    これも同様にCPUの使用状況などをコア別に詳細に確認することができるようになります。
    vmstatやtopでもボトルネックが特定できなかった時などに使用すると良いと思います。

    と…

    dstat

    これ便利。ちなみにpythonで書かれている。
    iostat, vmstat両方で確認できる内容がほぼそのまま確認できます。
    しかも色付きだし、表示が見やすく整形されている。(vmstatはなんか表示崩れてて見難い)

    おまけに–top-cpuや–top-cputimeなどのオプションを使用することで、その瞬間での最もcpu使用率の高いプロセスなどが表示されます。
    エッジが立っているようなプロセスの監視を行いたいときなどは便利かも。

    まとめ

    さくっと使ってみた間隔としては
    とりあえずdstatで大雑把に見る。プロセス単位で見たい場合はやはりtopコマンドが良さそうです。


    macのtopコマンドを追う

    家に帰ると充電器に指しておいた愛器のMACがファンを最高スピードまでクロックアップさせながら唸りをあげていた。

    なぜだ。蓋閉じてるのに。

    今回はこういった漠然とした状態からコンピュータ内部でざっくり何が起こっているか判別するときに使用できるtopコマンドを掘り下げて解説しよう。

    topコマンドはosx(mac)だけでなくunix実装のほとんどのディストリビューションで提供されているツールであろう。
    システム全体をざっくり見るときによく用いられる。
    ただこれディストリビューションによって確認できる情報や、オプションとか結構変わってくるので注意。

    さてmacでterminalを立ち上げtopコマンドを打鍵すると下記のような表示が見て取れると思う。

    Processes: 226 total, 4 running, 9 stuck, 213 sleeping, 1096 threads
    Load Avg: 3.12, 3.16, 2.93  CPU usage: 16.81% user, 23.22% sys, 59.95% idle  SharedLibs: 9200K resident, 14M data, 0B linkedit.
    MemRegions: 45374 total, 1842M resident, 59M private, 1496M shared. PhysMem: 5103M used (1685M wired), 2594M unused.
    VM: 538G vsize, 1066M framework vsize, 3072256(54) swapins, 3399825(0) swapouts.  Networks: packets: 6042458/6896M in, 3543389/504M out.
    Disks: 1397033/93G read, 1239425/75G written.
    

    Load Avg: 3?何も起動してないのに。。。

    表示される項目について解説する。

    Processes – total

    マシン上で動作しているプロセスの数

    Processes – running

    実行中プロセスの数
    実行中となりうるプロセス数は1CPUにつき1プロセスだけである。
    動作しているマシンのCPUがクアッドコアなため最大で同時に4つのプロセスが動作可能

    Processes – stuck

    そもそもstuckとは?
    osxのtopコマンドにおけるstuckとはプロセスの状態がLIBTOP_STATE_STUCKとなっている状態のプロセス数である。
    またカーネルの状態としてはTH_STATE_UNINTERRUPTIBLEであることを指す。
    これはプロセスが割り込み不可能なwait状態であることを指す。
    通常はディスクやネットワークに対するI/O待ちのような状態が該当する。

    Processes – sleeping

    wait状態のプロセスの数を表す。

    Processes – threads

    スレッドの数を表す。

    Load Avg

    ロードアベレージとは実行キューの中に入っている平均ジョブ数を表す。
    (これはosxの定義であってディストリビューションによって算出方法は多少異なることもある)
    3つの数値が並んでいるがこれは左から、1分平均、5分平均、15分平均を表す。

    CPU usage – user

    ユーザ実行時間を表す。ユーザ時間とはアプリケーションレイヤでカーネル処理(システムコール)に費やされている時間以外の時間のことを指す。
    例えばアプリケーション中でシステムコールを利用している場合、その間はシステム時間として認識される。

    CPU usage – system

    システム実行時間を表す。アプリケーションやOSによりシステムコールに費やされている時間を表す。

    CPU usage – idle

    アイドル時間を表す。

    SharedLibs – resident

    メモリに常駐している共有メモリを表す。

    SharedLibs – data

    データ領域を表す。

    SharedLibs – linkedit

    MemRegions – total

    使用メモリサイズ。単位はmach virtual memory。

    MemRegions – resident

    常駐メモリのサイズ

    MemRegions – private

    非共有メモリのサイズ

    MemRegions – shared

    共有メモリのサイズ

    PhysMem – used

    使用中の物理メモリサイズ
    wiredという表記があるが、これはos kernelによって使用されていることを意味する。

    PhysMem – unused

    未使用な物理メモリサイズ

    VM – vsize

    仮想メモリの総サイズ。
    仮想メモリとは実際には存在しないが、実メモリにマップ領域を用意し、実際のメモリ上にページを配置したり(スワップイン)
    抱えきれなくなったページをディスク上に吐き出したり(スワップアウト)することでプロセスからは膨大なメモリが使用可能なように見えるようにする仕組み。
    当然実メモリよりも大幅に大きなサイズとなる。

    VM – framework vsize

    共有メモリにより諸費される仮想メモリサイズ。

    VM – swapins

    スワップインを起こしたページ数

    VM – swapouts

    スワップアウトを起こしたページ数

    Network packets

    ネットワークに対するin/outのパケットサイズを表す。

    Disks

    ディスク装置に対するread/writeのデータサイズを表す。

    本日はこんなところで。にしてもosxはドキュメントが少ないですね。
    https://apple.stackexchange.com/
    上記はstackoverflowのapple版みたいなものなんですが、そちらが一番情報量が豊富なように思います。


    linux上での時間について

    まあなんでもないことでもあるんだけれども、個人的にもメモ。

    本日はlinuxにおける、時間、についてです。

    正確に言うとlinuxシステムとして提供している時間です。これには三種類ありそれぞれ

    • 実時間
    • ユーザ時間
    • システム時間

    になります。それぞれ説明していくと。

    実時間とは実際にプログラムの実行中などに経過した時間を表します。これは現実世界の時間の経過と完全に一致します。

    またユーザ時間とはプログラムの実行中にユーザのアプリケーションが消費したCPU時間のことを示します。

    同様にシステム時間とはプログラムの実行中にシステムが消費したCPU時間のことを示します。

    通常アプリケーションの消費した時間を計測したい際にはユーザ時間+システム時間ということになると思います。

    実時間に関してはOS内部では他にもアプリケーションがいくつも走っていますからそれらを除外した時間を計測できるという意味で、実時間を対象とするケースは少ないでしょう。