いますぐ実践! Linuxシステム管理

いますぐ実践! Linux システム管理 / Vol.227 / 読者数:2186名

こんばんは、うすだです。

私事で恐縮ですが、心機一転がんばります、と言って1ヶ月経ちました。

これを機に一所懸命やるぞ! と意気込んだはずが、 やや散漫に仕事をしてしまっていまして、案の定、 山積み状態になってまいりました。
(別にサボっているわけではない…ハズなのですが…。)

下記の記事を読んで、半分は当てはまるな…なんて思っていたのですが、 いままでやっていたことを中断してこれを読んでいる時点で、 すでに自分は集中していないということに気づいてしまい、愕然としております。

集中力のない人 6の特徴 | ニコニコニュース
http://news.nicovideo.jp/watch/nw349610

また、自分に集中力がないことは百も承知なので、 そこから抜け出す方法を教えてくれよ…と思ったりもしました。

いちおう、過去に、何度か試してみたりもしたのですが、そもそも集中力がないため、 長続きした試しがありません。

なんだか、鶏卵問題みたいになってしまっていますが、それでも試行錯誤を繰り返して、 いまの自分にあった方法を見つけるしかないように思っております。

というわけで、方法を模索している間は一所懸命やっているのだ、 と自分に言い聞かせながら、今回もはりきってまいりたいと存じます。

今回のお題 - コマンドが実行されるまでを追っかける

システム管理者も、サーバで作業するユーザさんも、普段、 特に何も意識しないでコマンド等を実行しているのではないかと思います。

ですが、実はシェルやPerlなどのスクリプトだったり、 由緒正しき?何かのバイナリだったりと、いくつかのフォーマットが存在するようです。

どのフォーマットを使っていて、それぞれどうやって実行すればいいのかというのを、 誰かがよきように判断しているように思われます。
しかし、いったい、だれがやっているのでしょうか? (シラジラしく…)

というわけで今回は、いつもとはやや趣向を変えて、 コマンドがどのように実行されるのかを、少し探ってみたいと思います。

カーネルがお膳立てします

タイトルでバレバレ…と言いますか、100人いたら102人がそう思っていると思いますが、 お膳立てをしているのは、Linuxカーネルさんです。

カーネルは、 ハードディスクなどに格納されている実行ファイルをメモリにロードして実行する際、 ファイルの先頭を盗み見てフォーマットが何か判断し、 それぞれのフォーマットに見合った処理をします。

カーネルソースの include/linux/binfmts.h には、 linux_binfmt構造体というものが定義されており、 これがインターフェースになっています。
前述の「フォーマットが何か判断」や「見合った処理」というのは、 実際にはlinux_binfmt構造体のメンバーである関数が行います。

それから、実行ファイルの中身と、メモリにロードされて実行されるモノは、 まったく同じではありません。
メモリ上には、処理内容(命令)が書かれた「テキスト」と、 ローカル変数を除くデータを格納するための「データ」、 関数の呼び出しの際に状態を保存するためなどで使う「スタック」が用意されてから、 実行されます。
実行ファイルには、テキストとデータにあらかじめロードされるべき情報が入っていて、 linux_binfmt構造体のメンバーである関数が、 よきようにロードして実行できる状態にまで仕立て上げてくださいます。

ちなみに、対応しているフォーマットは、ELF、スクリプト、a.out、FLATなどです。 具体的には、カーネルソースの fs/*binfmt*.c がそうです。
(ちなみに、a.out は昔使われていた(いる)もの、 FLAT はuClinuxなどMMUのないCPUのLinuxで使われているものです。)

もちろん、fs/binfmt_*.c を参考にして、 linux_binfmt構造体のメンバー関数を実装して登録すれば、 独自のフォーマットを実行できるようにすることもできます。

基本は ELF

スクリプトでない実行ファイルの、おそらく99.9999%(個人の想像です)が 「ELF(Executable and Linkable Format)」というフォーマットでできています。

ELFは、テキストなどにロードされる情報が、 「セクション」と呼ばれるところに格納されています。セクションは大抵いくつかあり、 それぞれ、実行ファイル内の位置とサイズ、 メモリ上に配置されるべき位置とサイズなどが記録されています。

実行ファイルの中を確認するには、「objdump」コマンドを使用します。 「binutils」パッケージに入っていますので、もし見当たらない場合は、 yumやaptなどでインストールしてください。

で、「-x」オプションをつけてobjdumpを実行すると、 セクションなどの情報を出力してくれます。 (厳密には、すべてのヘッダの情報を出力するオプションです。)

  $ objdump -x /bin/ls

  /bin/ls:     file format elf32-i386
  /bin/ls
  architecture: i386, flags 0x00000112:
  EXEC_P, HAS_SYMS, D_PAGED
  start address 0x0804be34

  Program Header:
      PHDR off    0x00000034 vaddr 0x08048034 paddr 0x08048034 align 2**2
           filesz 0x00000120 memsz 0x00000120 flags r-x
    INTERP off    0x00000154 vaddr 0x08048154 paddr 0x08048154 align 2**0
           filesz 0x00000013 memsz 0x00000013 flags r--
  ...中略...

  Sections:
  Idx Name          Size      VMA       LMA       File off  Algn
    0 .interp       00000013  08048154  08048154  00000154  2**0
                    CONTENTS, ALLOC, LOAD, READONLY, DATA
  ...後略...

最初に、対応するアーキテクチャなどの情報、 メモリ上のどこに配置するかなどの情報があり、 そのうち「Sections:」で始まる情報がずらずらっと出力されています。 これがセクションの情報です。
ここで「CODE」と書かれているセクションはテキストに、 「DATA」とあるセクションはデータにロードされます。

 

ところで、実は、ELFな実行ファイルは、自力では実行できません。
大抵の場合、「共有ライブラリ」という、 共通で使いまわせるライブラリを使うようになっており、 これらを使えるようにするお膳立てが、さらに必要となります。

そして、これはカーネルさんではなく、「動的リンカ」というひとがやります。 具体的には、「.interp」セクションに書かれています。
「-j」オプションで.interpセクションを指定して、 「-s」オプションでそのセクションの中身を出力させてみましょう。

  $ objdump -j .interp -s /bin/ls
  ...中略...
  Contents of section .interp:
   8048154 2f6c6962 2f6c642d 6c696e75 782e736f  /lib/ld-linux.so
   8048164 2e3200                               .2.             

この環境(Ubuntu12.04 i686)では、 「/lib/ld-linux.so.2」が動的リンカのようです。
ですので、実際には、カーネルがこの動的リンカを実行して、 ELFな実行ファイルを間接的に実行しています。

たとえば、/bin/ls を実行しようとしたとき、 実際には以下のように実行されていることになります。

  $ /lib/ld-linux.so.2 /bin/ls
  examples.desktop  テンプレート   ドキュメント  音楽   公開
  ダウンロード      デスクトップ   ビデオ        画像

スクリプトの実行

スクリプトの場合は、ファイルの先頭が「#!」で始まっていて、 その後に処理してくれる実行ファイル(インタープリタと言います)を指定します。

たとえば、「/bin/zcat」は、bash(/bin/bash)のスクリプトです。

  $ cat /bin/zcat
  #!/bin/bash
  # Uncompress files to standard output.
  ...後略...

ですので、たとえば、zcatコマンドを以下のように実行した場合、

  $ /bin/zcat /var/log/dpkg.log.2.gz

実際には以下のように実行されています。

  $ /bin/bash /bin/zcat /var/log/dpkg.log.2.gz

さらに、/bin/bash は共有ライブラリを使用しますので、 実際には以下のように実行されています。
(64bit な環境の場合、動的リンカのパスは、以下とは異なります。)

  $ /lib/ld-linux.so.2 /bin/bash /bin/zcat /var/log/dpkg.log.2.gz

…ふぅ。(ぜひ、実行してみてくださいね。)

 

もちろん、自分で作ったインタープリタを指定できます。
たとえば、以下のシェルスクリプトを、/home/usu/interp.sh というパスで作成します。

#!/bin/sh
echo $*

そして、上記をインタープリタとするスクリプトを書きます。
(仮に、interptest.detarame というファイル名にしておきます。)

#!/home/usu/interp.sh
detarame-

2行目以降はてきとーで構いません。
で、双方とも実行可能な状態にして、

  $ chmod +x /home/usu/interp.sh interptest.detarame

後者を、適当な引数をつけて実行してみましょう。

  $ ./interptest.detarame hikisuu1 hikisuu2
  ./interptest.detarame hikisuu1 hikisuu2

インタープリタが/home/usu/interp.sh なので、 interp.sh が実行されて上記のような出力が得られました。
くどいですが、以下のように実行しているのと同じです…よね。

  $ /home/usu/interp.sh ./interptest.detarame hikisuu1 hikisuu2

自分なりのナニかも実行できます

スクリプトの場合、「#!」を目印としてインタープリタが実行されます。 ですが、binfmt_misc というカーネルモジュールを使うと、 実行ファイルの中身がある形式か、もしくはあるサフィックス(拡張子)のときに、 指定したインタープリタをカーネルに起動してもらうことができます。

まずは登録です。/proc/sys/fs/binfmt_misc/register に、 以下の形式の文字列を送り込みます。(rootの権限が必要です。)

  :名前:型:オフセット:マジック:マスク:インタープリタ:フラグ

「名前」には、登録したものを識別するための名前を指定します。
「型」には、ファイルの中身で判別するなら「M」、 サフィックスで判別するなら「E」を指定します。
「オフセット」には、ファイルの中身で判別する際、 ファイルの先頭からの位置を指定します。省略すると先頭(つまり 0)になります。
「マジック」には、比較するデータかもしくはサフィックス(. は含まず)を指定します。 「\xcc」のようにアスキーコードを16進数で指定することもできます。
「マスク」には、比較する際のデータのビットを指定したいとき、マスクを指定します。 省略すると全部比較します。
「インタープリタ」には、起動してほしいインタープリタを指定します。 「フラグ」には、「P」「O」「C」が指定できますが、 基本的には指定の必要はない…と思います。たとえば P を指定すると、引数に、 自分自身(インタープリタじゃない方)が1つ増えます。

ちなみに、最初の「:」がデリミタ(区切り文字)になります。ですので、 : 以外の文字でも処理してもらえます。

登録されると、「/proc/sys/fs/binfmt_misc/名前」が作成されます。

たとえば、 サフィックスが html のファイルを直接実行すると Chromium を起動するようにするには、 以下のように登録します。

  # echo ":html:E::html::/usr/bin/chromium-browser:" > \
  /proc/sys/fs/binfmt_misc/register 

問題なければ、/proc/sys/fs/binfmt_misc/html が現れます。
中身を見ることで、登録内容を確認できます。

  $ ls /proc/sys/fs/binfmt_misc/
  html   register   status
  $ cat /proc/sys/fs/binfmt_misc/html
  enabled
  interpreter /usr/bin/chromium-browser
  flags: 
  extension .html

では実際に試してみます。

  $ echo test >l test.html
  $ chmod +x test.html
  $ ./test.html

とすると、Chromium が起動するか、すでに起動していれば新たに表示がされます。

また、ファイルの先頭が「%PDF-」だと Adobe Reader を起動するには、 以下のように登録します。(デリミタをわざと変えてみました。)

  # echo "^pdf^M^^%PDF-^^/usr/bin/acroread^" > \
  /proc/sys/fs/binfmt_misc/register 
  # cat /proc/sys/fs/binfmt_misc/pdf
  enabled
  interpreter /usr/bin/acroread
  flags: 
  offset 0
  magic 255044462d

試しに、PDFファイルに実行権をつけて直接実行すると、 Adobe Readerが自動起動されます。

  # echo -1 > /proc/sys/fs/binfmt_misc/名前

また、「0」を書き込むと、エントリ自体は消えませんが、 起動してくれなくなります(無効になります)。「1」を書き込むと、有効になります。

  # echo 0 > /proc/sys/fs/binfmt_misc/名前   (無効になる)
  # echo 1 > /proc/sys/fs/binfmt_misc/名前   (有効になる)

おわりに

以上、実行ファイルが実行されるまでを追いかけてみました。

…だからなに? とか、システム管理と直接関係あるの? と疑問に思われたかもしれません。ですが、裏でこういう処理が行われているのだ、 ということを知っておくと、トラブル時に何かヒントになったり、 何も知らない新人さんにえらそーなことが言えたりします。前向きに考えましょう。

また、こうなっているよというだけでなく、確認するための例も極力記載しましたので、 ぜひ実行してみてください。

宿題の答え

前回の宿題は、

  dhcp-optionに指定できる名前を調べましょう。

でした。

man を見ればもうおわかりかと思いますが、 「--help dhcp」オプションをつけて実行すると、一覧が出力されます。

  $ /usr/sbin/dnsmasq --help dhcp
    1 netmask
    2 time-offset
    3 router
  ...中略...
  125 vendor-id-encap
  255 server-ip-address

…が、もし万が一、man に記述がない場合、どうすればいいでしょうか。

ここでは、dnsmasq さんが親切じゃないと仮定して、強引に調べる方法をご紹介します。

一番安直で確実な方法は、 「-?」や「--help」といったオプションを指定して実行してみることです。では、 ためしに -? をつけてみましょう。

  $ /usr/sbin/dnsmasq -\?
  dnsmasq: bad command line options: try --help

ご丁寧に、--help を試せとご教示いただけました。

  $ /usr/sbin/dnsmasq --help
  Usage: dnsmasq [options]

  Valid options are:
  ...この後大量のオプションの説明が...

ヘルプが出力されたら、該当するオプションを探せばよいですよね。
(…ああ、dnsmasq さんはほんとうに親切な方ですね…。)

さて、上記でも手がかりが得られない…となりますと、 実行ファイルから文字列を抜き出して推測する、という荒技もございます。
以下のように、「strings」コマンドで抜き出せます。

  $ strings /usr/sbin/dnsmasq
  /lib/ld-linux.so.2
  \>:|
  4Btu
  libdbus-1.so.3
  __gmon_start__
  ...この後大量の文字列が...

とはいえ、たくさんありすぎて、よくわからないですね…。
幸い、/etc/dnsmasq.conf のコメントには、 「router」「ntp-server」や「domain-search」といったサンプルが記述されています。 これらの名前が番号順に並んでいる可能性が高そうですので、 router で探りを入れてみましょう。

  $ strings /usr/sbin/dnsmasq | less
  (…と実行して、/router(リターン!) と打ち込みます)

すると、以下のように並んでいる箇所があります。

  ...前略...
  time-offset
  router
  dns-server
  log-server
  lpr-server
  ...後略...

ありがたいことに、上記の文字列の並びとDHCPのオプション番号が、 それとなく一致しています。後は、番号とその意味から名前を推測し、 この辺にある名前を拝借すれば使えそうです。

それでもわからない場合は、もう、ソースコードを見ます。
もっとも確実で、面倒くさい方法ですが、処理の内容などもわかって勉強にはなります。 たとえば Ubuntu など Debian 系をお使いの貴兄は、

  $ apt-get source dnsmasq

と実行すると、以下のように、 ソースコードなどがカレントディレクトリに展開されます。
(RedHat系な貴兄は、--sourceオプション付きでyumdownloaderコマンドを実行すると、 ソースRPMが得られますので、後は yum install などしてください。 (おっと、ソースのリポジトリの追加も必要です。))

  dnsmasq-2.59/            dnsmasq_2.59-4.dsc
  dnsmasq_2.59-4.diff.gz   dnsmasq_2.59.orig.tar.gz

dnsmasq-2.59/src/option.c を見ると、以下のような箇所があります。

  static const struct {
    char *name;
    unsigned char val, size;
  } opttab[] = {
    { "netmask", 1, OT_ADDR_LIST },
    { "time-offset", 2, 4 },
    { "router", 3, OT_ADDR_LIST  },
  ...後略...

この opttab[] という配列は、各所でそれっぽい用途に使われています。
(option_string, display_opts, parse_dhcp_opt などの関数内です。)

ですので、ここの name にあたる名前を拝借すれば、間違いないだろうということが、 推測できます。

…とまあ、ここまで追いかけなくても、Google様などに聞いてしまった方が、 おそらく早く解決するのだと思いますが…。
(脳みそは、使った分だけ賢くなる、ということで…使ったのかな…。)

今回の宿題

今回の宿題は、

  スクリプトで自身をインタープリタに指定するとどうなるでしょうか?

です。

カーネルさんの処理を想像してから、実際に試してみてください。

少なくとも、無限に呼び出されて返ってこなくなってしまう、 ということはないですので、安心してお試しください。(^ε^;

あとがき

息子にプログラミングを教えたいと思いはじめて、もう何年も時が経ってしまいました。

最初の頃は息子も小学生で、いきなりコードを書くのは無理かもしれないと思い、 Squeak あたりを考えておりました。

ようこそ、スクウィークランドへ!
http://squeakland.jp/

ですが、まずはこっちが覚えたりとか、 使える環境を揃えねばとか考えているうちに時は過ぎ、うやむやになってしまいました。

じゃあ、DSで動けばいいんじゃないかと思って、 500円払ってプチコンをインストールしてみたのですが、 コードの入力が超絶面倒くさく、 ゲームの100歩手前くらいのものを作ったところで断念しました。

プチコン
http://smileboom.com/special/petitcom/

…と思って今見たら、mkII が出てるんですね。セーブできるのか…。

プチコンmkII
http://smileboom.com/special/ptcm2/

そして、最近になってようやく、本人の口から、 プログラム作ってみたいという言葉が発せられるようになりました。

もう高校生なので、なんでもいけるんじゃないかと思うのですが、 やはりプラットフォームにあまり依存せず、 最初のうちからビジュアルな結果が得られるものがいいんじゃないかな、 ということで、JavaScript を検討しております。

問題は、JavaScript は意外と深い(たいへん)かもしれないことと、 これっていう初心者本あるいはサイトが見当たらないことです。

というわけで、この本が高校生でもいけるんじゃないかとか、 この言語の方がいいよとか、ご意見がございましたら、 忌憚なくお寄せいただけますとたいへん幸いです。

 

あ、それから、すみませんが宣伝です。

次号の某日経Linuxの特集が、いろいろな言語の入門となっているのですが、 シェルスクリプトを担当させていただきました。
例によって栗々しておりますので、連載と合わせて立ち読んでみて、 よいと思われましたら、迷わずご購入いただけますと幸いです。:-)

日経Linux - 本誌次号予告 - 2012年10月号
http://itpro.nikkeibp.co.jp/article/MAG/20120806/414425/

 

…あ、そうか、これ読ませればいいんだ! (自己解決!)
…い、いえ、ご意見は猛烈にお待ちしております。

 

今回も、ここまで読んでいただき、たいへんありがとうございました。
次回は、9月16日(日) の未明にお会いしましょう!

 

「いますぐ実践! Linux システム管理」の解除は、以下からできます。
http://www.usupi.org/sysad/ (まぐまぐ ID:149633)

バックナンバーは、こちらにほぼ全部そろっています。
http://www.usupi.org/sysad/backno.html

「栗日記」- TwitterとFacebookはほぼコレ専用です。
http://www.usupi.org/kuri/ (まぐまぐ ID:126454)
http://usupi.seesaa.net/ (栗日記ブログ)
http://usupi.org/k/ (モバイル栗日記)
http://twitter.com/kuriking/ (栗つぶやき)
http://facebook.com/kuriking3 (栗顔本)


[バックナンバーのトップへ] [Linux システム管理のトップへ]

トップ

バックナンバー
    [日付順] [目的別]

プロフィール

▼ リンク

独学Linux
Linuxデスクトップ環境に関する情報が満載です。 メルマガもありますよ。
Server World
CentOS 6をサーバとしたときの設定例が、これでもかというくらいたくさん載っています。 CentOS以外のディストリビューション(Fedora, Ubuntu)も充実しています。
LINUXで自宅サーバーを構築・導入(Fedora9)
Fedora9のインストールの仕方から管理方法まで、詳しく載っています。 SearchManには情報がもりだくさんです。
マロンくん.NET
〜サーバ管理者への道〜
Linuxをサーバとして使用するための、いろいろな設定方法が載っています。 マロンくんもかわいいです。 なんといっても、マロンくんという名前がいいですね!!
日経Linux
今や数少なくなってしまったLinuxの雑誌。ニュースやガイドもあります。
Linux Square − @IT
@ITが提供する、Linux の情報が満載。 載っていない設定方法はないんじゃないでしょうか。
gihyo.jp…技術評論社
Linuxに限らず様々な技術情報が満載のサイト。 SoftwareDesign誌も、 ソフトウェア技術者は必見です。
SourceForge.JP Magazine
Linux に限らず、オープンソース関連の記事が網羅されています。
ITmediaエンタープライズ:Linux Tips 一覧
Tips というより FAQ 集でしょうか。わからないことがあれば覗きましょう。
IBM developerWorks : Linux
開発者向けですが、勉強になりますよ。
Yahoo!ニュース - Linux
Yahoo!のLinuxに関するニュース一覧です。
栗日記
システム管理とかと全然関係ありませんが、毎日栗の絵を描いています。
システム管理につかれちゃったとき、癒されたいときに、ご覧ください。:-)
WEB RANKING - PC関連
ランキングに参加してみました。押してやってください。

▼ 作ってみました

Add to Google

▼ せんでん




▼ 最近読んだ本

▼ 気に入ってる本