2011年12月24日土曜日

Fedora 16 で久々に Xen を試してみる

KVM の登場で、ほとんど触ることが無くなっていた Xen ですが、Linux 3.0 以降で Dom0 動作可能になっており、Fedora 16 なら簡単に Xen を動かせるので、試してみました。

■準備
yum install xen を実行して、Xen 関連のパッケージを導入します。grub2 のメニューは、自動で追加されるので、特に何もすることはなく、導入は簡単でした。再起動して、
Linux, with Xen 4.1 and Linux 3.1.2-1.fc16.x86_64
このように with Xen 4.1 がついたメニューを選択して起動すれば OK でした。

■実験内容
ホストには Fedora 16 x86_64 、ゲストには CentOS 6.2 x86_64 を使い、Xen 準仮想化、Xen 完全仮想化、KVM の3つの場合で、パフォーマンスがどの程度違うものかを比較。ハードは、ThinkPad X301 (Core2 Duo U9400 1.40GHz) です。
個人設備の関係上、ディスク性能が貧弱なため、ベンチマークには姫野ベンチを使いました。
なお、CentOS 6 では、DomU 用カーネルというのは無いので、カーネルの種類を切り替えるようなことはしなくて済みます。CentOS 5 の場合は、準仮想化で動くには xen カーネルを使う必要がありましたし、CentOS 4 では xenU カーネルを使う必要がありました。

■実験結果
単位MFLOPS
 84.511594  Xen 完全仮想化 ゲスト (CentOS 6.2 x86_64)
 86.336558  Xen 準仮想化 ゲスト   (CentOS 6.2 x86_64)
 96.247089  Xen Dom0   (Fedora16 x86_64、ゲストとは OS 環境が異なります)
106.986019  KVM ゲスト (CentOS 6.2 x86_64)
101.272400  KVM ホスト (Fedora16 x86_64、ゲストとは OS 環境が異なります)
姫野ベンチの結果のみですが、KVM のほうがパフォーマンス (CPU, メモリ周り) が良いという結果になりました。

■所感
ベンチを実行する前の段階で、体感上 Xen のほうがもっさりしていると感じました。普段、ネイティブ環境 (KVM ホスト側) で作業して、たまに仮想マシンを使うという私の利用形態だと、わざわざ性能劣化した Dom0 で通常作業はしたくないですし、Xen のメリットは全く無いという感想を持ちました。また個人的に Xen で気になる点として、Dom0 上で標準の性能ツール (sar 等) を採取しても、Xen ゲストの性能データを得られない点が挙げられます。KVM であれば、ホスト側の sar には、ゲストの値も計上されます。また、KVM ゲストは、KVM ホストからはプロセスに見えるため、top コマンドでも、KVM ゲスト毎の負荷状況を簡単にモニターできます。

■個人的結論
現時点で、Xen を使うメリットは全くないと感じました。Hyper-V のハイパーバイザー部分は Xen と共有しているそうですし、Oracle VM も Xen ベースだとのこと。それらがいくらチューニングしようとも、KVM よりもレイヤーが厚い分だけ、性能面で不利なのではないかと思われます。

2012-02-19追記
古い VT(CPUの仮想化支援機能)非対応のPC(P4 3.6G)を再利用しようとして、そんな時には Xen が役立つと再認識しました。Fedora 16 で (KVM無しの) QEMU を試しましたが、実用的に使うには無理がありました。QEMU では、ゲストが起動するのに 30 分かかるのが、Xen であれば許せる時間内(数分)でゲスト起動できました。

2011年12月18日日曜日

ksh で扱える整数の上限

bash と tcsh について、扱える整数の上限を調べましたが、さらに ksh についても調べてみました。
#!/bin/ksh93

function test_int_limit {
        typeset i=2147483646
        echo -e "INT_MAX-1 = "$i
        let i++
        echo -e "INT_MAX   = "$i
        let i++
        echo -e "INT_MAX+1 = "$i
echo
        i=4294967294
        echo -e "ULONG_MAX-1 = "$i
        let i++
        echo -e "ULONG_MAX   = "$i
        let i++
        echo -e "ULONG_MAX+1 = "$i
echo
        i=999999999999999998
        echo "i = "$i
        let i++
        echo "i+1 = "$i
        let i++
        echo "i+2 = "$i
echo
        i=9223372036854775806
        echo -e "LLONG_MAX-1 = "$i
        let i++
        echo -e "LLONG_MAX   = "$i
        let i++
        echo -e "LLONG_MAX+1 = "$i
echo
}

test_int_limit
ksh について、何点か補足メモ、、、

(1) 関数定義する場合、function キーワードを使用して、関数名の後ろに () はつけません。
つけると構文エラーになります。
(2) ローカル変数を使うには、typeset を指定します。
bash にも typeset がありますが、あちらは、Obsolute (廃止) とされています。
(3) 関数定義は、(1) 以外にも旧来の 関数名() { ... } という記載方法もある。
しかし、その場合に typeset によるローカル変数指定が機能しません。
なので、ksh の場合は (1) の書き方を使いましょう。

テストスクリプトを実行した結果が、下記です。
# uname -a                                                                                                                                                                      
Linux my41 2.6.18-274.12.1.el5 #1 SMP Tue Nov 29 13:37:46 EST 2011 x86_64 x86_64 x86_64 GNU/Linux
# ksh93 --version                                                                                                                                                               
  version         sh (AT&T Research) 93t+ 2010-02-02
# ./test_int_limit.ksh93 
INT_MAX-1 = 2147483646
INT_MAX   = 2147483647
INT_MAX+1 = 2147483648

ULONG_MAX-1 = 4294967294
ULONG_MAX   = 4294967295
ULONG_MAX+1 = 4294967296

i = 999999999999999998
i+1 = 999999999999999999
i+2 = 1e+18

LLONG_MAX-1 = 9223372036854775806
LLONG_MAX   = 9.22337203685477581e+18
LLONG_MAX+1 = 9.22337203685477581e+18
このように、18桁までは整数、19桁以上は浮動小数点数として扱ってくれるようです。
そうです。ksh は浮動小数点数を扱えます。一方、bash では浮動小数点数を扱えません。
ごく稀に、bash でも浮動小数点数を使いたい場合があり、やむなく gawk を呼び出したりしますが、ksh ならそのようなことをしなくても済むようです。
なお、ksh の typeset には、-i オプション (整数型指定) がありますが、これを指定すると内部的に int 扱いになるようで、INT_MAX までしか格納できなくなります。

bash と tcsh の扱える整数の上限については、次の記事参照。
bash で扱える整数の上限
tcsh で扱える整数の上限

tcsh で扱える整数の上限

以前、bash で扱える整数の上限 という記事を書きましたが、tcsh についても調べてみました。
#!/bin/tcsh

@ i=2147483646
echo "INT_MAX-1 = "$i
@ i++
echo "INT_MAX   = "$i
@ i++
echo "INT_MAX+1 = "$i
echo ""
@ i=4294967294
echo "ULONG_MAX-1 = "$i
@ i++
echo "ULONG_MAX   = "$i
@ i++
echo "ULONG_MAX+1 = "$i
echo ""
@ i=9223372036854775806
echo "LLONG_MAX-1 = "$i
@ i++
echo "LLONG_MAX   = "$i
@ i++
echo "LLONG_MAX+1 = "$i
echo ""
実行結果は次の通りです。
# tcsh --version                                                                                                                                                                
tcsh 6.17.00 (Astron) 2009-07-10 (x86_64-unknown-linux) options wide,nls,dl,al,kan,rh,color,filec
# ./test_int_limit.tcsh                                                                                                                                                         
INT_MAX-1 = 2147483646
INT_MAX   = 2147483647
INT_MAX+1 = -2147483648

ULONG_MAX-1 = -2
ULONG_MAX   = -1
ULONG_MAX+1 = 0

LLONG_MAX-1 = -2
LLONG_MAX   = -1
LLONG_MAX+1 = 0
このように、現在の tcsh では INT_MAX までしか扱え無いようです (注:実験は、64bit環境で行ってます) 。もし、仕事で csh を指定されているなど、どうしても csh 系でスクリプトを書くという方は、お気をつけて。
なお、あちこちで言われているように、csh 系でスクリプトを書くのはやめたほうが良いだろうと思います。最大の難点は、関数定義できない点だと思います。

参考URL:
有害な csh プログラミング
結論: csh はプログラミングにはまったく向かないツールであり、
そのような目的に使うことは厳しく禁じられるべきである!
8. まとめ ... そもそもが欠陥品なのです。

2011年12月17日土曜日

bash で外部コマンドを使わずに sleep する方法

bash には、いくつかのビルトインコマンド (test や printf 等) があり、それらを用いた場合は、外部コマンドの起動 (fork & exec) を節約できます。一般に、fork & exec は重いので、できればもうちょっとビルトインを増やしてくれるといいのにと思うことがしばしばあります。もちろん肥大化しないため、最小限となっているものとは思うのですが・・・

この記事では、外部コマンドの sleep を呼び出すことなく、bash スクリプトを秒単位でスリープさせる方法を示します。本日、ふと思いつきました。

bash のビルトインの read には、入力待ちをタイムアウトさせるための -t オプションが備わっています。これを使えば sleep の代用にできるはずです。ただし、絶対に入力が発生しない何かを read して、かつ、ブロックさせなくてはいけません。もし、C であれば、pipe() システムコールを呼んで、作成されたパイプを read すれば良いと思いますが、bash の構文で pipe() 相当をやる方法はありません。そこで、mkfifo で名前つきパイプ (FIFO) を作成することにしました。

次が実験に使ったスクリプトです。
#!/bin/bash
#
# Name: test_my_builtin_sleep.bash
#

setup_my_sleep() {
        local    sleep_fifo=/tmp/.sleep_fifo.$$.$RANDOM$RANDOM
        mkfifo  $sleep_fifo
        exec 9<>$sleep_fifo
        rm -f   $sleep_fifo
}

setup_my_sleep

my_sleep() {
        read -t $1 0<&9
}

time {
for ((i=0;i<20;i++))
do
        sleep 1
        read < /proc/uptime
        echo $REPLY
done
}

echo

time {
for ((i=0;i<20;i++))
do
        my_sleep 1
        read < /proc/uptime
        echo $REPLY
done
}
exec 9<>$sleep_fifo という書き方で、fd 9 を使って FIFO を rw モードでオープンしています。また、直後に FIFO のパスを rm していますが、オープン中なのでスクリプトが終わるまでは、FIFO を利用できます。

次のような実行結果になりました。
ThinkPad X301 Core2 1.40GHz 上の CentOS 5 x86_64 で実行しています。
# dmidecode | grep Version                                                                                                                                                      
        Version: 6EET54WW (3.14 )
        Version: ThinkPad X301
        Version: Not Available
        Version: Not Available
        Version: Intel(R) Core(TM)2 Duo CPU     U9400  @ 1.40GHz
        SBDS Version: 03.01
# cat /etc/redhat-release                                                                                                                                                       
CentOS release 5.7 (Final)
# uname -a
Linux my41 2.6.18-274.12.1.el5xen #1 SMP Tue Nov 29 14:18:21 EST 2011 x86_64 x86_64 x86_64 GNU/Linux
# ./test_my_builtin_sleep.bash 
4066.52 3937.55
4067.53 3938.46
4068.54 3939.47
4069.55 3940.45
4070.55 3941.45
4071.56 3942.46
4072.57 3943.47
4073.58 3944.47
4074.59 3945.47
4075.59 3946.48
4076.60 3947.48
4077.61 3948.49
4078.62 3949.49
4079.63 3950.50
4080.63 3951.50
4081.64 3952.49
4082.65 3953.50
4083.66 3954.50
4084.67 3955.51
4085.68 3956.51

real    0m20.161s
user    0m0.012s
sys     0m0.040s

4086.68 3957.52
4087.68 3958.52
4088.69 3959.53
4089.69 3960.53
4090.69 3961.53
4091.70 3962.53
4092.70 3963.54
4093.71 3964.54
4094.71 3965.55
4095.72 3966.55
4096.72 3967.55
4097.72 3968.56
4098.73 3969.56
4099.73 3970.57
4100.74 3971.57
4101.74 3972.58
4102.74 3973.58
4103.75 3974.58
4104.75 3975.59
4105.76 3976.59

real    0m20.079s
user    0m0.000s
sys     0m0.000s
前半は外部コマンドの sleep を使っており、後半は read -t & FIFO 方式です。なお、時間の経過が読み取り易いように、/proc/uptime を表示しています。
外部コマンドを用いた場合は、user+sys で 52 ミリ秒のオーバーヘッドが見えているのに対して、read -t & FIFO 方式では、ゼロ (オーバーヘッドが1ミリ秒未満) に見えています。期待通りです。

システムの負荷が高いと、fork & exec が遅延する場合があるので、実験で用いたスクリプトのように小刻みに sleep する処理は、影響を受け易いです。そんな場面に遭遇したら、read -t & FIFO 方式を試してみては。
それだったら、無理に bash で書かなくてもいいんじゃないのという声もありそうですが。
そりゃその通りで、適材適所で perl に Ruby に Python に・・・豊かな時代ですね。

2014-05-11追記
RHEL7.0 RC を試していたのですが、最近は read -t で少数(0.5秒とか)を指定できるようです。sleep も、いつの間にか。
[root@hoge ~]# uname -a
Linux hoge 3.10.0-121.el7.x86_64 #1 SMP Tue Apr 8 10:48:19 EDT 2014 x86_64 x86_64 x86_64 GNU/Linux
[root@hoge ~]# rpm -q bash
bash-4.2.45-5.el7.x86_64
[root@hoge ~]# time sleep 0.5

real    0m0.501s
user    0m0.001s
sys     0m0.000s
[root@hoge ~]# time read -t 0.5

real    0m0.500s
user    0m0.000s
sys     0m0.000s
というわけで、read -t + FIFO でミリ秒単位の sleep も出来そうです。ご入用の方は、どうぞ。
なお、mkfifo を使わないことが可能なのかどうかは、未だに分からずです。bash のビルトインを自作すれば可能でしょうけども。

2011年11月27日日曜日

mawk は確かに速い(2倍くらい)

AWK Users JP の次の記事を読んで、手持ちのよく使う awk スクリプトを mawk で実行してみたところ、確かに速かったです。軒並み2倍速かった。

awk のパフォーマンス

試しは、Fedora 16 上で行いましたが、常用している CentOS 5 (or RHEL5) でも簡単に利用できるかどうか EPEL を見てみたら、有難いことに提供されてました。
というわけで、まず、インストールを。
[root@centos5 ~]# uname -a
Linux centos5 2.6.18-274.7.1.el5 #1 SMP Thu Oct 20 16:21:01 EDT 2011 x86_64 x86_64 x86_64 GNU/Linux
[root@centos5 ~]# yum install mawk --enablerepo=epel
Loaded plugins: downloadonly, fastestmirror
Loading mirror speeds from cached hostfile
 * base: www.ftp.ne.jp
 * epel: ftp.jaist.ac.jp
 * extras: www.ftp.ne.jp
 * updates: www.ftp.ne.jp
Setting up Install Process
Resolving Dependencies
--> Running transaction check
---> Package mawk.x86_64 0:1.3.4-5.20100625.el5 set to be updated
--> Finished Dependency Resolution

Dependencies Resolved

==========================================================================================
 Package         Arch              Version                          Repository       Size
==========================================================================================
Installing:
 mawk            x86_64            1.3.4-5.20100625.el5             epel            104 k

Transaction Summary
==========================================================================================
Install       1 Package(s)
Upgrade       0 Package(s)

Total download size: 104 k
Is this ok [y/N]: y
Downloading Packages:
mawk-1.3.4-5.20100625.el5.x86_64.rpm                               | 104 kB     00:00     
Running rpm_check_debug
Running Transaction Test
Finished Transaction Test
Transaction Test Succeeded
Running Transaction
  Installing     : mawk                                                               1/1 

Installed:
  mawk.x86_64 0:1.3.4-5.20100625.el5                                                      

Complete!
[root@centos5 ~]# 

次のテスト用の awk スクリプトは、a から zzzz までの文字列 (Linux の SCSI ディスク名の sdXX の XX 部分に使われている) を出力するものです。適度に重いかと。。
#
# Note: This awk script prints
#       a, b, ... z, aa, ab, ... zz, aaa, aab, ... zzz
#
function get_sdstr(n,   r,ret) {
        for (;;) {
                if (n <= 26) {
                        return letter[n]""ret

                } else {
                        r = n % 26 ; r = r ? r : 26
                        ret = letter[r]""ret
                        n = (n-r)/26
                }
        }
}

BEGIN {
        for (i=1; i <= 26; i++) {
                letter[i] = substr("abcdefghijklmnopqrstuvwxyz",i,1)
        }

        for (i=1; i <= (26+26*26+26*26*26+26*26*26*26); i++) {
                print get_sdstr(i)
        }
        exit(0)
}
これを、gawk と mawk でそれぞれ実行して、/usr/bin/time でパフォーマンス測定した結果が、次の通りです。
[root@centos5 ~]# md5sum test.awk 
ee228358d1513e48e0495e1e79f3956c  test.awk
[root@centos5 ~]# rpm -q gawk
gawk-3.1.5-14.el5
[root@centos5 ~]# rpm -q mawk
mawk-1.3.4-5.20100625.el5
[root@centos5 ~]# 
[root@centos5 ~]# /usr/bin/time gawk -f ./test.awk | md5sum
3.43user 0.01system 0:03.47elapsed 99%CPU (0avgtext+0avgdata 3824maxresident)k
0inputs+0outputs (0major+279minor)pagefaults 0swaps
a9de67bf77dec1b5285ee27f71ab3564  -
[root@centos5 ~]# 
[root@centos5 ~]# /usr/bin/time mawk -f ./test.awk | md5sum
1.48user 0.00system 0:01.49elapsed 99%CPU (0avgtext+0avgdata 2976maxresident)k
0inputs+0outputs (0major+220minor)pagefaults 0swaps
a9de67bf77dec1b5285ee27f71ab3564  -
このように、2倍くらい速いです。
まあ、処理内容によるでしょうけど、わたしが日頃使う手持ちスクリプトは軒並み2倍速かったので、これからは有難く常用させてもらおうと思います。作者さん&メンテナー様に感謝。

EPEL の使い方については、次の記事を参照ください。
CentOS 6 で ntfs をマウントする
なお、CentOS 6 (or RHEL6) 用の EPEL チャネルにも mawk ありました。

gawk で性能足りないということがあったら、最後の切り札に使える場合があるかも。ただし、GNU 拡張等、互換性に注意する必要がある。

2011年11月13日日曜日

Ruby からシステムコールを直接呼び出す方法

Ruby からシステムコールを直接呼び出す方法です。自分用のメモ。

perl には syscall() が用意されており、使ったことがありましたので、Ruby にもあるかな?とライブラリリファレンスを参照してみると、案の定ありました。
clock_gettime(CLOCK_MONOTONIC) を呼び出すサンプルです。
#!/usr/bin/env ruby

NR_clock_gettime = 228  # defined in asm-x86_64/unistd.h
CLOCK_MONOTONIC = 1     # CLOCK_MONOTONIC defined in linux/time.h
ts = ' ' * 16
syscall(NR_clock_gettime, CLOCK_MONOTONIC, ts)
(tv_sec, tv_nsec) = ts.unpack("l!l!")
printf "%d.%09d\n", tv_sec, tv_nsec

次が実行結果です。
# ./monotonic.rb ; cat /proc/uptime 
134772.112499324
134772.11 134476.00
なお、一般に、システムコール番号はアーキテクチャ毎に異なります。上記の 228 は、x86_64 の場合です。

■関連記事
経過時間の計測方法

2011年11月6日日曜日

ビルトインを自作して、bash で処理時間をミリ秒単位で計測

先日、bash で処理時間(経過時間)を 10 ミリ秒の粒度で計測する方法を書きました。おそらく、bash 自体やプロセス生成のオーバーヘッドを考えると、実用上はそれで十分と思います。
この記事では、いささかオーバースペック(と言うか諸々のオーバーヘッドを加味すると無意味かも? )ですが、bash のビルトイン(builtin)作成方法に興味があり、練習として、ミリ秒より細かく計測するためのインターフェースを作成してみました。

以下、CentOS 5.7 x86_64 上、root で作業しています。

まずは、bash のソース RPM (.src.rpm) を入手して、展開します。
http://vault.centos.org/5.7/os/SRPMS/
この時点で最新の bash-3.2-32.el5.src.rpm を使いました(作業環境と同じバージョン)。
# rpm -ivh bash-3.2-32.el5.src.rpm
# rpmbuild -bc /usr/src/redhat/SPEC/bash.spec
上の操作で、/usr/src/redhat/BUILD/bash-3.2/ の下にソースが展開されます。
ここで、bash ビルトインのコンパイル環境を整えるため、-bp ではなくて、-bc とします。
-bp のあとに、手動で configure という流れで作業すると、作成したビルトインの enable 時にリンクエラーになってしまいます(具体的には sh_xrealloc で引っかかりました)。

次に、ソースツリーの下の example/loadables の下へ作業ディレクトリを移動して、ひとまずサンプルをビルドして、使ってみます。
# cd /usr/src/redhat/BUILD/bash-3.2/examples/loadables/
# make -k
※ビルド過程は省略します。わたしの環境では、多少エラーが出てました。
# enable -f ./strftime strftime  ※もしリンクエラーになると、bash が落ちます
# enable | grep strftime         ※落ちなければロードされたはずですが、確認です
enable strftime
# strftime "%F %T"               ※試しに strftime を実行
2011-11-06 15:11:13

ここまでで、ビルトインを自作する環境が整います。あとは、サンプルを参考にしながら monotonic というビルトインを自作してみました。strftime.c を下敷きにしています。
     1  /* monotonic - loadable builtin interface to clock_gettime(CLOCK_MONOTONIC) */
     2  
     3  /* Copyright (C) 2011-2011
     4     luna2 at http://luna2-linux.blogspot.com/
     5     If you contact luna2, email to blue3waters at gmail.com.
     6  
     7     This file is subject to the GNU General Public License.
     8     It comes with NO WARRANTY.
     9   */
    10  
    11  /* See Makefile for compilation details. */
    12  
    13  #include <config.h>
    14  
    15  #if defined (HAVE_UNISTD_H)
    16  #  include <unistd.h>
    17  #endif
    18  
    19  #include "bashtypes.h"
    20  #include "posixtime.h"
    21  
    22  #include <stdio.h>
    23  
    24  #include "builtins.h"
    25  #include "shell.h"
    26  #include "common.h"
    27  #include "bashgetopt.h"
    28  
    29  #include <time.h>       /* clock_gettime */
    30  
    31  int
    32  monotonic_builtin (list)
    33    WORD_LIST *list ;
    34  {
    35    char  *tbuf = NULL ;
    36    size_t tbsize ;
    37    char  *tv_sec = NULL ;
    38    char  *tv_nsec = NULL ;
    39    struct timespec t ;
    40  
    41    if (list == 0) {
    42      builtin_usage() ;
    43      return EX_USAGE ;
    44    }
    45  
    46    tv_sec = list->word->word ;
    47    if (tv_sec == 0 || *tv_sec == 0) {
    48      builtin_usage() ;
    49      return EX_USAGE ;
    50    }
    51  
    52    list = list->next ;
    53  
    54    if (list && list->word->word) {
    55      tv_nsec = list->word->word ;
    56    } else {
    57      builtin_usage() ;
    58      return EX_USAGE ;
    59    }
    60  
    61    clock_gettime(CLOCK_MONOTONIC, &t) ;
    62  
    63    tbsize = 32 ;         /* enough to store ULONG_MAX */
    64    tbuf = xmalloc(tbsize) ;
    65    snprintf(tbuf, tbsize, "%lu", t.tv_sec) ;
    66    bind_variable(tv_sec, tbuf, 0) ;
    67    free(tbuf) ; tbuf = NULL ;    /* erase immediately */
    68  
    69    tbsize = 32 ;
    70    tbuf = xmalloc(tbsize) ;
    71    snprintf(tbuf, tbsize, "%lu", t.tv_nsec) ;
    72    bind_variable(tv_nsec, tbuf, 0) ;
    73    free(tbuf) ; tbuf = NULL ;
    74  
    75    return EXECUTION_SUCCESS ;
    76  }
    77  
    78  /* An array of strings forming the `long' documentation for a builtin xxx,
    79     which is printed by `help xxx'.  It must end with a NULL. */
    80  char *monotonic_doc[] = {
    81          "The interface to clock_gettime(CLOCK_MONOTONIC).",
    82          "Store the results in shell variable TV_SEC and TV_NSEC",
    83          (char *)NULL
    84  } ;
    85  
    86  /* The standard structure describing a builtin command.  bash keeps an array
    87     of these structures.  The flags must include BUILTIN_ENABLED so the
    88     builtin can be used. */
    89  struct builtin monotonic_struct = {
    90          "monotonic",            /* builtin name */
    91          monotonic_builtin,      /* function implementing the builtin */
    92          BUILTIN_ENABLED,        /* initial flags for builtin */
    93          monotonic_doc,          /* array of long documentation strings. */
    94          "monotonic TV_SEC TV_NSEC",     /* usage synopsis; becomes short_doc */
    95          0                       /* reserved for internal use */
    96  } ;
これをビルドするため、Makefile を書き換えます。差分は、次の通りです。
# diff -u Makefile.org Makefile
--- Makefile.org        2011-11-06 12:35:25.000000000 +0900
+++ Makefile    2011-11-06 14:00:09.000000000 +0900
@@ -69,7 +69,7 @@
 SHOBJ_CFLAGS = -fPIC
 SHOBJ_LD = ${CC}
 SHOBJ_LDFLAGS = -shared -Wl,-soname,$@
-SHOBJ_XLDFLAGS = 
+SHOBJ_XLDFLAGS = -lrt
 SHOBJ_LIBS = 
 SHOBJ_STATUS = supported
 
@@ -83,7 +83,7 @@
 
 ALLPROG = print truefalse sleep pushd finfo logname basename dirname \
          tty pathchk tee head mkdir rmdir printenv id whoami \
-         uname sync push ln unlink cut realpath getconf strftime
+         uname sync push ln unlink cut realpath getconf strftime monotonic
 OTHERPROG = necho hello cat
 
 all:   $(SHOBJ_STATUS)
@@ -186,6 +186,9 @@
 strftime:      strftime.o
        $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ strftime.o $(SHOBJ_LIBS)
 
+monotonic:     monotonic.o
+       $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ monotonic.o $(SHOBJ_LIBS)
+
 # pushd is a special case.  We use the same source that the builtin version
 # uses, with special compilation options.
 #
@@ -236,3 +239,4 @@
 mkdir.o: mkdir.c
 realpath.o: realpath.c
 strftime.o: strftime.c
+monotonic.o: monotonic.c
特に、librt のリンクが必要なので、SHOBJ_XLDFLAGS に -lrt を設定しています。

もう一度 make -k を実行すれば、monotonic が出来上がります。
# make -k
# ls -l monotonic*
-rwxr-xr-x 1 root root 11463 Nov  6 15:29 monotonic
-rw-r--r-- 1 root root  2379 Nov  6 15:29 monotonic.c
-rw-r--r-- 1 root root  9992 Nov  6 15:29 monotonic.o

試しに動かしてみた結果です。
# enable -f ./monotonic monotonic
# enable | grep monotonic
enable monotonic
# help monotonic
monotonic: monotonic TV_SEC TV_NSEC
    The interface to clock_gettime(CLOCK_MONOTONIC).
    Store the results in shell variable TV_SEC and TV_NSEC
# monotonic TV_SEC TV_NSEC
# echo $TV_SEC $TV_NSEC
12115 474565954
# cat /proc/uptime ; monotonic TV_SEC TV_NSEC ; echo $TV_SEC $TV_NSEC
12148.86 11976.46
12148 861371954
どうやら、うまく動きました。
monotonic は、指定されたシェル変数(上では TV_SEC および TV_NSEC ですが、他の名前でも可)に、clock_gettime(CLOCK_MONOTONIC) で得られた値を設定します。

経過時間を計測する場合、得られた値の差を求めれば良いので、実際にやってみます。
#!/bin/bash
#
# Name: elaps_ns.bash
#

enable -f ./monotonic monotonic

set_START() {
        monotonic START START_NSEC
}

get_ELAPS() {
        monotonic END END_NSEC
        if [ $END_NSEC -ge $START_NSEC ] ; then
                let ELAPS=END-START
                let ELAPS_NS=END_NSEC-START_NSEC
        else
                let ELAPS=END-START-1
                let ELAPS_NS=1000000000+END_NSEC-START_NSEC
        fi
}

echo SECONDS=$SECONDS   #bash's builtin
time {
        set_START
}
echo START=$START NSEC=$START_NSEC

echo -e "\n### sleep (2500 x 1000) [us]  using usleep"
time {
        usleep 2500000
}

time {
        get_ELAPS
}
echo END=$END NSEC=$END_NSEC
printf "ELAPS=START-END=$ELAPS.%09d\n" $ELAPS_NS
echo
echo SECONDS=$SECONDS   #bash's builtin
# ./elaps_ns.bash 
SECONDS=0

real    0m0.000s
user    0m0.000s
sys     0m0.000s
START=14665 NSEC=852954954

### sleep (2500 x 1000) [us]  using usleep

real    0m2.503s
user    0m0.000s
sys     0m0.000s

real    0m0.000s
user    0m0.000s
sys     0m0.000s
END=14668 NSEC=356643954
ELAPS=START-END=2.503689000

SECONDS=2
bash 組み込みの time で計った経過時間 2.503 と一致することが確認できます。

利用したシステムコール clock_gettime(CLOCK_MONOTONIC) については、次の記事を参照してください。

経過時間の計測方法

2011年11月5日土曜日

経過時間の計測方法

最近、スクリプト言語の恩恵により、C で書く機会というのは滅多にないのですが、ふと経過時間測定のための bash 用 builtin を書きたいと思い、その基礎として、現在の Linux で利用できるインタフェースについて調べました。

わたしの過去の C プログラミング経験 (某UNIX,10年くらい前) では、経過時間の計測には gettimeofday() を使っていました。たぶんそれしか無かった (?) ので。
しかし、その場合、システム時間が遡ってしまう可能性 (システム管理者が日時設定する時など) を考慮する必要があります。
2011-11-06追記、省みると、その当時,その UNIX でも times() はあったでしょうから、わたしが無知だったということですね。wrap への考慮は必要としても。

調べてみると、現在の Linux であれば、clock_gettime(CLOCK_MONOTONIC,) を使えば良さそうだと知りました。
このシステムコールを使えば、システム起動からの経過時間を高精度で得られるようです。
つまり、(カーネルソースまで調べてないですが、おそらくは) /proc/uptime の左側数字の情報源を高精度で得られます。
これなら、システム時刻が遡るタイミングであっても、経過時間を高精度で計測できます。
よく、times() システムコールの戻り値を、経過時間の計測に使ってバグっている (wrap のタイミングで) のを見かけますが、こちらを使えば、そんなバグも作らずに済みそうです。(Solaris やら HP-UX も対応しようとして、共通に使えるものを探した結果そうなるのでしょうけど … )

以下が、サンプルプログラムです。コンパイル時には、librt をリンクするため、リンクオプション -lrt を付加する必要があります。
/*
    gcc -o monotonic monotonic.c -lrt
*/
#include <time.h>
#include <stdio.h>

main()
{
        struct timespec tp ;
        clock_gettime(CLOCK_MONOTONIC, &tp) ;
        printf("%llu %llu\n", tp.tv_sec, tp.tv_nsec) ;
}
実行結果は次の通りです。
# cat /etc/redhat-release 
CentOS release 5.7 (Final)
# uname -a
Linux xxx 2.6.18-274.3.1.el5 #1 SMP Tue Sep 6 20:13:52 EDT 2011 x86_64 x86_64 x86_64 GNU/Linux
# gcc -o monotonic monotonic.c -lrt
# ./monotonic ; cat /proc/uptime 
10714 102582089
10714.10 10559.35
このように、/proc/uptime の値が高精度で得られている様子が見えます。

2011-11-06追記
実際に、clock_gettime(CLOCK_MONOTONIC) を呼び出す bash ビルトインを自作してみました。次の記事を参照ください。

ビルトインを自作して、bash で処理時間をミリ秒単位で計測

2011-11-12追記
ソースから、/proc/uptime と clock_gettime(CLOCK_MONOTONIC) の情報源が同じであることを確かめました。参照したのは、kernel-2.6.18-274.el5 です。
まず、/proc/uptime は次のようになっています。
     97 static int uptime_read_proc(char *page, char **start, off_t off,
     98                                  int count, int *eof, void *data)
     99 {
    100         struct timespec uptime;
    101         struct timespec idle;
    102         int len;
    103         cputime_t idletime = cputime_add(init_task.utime, init_task.stime);
    104 
*   105         do_posix_clock_monotonic_gettime(&uptime);
    106         cputime_to_timespec(idletime, &idle);
    107         len = sprintf(page,"%lu.%02lu %lu.%02lu\n",
    108                         (unsigned long) uptime.tv_sec,
    109                         (uptime.tv_nsec / (NSEC_PER_SEC / 100)),
    110                         (unsigned long) idle.tv_sec,
    111                         (idle.tv_nsec / (NSEC_PER_SEC / 100)));
    112 
    113         return proc_calc_metrics(page, start, off, count, eof, len);
    114 }
"fs/proc/proc_misc.c"
105行目の do_posix_clock_monotonic_gettime() は、もう名前からして、clock_gettime(CLOCK_MONOTONIC) に対応していそうですが、その通りです。
    114 #define do_posix_clock_monotonic_gettime(ts) ktime_get_ts(ts)
"include/linux/time.h"
    157 /*
    158  * Call the k_clock hook function if non-null, or the default function.
    159  */
*   160 #define CLOCK_DISPATCH(clock, call, arglist) \
    161         ((clock) < 0 ? posix_cpu_##call arglist : \
    162          (posix_clocks[clock].call != NULL \
    163           ? (*posix_clocks[clock].call) arglist : common_##call arglist))
...
    218 /*
    219  * Get monotonic time for posix timers
    220  */
    221 static int posix_ktime_get_ts(clockid_t which_clock, struct timespec *tp)
    222 {
*   223         ktime_get_ts(tp);
    224         return 0;
    225 }
    226 
    227 /*
    228  * Initialize everything, well, just everything in Posix clocks/timers ;)
    229  */
    230 static __init int init_posix_timers(void)
    231 {
    232         struct k_clock clock_realtime = {
    233                 .clock_getres = hrtimer_get_res,
    234         };
    235         struct k_clock clock_monotonic = {
    236                 .clock_getres = hrtimer_get_res,
*   237                 .clock_get = posix_ktime_get_ts,
    238                 .clock_set = do_posix_clock_nosettime,
    239         };
    240 
    241         register_posix_clock(CLOCK_REALTIME, &clock_realtime);
*   242         register_posix_clock(CLOCK_MONOTONIC, &clock_monotonic);
    243 
    244         posix_timers_cache = kmem_cache_create("posix_timers_cache",
    245                                         sizeof (struct k_itimer), 0, 0, NULL, NULL);
    246         idr_init(&posix_timers_id);
    247         return 0;
    248 }
...
    920 asmlinkage long
    921 sys_clock_gettime(const clockid_t which_clock, struct timespec __user *tp)
    922 {
    923         struct timespec kernel_tp;
    924         int error;
    925 
    926         if (invalid_clockid(which_clock))
    927                 return -EINVAL;
*   928         error = CLOCK_DISPATCH(which_clock, clock_get,
    929                                (which_clock, &kernel_tp));
    930         if (!error && copy_to_user(tp, &kernel_tp, sizeof (kernel_tp)))
    931                 error = -EFAULT;
    932 
    933         return error;
    934 
    935 }
"kernel/posix-timers.c"
モノトニック時刻の実装詳細は、ktime_get_ts() の先にありますが、この記事では情報源の確認までにします。jiffies_64 に帰着するのかと思ってましたが、そうでは無いようでした。興味のある方は、先を追ってみては。
以上から、10ミリ秒精度で十分であれば、モノトニック時刻の取得は、/proc/uptime の読み出しで代用できるということが確かめられました。各種スクリプト言語から、簡単に利用できて便利と思います。
なお、主要スクリプト言語について、clock_gettime() を呼び出すインターフェースがあるか調べてみました。以下。

perl
http://perldoc.perl.org/Time/HiRes.html

Ruby
見つけられませんでした。2011-11-13追記、Ruby にも perl と同様な syscall() が用意されていますので、強引に呼び出すことはできました。

Python
http://stackoverflow.com/questions/1205722/how-do-i-get-monotonic-time-durations-in-python
同様にして、ほとんど何でも C ライブラリを呼び出せそうです。すごいな Python は。
perl でも、システムコールであれば、syscall() を使って無理すれば呼べますが、こんなにキレイにはできません。2011-11-13追記、実際動かしてみると、CentOS 5 の python 2.4 では動かず、CentOS 6 の python 2.6 なら動きました。CentOS 5 では ctypes が足りませんでした。
2012-10-14追記、CentOS 5.8 で python-ctypes が追加されていたようです。

Ruby については、システムコール直呼び出しはやったことないので不明です。今度、調べてみよう。


■関連記事
bash で処理時間を 10 ミリ秒単位で計測する方法
ビルトインを自作して、bash で処理時間をミリ秒単位で計測
perlでサブルーチンを動的に定義しようとしてハマりました

2011年10月30日日曜日

bash で処理時間を 10 ミリ秒単位で計測する方法

bash スクリプト中の特定部分の処理時間(経過時間)を、1秒より細かい粒度で計測したいため、その方法を考えてみました。

まず、秒単位で良ければ、次の場所にあれこれと書いてある方法で良いかと思います。

http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=22451&forum=10

ただ、bash には組み込み変数 SECONDS (シェルが起動されてからの経過秒数) があるので、次のように書いたほうがオーバーヘッドが少ないはずです。
#!/bin/bash
#
# Name: elaps.bash
#

sleep 1

START=$SECONDS  #bash's builtin
echo START=$START

echo -e "\n### sleep 2"
sleep 2
echo

END=$SECONDS
let ELAPS=$END-$START
echo END=$END
echo ELAPS=START-END=$ELAPS
# ./elaps.bash 
START=1

### sleep 2

END=3
ELAPS=START-END=2

さて、わたしが取り組んでいる内容に対して、秒では粗すぎます。ミリ秒程度まで取得したいのですが、標準では bash から直接 gettimeofday() を呼ぶことができません。builtin を作れば不可能ではありませんが、ターゲットがそこまで自由に出来る環境ではないので、別の方法を考えました。

目をつけたのは、/proc/uptime です。このファイルから、システム起動からの経過時間を 10 ミリ秒の粒度で読み取ることができます。man proc 参照。
# cat /proc/uptime 
4542.13 4528.56
# uptime
 11:42am  up  1:15,  2 users,  load average: 0.13, 0.07, 0.02
bash の組み込みコマンド read を利用すれば、オーバーヘッド少なく 10 ミリ秒の粒度で経過時間を計測できるはずです。次が、テストスクリプトです。
#!/bin/bash
#
# Name: elaps_ms.bash
#

set_START() {
        local dummy
        read START dummy < /proc/uptime
}

get_ELAPS() {
        local dummy
        read END dummy < /proc/uptime
        let ELAPS=${END/./}0-${START/./}0
}

echo SECONDS=$SECONDS   #bash's builtin
time {
        set_START
}
echo START=$START

echo -e "\n### sleep (50 x 10 x 1000) [us]  using usleep"
time {
        usleep 500000
}

time {
        get_ELAPS
}
echo END=$END
echo ELAPS=START-END=$ELAPS
echo
echo SECONDS=$SECONDS   #bash's builtin
# ./elaps_ms.bash 
SECONDS=0

real    0m0.001s
user    0m0.000s
sys     0m0.000s
START=5579.40

### sleep (50 x 10 x 1000) [us]  using usleep

real    0m0.520s
user    0m0.000s
sys     0m0.010s

real    0m0.001s
user    0m0.000s
sys     0m0.000s
END=5579.92
ELAPS=START-END=520

SECONDS=0
このように、500 ミリ秒の usleep 実行の経過時間を 52 x 10 ミリ秒 = 520 ミリ秒と計測することができました。bash 組み込みの time コマンドの計測結果とも一致しています。一方、SECONDS による計測では、0 秒となってしまっています。

さてここで、少しだけ気になる点があります、bash で扱える整数の範囲です。上述のテストスクリプトでは、/proc/uptime から読み取った小数(小数点以下 2 桁)を、単位ミリ秒の整数に変換しています。1日をミリ秒で表すと 24 x 60 x 60 x 1000 = 86400000 となりますが、
# getconf INT_MAX
2147483647
# bc -l
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
2147483647 / (24 * 60 * 60 * 1000)
24.85513480324074074074
なので、システム起動から 25 日経過した時に大丈夫かな?と不安になりませんか?
しかし、過去に調べたとおり、最近は 32bit 環境の bash でも、64bit 幅までの整数を扱えるので、大丈夫なはずです。次の記事を参照してください。

bash で扱える整数の上限

2011-11-06追記
試しに bash のビルトインを自作して、もっと細かい粒度で経過時間を測定してみました。

ビルトインを自作して、bash で処理時間をミリ秒単位で計測

2011年10月29日土曜日

perl でモジュールの存在を確認する方法

perl で、使いたいモジュールがシステムにインストールされていない場合に、use hoge.pm のエラー(例外)を捕捉する方法を調べました。
「Perl クックブック VOLUME 1」のレシピ 12.2 に書いてあるのですが、次のような具合にします。
BEGIN {
        $zlib = "Compress::Zlib" ;
        unless (eval "use $zlib ; 1") {
                #warn "WARNING: couldn't use $zlib" ;  # 警告出すか否かは文脈次第
                undef $zlib ;  # あとでこの変数を確認することで、代替処理やエラー処理
        }
}
具体的な利用局面について補足しますと、gzip ファイルデータを直接扱いたかったのですが、わたしの主戦場である RHEL/CentOS では、Compress::Zlib が別パッケージ (perl-Compress-Zlib) になっており、必ずしもインストールされていないため、その場合、仕方ないので gzip コマンドを呼ぶという代行処理を行う必要がありました。

Python の”電池が付属しています”思想のように、perl でもこのあたりまでデフォルトで入っていてくれると手間が減るのにと思ってしまいます。Python の場合、かなり昔から gzip ライブラリが標準で入っているようです。

2011年10月24日月曜日

CentOS 5 サスペンドメニューを出さないようにする

CentOS 5 で、マシンが対応していると、System メニューに Suspend が表示されるが、これが Log Out のすぐ下に表示されるため、誤操作し易い。
サーバ用途では、サスペンドすることはまずないと思うので、このような位置に表示するというのは、デザイン上のミスじゃないのかと思ってしまいます。もちろん GNOME は、サーバ用途に作られているわけじゃないのでしょうけども、RHEL としてまとめるときに、配慮できないもんでしょうかね。

Suspend メニューを表示しないようにするには、gconf-editor をインストールして、apps / gnome-power-manager の can_suspend を外せば良いです。ついでに、can_hibernate も外しておいたほうが良いかと。


なお、そもそも、安定稼動のためには、サーバで X を使わないのが正解だろうと思います。

2011-11-12追記
コマンドラインからでも修正できるようです。
# gconftool-2 --type boolean --set /apps/gnome-power-manager/can_suspend false
# gconftool-2 --type boolean --set /apps/gnome-power-manager/can_hibernate false
http://blog.toracat.org/2009/04/getting-rid-of-suspend-on-a-desktop-machine/

なお、CentOS 6 では、仕組みが変更されているため、上記の方法は通用しません。

2011年10月10日月曜日

date コマンドで先月を求める方法

date コマンドには、last month という指定があり、これで先月を求められると思ったのですが、次のように変なことになってしまう場合があります。
# date -d "2011-03-30 last month" +"%F"
2011-03-02
調べてみると、次のような情報がありました。
http://www.walkernews.net/2007/06/03/date-arithmetic-in-linux-shell-scripts/

つまり、次のように細工すれば良いとのことです。なお、15 の部分は、全ての月で存在する日付(01~28)のどれかを指定すれば良いので、01 でも大丈夫です。
# date -d "$(date +%Y-%m-15) -1 month" +%Y-%m
2011-09
同様に、先々月だったら、次のように -2 にすれば良いです。
# date -d "$(date +%Y-%m-15) -2 month" +%Y-%m
2011-08

■関連記事
ZFS on Linux の snapshot 運用スクリプト例

2011年10月2日日曜日

gdisk コマンド (Fedora 16 からは GPT がデフォルト)

Fedora 16 からは、パーティションテーブルがデフォルトで GPT になります。

http://fedorapeople.org/groups/docs/release-notes/ja/sect-Release_Notes-Changes_for_Sysadmin.html

次は、VMware 上に Fedora 16 Alpha をインストールした環境での見え方です。
[root@fedora16 ~]# fdisk -l /dev/sda

WARNING: GPT (GUID Partition Table) detected on '/dev/sda'! The util fdisk doesn't support GPT. Use GNU Parted.


Disk /dev/sda: 21.5 GB, 21474836480 bytes
255 heads, 63 sectors/track, 2610 cylinders, total 41943040 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1               1    41943039    20971519+  ee  GPT
[root@fedora16 ~]# parted -s /dev/sda print
Model: VMware, VMware Virtual S (scsi)
Disk /dev/sda: 21.5GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number  Start   End     Size    File system     Name  Flags
 1      1049kB  3146kB  2097kB                        bios_grub
 2      3146kB  527MB   524MB   ext4            ext4  boot
 3      527MB   1052MB  524MB   linux-swap(v1)
 4      1052MB  21.5GB  20.4GB
sda1 (bios_grub) には、GRUB2 の stage2 が格納されており、EFI 対応でない旧来のシステム (旧来のBIOS搭載システム) でも、この bios_grub 領域を使うことで、GPT パーティションが使用されているディスクからブート可能になるようです。

さて、GPT を扱うツールと言うと、parted があるわけですが、fdisk とは異なり、指示した操作が即座にコミットされるという怖さがあります。幸いにも、fdisk と似た操作性で GPT を扱うことを可能とした gdisk というツールが普及しつつあります。
次は、gdisk でパーティション一覧を表示したサンプルです。
[root@fedora16 ~]# gdisk -l /dev/sda
GPT fdisk (gdisk) version 0.7.2

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Disk /dev/sda: 41943040 sectors, 20.0 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): BD020EE9-4CD0-45E3-8244-C1BEE950D2CB
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 41943006
Partitions will be aligned on 2048-sector boundaries
Total free space is 4029 sectors (2.0 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048            6143   2.0 MiB     EF02  
   2            6144         1030143   500.0 MiB   EF00  ext4
   3         1030144         2054143   500.0 MiB   8200  
   4         2054144        41940991   19.0 GiB    0700
fdisk と似たコマンド体系になっています。
[root@fedora16 ~]# gdisk /dev/sda
GPT fdisk (gdisk) version 0.7.2

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.

Command (? for help): ?
b       back up GPT data to a file
c       change a partition's name
d       delete a partition
i       show detailed information on a partition
l       list known partition types
n       add a new partition
o       create a new empty GUID partition table (GPT)
p       print the partition table
q       quit without saving changes
r       recovery and transformation options (experts only)
s       sort partitions
t       change a partition's type code
v       verify disk
w       write table to disk and exit
x       extra functionality (experts only)
?       print this menu

Command (? for help):
gdisk は、つい最近 EPEL に追加されました (9月に正式なリポジトリーに公開されました)。CentOS 5 以上 (もちろん RHEL5 以上も可) でも利用可能です。2TB を越えるディスクを扱う場合に重宝すると思います。parted が気に入っているというなら、それでも良いでしょうが、操作ミスにお気をつけください。

2014-02-02追記
いつの間にか fdisk が GPT 対応になっていることに気づきました。Fedora 20 環境で発見。
http://blog.stgolabs.net/2012/09/fdisk-updates-and-gpt-support.html
ということは、将来の RHEL7 や CentOS 7 には、GPT 対応の fdisk が収録されそうですね。Fedora 20 に収録されている GPT 対応の fdisk は、なかなかよく出来ている印象です。

2014-04-27追記
RHEL7 beta を触ってみたら、GPT 対応の fdisk が収録されていました。ただし、man は古いままで、GPT は扱えないとの記載のままでした。
# fdisk /dev/sda
Welcome to fdisk (util-linux 2.23.2).

Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


コマンド (m でヘルプ): m
コマンドの動作
   a   toggle a bootable flag
   b   edit bsd disklabel
   c   toggle the dos compatibility flag
   d   delete a partition
   g   create a new empty GPT partition table
...
なお、RHEL7 beta には gdisk も収録されてました。もちろん parted もありました。というわけで3本立てになっちゃってます。

2015-01-31追記
追記していませんでしたが、gdisk は、最終的に RHEL7/CentOS7 に収録されています。ですので、これから先は gdisk の利用人口が増える(したがってネット上の情報も増える)ものと思われます。ちなみに、見つけてから 3 年ほど、たびたび gdisk のお世話になっていますが、特に不都合/不具合に遭遇したことはありません。そのため、未だに parted は苦手(GParted のほうは時々使います)ですが、RHEL7 に収録されたし、gdisk が消え去ることは無いと思えるので、基本 gdisk を使っていこうと思います。

2015-05-29追記
いつの間にやら、RHEL6.6/CentOS6.6 で gdisk が標準で同梱されるようになってました。これで利用者も更に増える(ネットの情報量も増える)でしょう。いままででも不都合に遭遇したことは無かったですが、頼れるツール(枯れたツール)に成長していくことと思います。んが、一方で最新の fdisk は、GPT にも対応しているので、将来はわかりませんね。果たして gdisk が淘汰されるでしょうかね。。。

2011年9月20日火曜日

CentOS 5.7 のトピックス

CentOS 5.7 で新たに追加された機能等の、自分用メモです。
(重要事項)
自分の中では、特にはなかった。

(リリースノートには書かれていないが目に留まったもの)
■tcsh617 の追加
tcsh 6.17 にリベースしたパッケージが提供されているようだ。
csh を使ってハマっている場合に、試してみると良さそう。
■rm コマンドの性能向上
https://bugzilla.redhat.com/show_bug.cgi?id=523923
1つのディレクトリに多数のファイルが存在する場合、コーディング上の工夫により、性能向上を達成するらしい。下手に C で書くよりも、rm を呼んで、その最適化されたコードを利用したほうがお得な場合もあるかも。

2011年9月10日土曜日

Fedora 16 に新採用された chrony の設定

Fedora 16 (今現在は Alpha ですが) では、NTP クライアントが ntpd から chronyd に変更されています。設定方法について、メモです。

まず、rpm 情報です。
[root@fedora16 ~]# rpm -qi chrony
Name        : chrony
Version     : 1.26
Release     : 3.20110831gitb088b7.fc16
Architecture: x86_64
Install Date: Sat 10 Sep 2011 06:54:41 AM JST
Group       : System Environment/Daemons
Size        : 546684
License     : GPLv2
Signature   : RSA/SHA256, Tue 06 Sep 2011 07:54:34 PM JST, Key ID 067f00b6a82ba4b7
Source RPM  : chrony-1.26-3.20110831gitb088b7.fc16.src.rpm
Build Date  : Wed 07 Sep 2011 12:29:09 AM JST
Build Host  : x86-14.phx2.fedoraproject.org
Relocations : (not relocatable)
Packager    : Fedora Project
Vendor      : Fedora Project
URL         : http://chrony.tuxfamily.org
Summary     : An NTP client/server
Description :
A client/server for the Network Time Protocol, this program keeps your
computer's clock accurate. It was specially designed to support
systems with intermittent internet connections, but it also works well
in permanently connected environments. It can use also hardware reference
clocks, system real-time clock or manual input as time references.
systemd の初期状態を確認。chronyd は disabled になってます。#それにしても、systemctl コマンドに慣れないなあ、、、
[root@fedora16 ~]# systemctl status chronyd.service
chronyd.service - NTP client/server
          Loaded: loaded (/lib/systemd/system/chronyd.service; disabled)
          Active: inactive (dead)
          CGroup: name=systemd:/system/chronyd.service
これを、enable に変更する。
[root@fedora16 ~]# systemctl enable chronyd.service
ln -s '/lib/systemd/system/chronyd.service' '/etc/systemd/system/multi-user.target.wants/chronyd.service'
[root@fedora16 ~]# systemctl status chronyd.service
chronyd.service - NTP client/server
          Loaded: loaded (/lib/systemd/system/chronyd.service; enabled)
          Active: inactive (dead)
          CGroup: name=systemd:/system/chronyd.service
設定ファイル (/etc/chronyd.conf) は、ntpd と似た雰囲気です。
[root@fedora16 ~]# rpm -qc chrony  ※設定ファイルの所在を確認
/etc/chrony.conf
/etc/chrony.keys
/etc/logrotate.d/chrony
# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
server 0.fedora.pool.ntp.org iburst
server 1.fedora.pool.ntp.org iburst
server 2.fedora.pool.ntp.org iburst
server 3.fedora.pool.ntp.org iburst

# Ignore stratum in source selection.
stratumweight 0

# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift

# Enable kernel RTC synchronization.
rtcsync

# In first three updates step the system clock instead of slew
# if the adjustment is larger than 100 seconds.
makestep 100 3

# Allow client access from local network.
#allow 192.168/16

# Serve time even if not synchronized to any NTP server.
#local stratum 10

keyfile /etc/chrony.keys

# Specify the key used as password for chronyc.
commandkey 1

# Disable logging of client accesses.
noclientlog

# Send a message to syslog if a clock adjustment is larger than 0.5 seconds.
logchange 0.5

logdir /var/log/chrony
#log measurements statistics tracking
そんでもって起動!
[root@fedora16 ~]# systemctl start chronyd.service   ※起動!
[root@fedora16 ~]# systemctl status chrnoyd.service  ※打ちにくい、、、typo した、、、
chrnoyd.service
          Loaded: error (Reason: No such file or directory)
          Active: inactive (dead)
[root@fedora16 ~]# systemctl status chronyd.service  ※確認
chronyd.service - NTP client/server
          Loaded: loaded (/lib/systemd/system/chronyd.service; enabled)
          Active: active (running) since Sat, 10 Sep 2011 20:54:47 +0900; 14s ago
         Process: 19657 ExecStartPost=/usr/libexec/chrony-helper add-dhclient-servers (code=exited, status=0/SUCCESS)
         Process: 19654 ExecStart=/usr/sbin/chronyd -u chrony $OPTIONS (code=exited, status=0/SUCCESS)
         Process: 19645 ExecStartPre=/usr/libexec/chrony-helper generate-commandkey (code=exited, status=0/SUCCESS)
        Main PID: 19656 (chronyd)
          CGroup: name=systemd:/system/chronyd.service
                  └ 19656 /usr/sbin/chronyd -u chrony
ntpq のような状態確認は、chronyc で行います。
[root@fedora16 ~]# chronyc sources -v
210 Number of sources = 4

  .-- Source mode  '^' = server, '=' = peer, '#' = local clock.
 / .- Source state '*' = current synced, '+' = OK for sync, '?' = unreachable,
| /                'x' = time may be in error, '~' = time is too variable.
||                                                 .- xxxx [ yyyy ] +/- zzzz
||                                                /   xxxx = adjusted offset,
||         Log2(Polling interval) -.             |    yyyy = measured offset,
||                                  \            |    zzzz = estimated error.
||                                   |           |                         
MS Name/IP address           Stratum Poll LastRx Last sample
============================================================================
^+ javanese.kjsl.com             2    6     18    -11ms[  -11ms] +/-  211ms
^* balthasar.gimasystem.jp       3    6     20   +321us[ +815us] +/-   53ms
^+ z2.ktroad.net                 3    6     20  +1228us[+1722us] +/-   75ms
^+ 219x123x70x90.ap219.ftth.     2    6     20  -3397us[-3397us] +/-   57ms

[root@fedora16 ~]# chronyc sourcestats -v
210 Number of sources = 4
                             .- Number of sample points in measurement set.
                            /    .- Number of residual runs with same sign.
                           |    /    .- Length of measurement set (time).
                           |   |    /      .- Est. clock freq error (ppm).
                           |   |   |      /           .- Est. error in freq.
                           |   |   |     |           /         .- Est. offset.
                           |   |   |     |          |          |   On the -.
                           |   |   |     |          |          |   samples. \
                           |   |   |     |          |          |             |
Name/IP Address            NP  NR  Span  Frequency  Freq Skew  Offset  Std Dev
==============================================================================
javanese.kjsl.com          10   6   397      2.808      6.249    -12ms   570us
balthasar.gimasystem.jp    10   8   395      0.171      8.376  +3871ns   717us
z2.ktroad.net              10   5   395      0.157      8.272  +1367us   600us
219x123x70x90.ap219.ftth.  10   6   395      2.790     10.922  -3060us   979us

ここまで書いてなんですが、ntpd が無くなったわけではないので、そちらの方が良ければ、入れれば良いです。
[root@fedora16 ~]# yum search ntp | grep ^ntp.x86_64
ntp.x86_64 : The NTP daemon and utilities

2014-05-05追記
RHEL7.0 RC を試しましたが、デフォルトの NTP クライアントは、やはり chrony に変更になっていました。設定/操作方法は、Fedora 16 で調べた時と特段変わりないようです。それと、やはり ntpd も収録されているので、そちらを使うことも可能でした。

2015-02-01追記
追記していませんでしたが、RHEL7/CentOS7 では、デフォルトの NTP クライアントは、chrony でリリースされています。
殆どの場合、/etc/chronyd.conf 中の server 行を修正して、systemctl restart chrony.service を実行するだけで良いかと思います。6ヶ月以上動かしているマシンがありますが、chrony に関して特段不都合は無い感触です。
なお、ntpd に切り替えることもできるので、これまでの運用経験値・実績を重視するのであれば、切り替える選択肢もあります。当然ではありますが、ネット上の情報量は ntpd のほうが多いというメリットもあります。

Fedora 16 Alpha で GRUB2 をパーティションにインストール

Fedora 16 Alpha を試そうと、マルチブート環境 (マシンは ThinkPad X301) のパーティションの1つにインストールしましたが、インストールの終盤で bootloader のインストールに失敗したというエラーが出て、起動できず、いきなりハマってしまいました。
バグ情報を見たら、bootloader をパーティションにインストールできないよ とありました。

http://fedoraproject.org/wiki/Common_F16_bugs
Installation issues

Installer cannot install bootloader to a system partition (but will try and fail)
link to this item - Bugzilla: #730915

Fedora 16 uses the GRUB 2 bootloader for new installations. GRUB 2 does not currently support installation to a partition (rather than the MBR), but the Fedora 16 Alpha installer will still allow you to try and install the bootloader to a partition. This will fail, and so if you attempt this, the Fedora 16 Alpha installer will not install a bootloader at all. The installed system will be functional.

To work around this issue, you can adjust your bootloader configuration from another installed operating system. You can force installation of GRUB 2 to the Fedora partition by booting the Fedora 16 Alpha installation disc in rescue mode, using chroot to access the installed system, and running grub2-install --force '(hd0,5)' (where hd0,5 is the appropriate designation for the target partition, with GRUB 2 partition numbering starting at 1 as opposed to 0 with old GRUB). Alternatively, specify an ordinary partition device name such as /dev/sda5. Note that with --force the warnings are still printed.

This issue will be resolved in Fedora 16 Beta: the installer will use the --force parameter when trying to install the bootloader to a partition.
わたしのマルチブート環境では、MBR には MBM を入れてあり、Fedora や CentOS のインストール時には、各パーティションの PBR に GRUB を入れるという構成にしています。GRUB2 の場合、PBR へのインストール時には --force オプションを指定する必要があるようですが、F16 Alpha のインストーラーが --force をつけていないために、失敗してしまうようです。

仕方が無いので、レスキューモードでなんとかなるだろうと、ほぼ初体験の GRUB2 で悪戦苦闘。GRUB1 とほぼ同じだろうと、grub2-install をかけるも NG。--force をつけても NG。その後、grub2-各種 の --help を見ながら格闘しましたが、レスキューモードではうまく行きませんでした。
そんじゃあ別のパーティションに入っている Fedora 15 に GRUB2 を入れてなんとかできないかと、さらに格闘すること1時間ぐらい、やっと成功しました。
Fedora 15 で使ったコマンドは、次のような感じです。今回、/dev/sda8 に Fedora 16 を入れています。
# mkdir /mnt_sda8
# mount /dev/sda8 /mnt_sda8
# grub2-install --force --root-directory=/mnt_sda8 /dev/sda8
# chroot /mnt_sda8
# grub2-mkconfig > /boot/grub2/grub.cfg
GRUB2 の設定ファイル grub.cfg は手書きではないようで、上記のように grub2-mkconfig で生成するという方式になっているようです。

最後にスクリーンショットでもと思ったのですが、PNG ファイルをセーブするときにクラッシュしてしまうようなので、ここまでです。なお、現在の Fedora (というか GNOME?) だと、PrtSc キー押下でスクリーンショットのキャプチャーが出来ます。つまり、Windows と同じ操作方法ということ。

2011-10-01追記
アップデートしたら、スクリーンショットが落ちなくなったので、いくつかアップします。



2011年9月1日木曜日

perl でオプション解析(getoptコマンド編)

perl から、getopt(1) コマンドを呼び出してオプション解析するテンプレートです。
#!/usr/bin/perl
#
# getopt-cmd-template.pl
#

use Text::ParseWords ;

sub usage_exit {
        print STDERR "Usage: getopt-cmd-template.pl [-a] [-d dir] item1 item2 ...\n" ;
        exit 1 ;
}

sub get_quotedwordstr {
        my $s ;
        foreach (@_) {
                $s = sprintf("$s '%s'", "$_") ;
        }
        return $s ;
}

print "@ARGV\n" ;       ####DEBUG
$argstr = get_quotedwordstr(@ARGV) ;
print "$argstr\n" ;     ####DEBUG
$optstr = `getopt -q -o ad:h -- $argstr` ; if ($? != 0) { usage_exit ; }
@opts = shellwords($optstr) ;
while (1) {
        $_ = shift @opts ;
        m/^$/  && do { next ; } ;
        m/^-a/ && do { $opt_a = 1           ; next ; } ;
        m/^-d/ && do { $opt_d = shift @opts ; next ; } ;
        m/^--/ && do { last ; } ;
        usage_exit ;
}

$argc = scalar @opts ;
print "\$argc=$argc\n" ;        ####DEBUG
print "\@opts=@opts\n" ;        ####DEBUG
print "\$opt_a=$opt_a\n" ;      ####DEBUG
print "\$opt_d=$opt_d\n" ;      ####DEBUG
ひと手間かかった部分だけ補足しますと、

空白を含むような item にも対応できるように、get_quotedwordstr を記述しています。

また、getopt(1) の処理結果をうまく単語分割して配列に格納するため、Text::ParseWords に含まれる shellwords を使いました。これの存在に気づくまで時間がかかりました。

実行結果は、次のようになります。
# ./getopt-cmd-template.pl -d dir item1 item2
-d dir item1 item2
 '-d' 'dir' 'item1' 'item2'
$argc=2
@opts=item1 item2
$opt_a=
$opt_d=dir
コマンドラインの後半でオプション指定しても、うまく処理されます。
# ./getopt-cmd-template.pl item1 item2 -d dir
item1 item2 -d dir
 'item1' 'item2' '-d' 'dir'
$argc=2
@opts=item1 item2
$opt_a=
$opt_d=dir
Getopt::Std では、このように、うまく処理はできません。次の記事を参照ください。
perl でオプション解析(Getopt::Std編)

2011年8月31日水曜日

perl でオプション解析(Getopt::Std編)

以前、bash スクリプトでオプション解析をする際のテンプレートを書きましたが、perl についても、自分用にテンプレート化してみました。
#!/usr/bin/perl
#
# getopts-template.pl
#

use Getopt::Std ;

sub usage_exit {
        print STDERR "Usage: getopts-template.pl [-a] [-d dir] item1 item2 ...\n" ;
        exit 1 ;
}

print "@ARGV\n" ;       ####DEBUG
{
        local $SIG{__WARN__} = \&usage_exit ;
        if (getopts("ad:h") != 1) {
                usage_exit ;
        }
}

if (defined $opt_h) {
        usage_exit ;
}

$argc = scalar @ARGV ;
print "\$argc=$argc\n" ;        ####DEBUG
print "\@ARGV=@ARGV\n" ;        ####DEBUG
print "\$opt_a=$opt_a\n" ;      ####DEBUG
print "\$opt_d=$opt_d\n" ;      ####DEBUG
要所だけ補足しますと、

__WARN__ の行を書かない場合、未定義オプションを指定すると、Getopt::Std が自分でエラーメッセージ(Unknown option: x)を出してしまいます。
わたしは、この振る舞いは嫌なので、自分で用意した usage_exit を呼ぶようにしました。

もう1点、引数が必要なオプション -d dir について、dir が省略された場合にエラーにするには、getopts の戻り値を見る必要があります。getopts は、エラーが生じると、1 以外の値を返します。

実行結果は、次のようになります。
# ./getopts-template.pl -d dir item1 item2
-d dir item1 item2
$argc=2
@ARGV=item1 item2
$opt_a=
$opt_d=dir
bash の getopts と同じ振る舞いのようで、コマンドラインの後半でオプション指定を書くと、うまく処理できません。うーん、残念です。
# ./getopts-template.pl item1 item2 -d dir
item1 item2 -d dir
$argc=4
@ARGV=item1 item2 -d dir
$opt_a=
$opt_d=

2011-09-01追記
getopt(1) を使うテンプレートも作りました。次の記事も参照ください。
perl でオプション解析(getoptコマンド編)

2012-02-11追記
getopts の場合は、-ll のような複数回の指定を区別できないことを知りました。getopt(1) を使う方法ならば対応できますが、コードが長くなるのがいまひとつ。Getopt::Long を使うと、また、別のイライラに遭遇するようです。。。

2017-01-30追記
久々に、このパターンを CentOS 7 で使ったのですが、何やらごちゃごちゃと余計なメッセージが出ました。
[root@hoge tmp]# ./getopts-template.pl --help           
--help
./getopts-template.pl version [unknown] calling Getopt::Std::getopts (version 1.07 [paranoid]),
running under Perl version 5.16.3.

Usage: getopts-template.pl [-OPTIONS [-MORE_OPTIONS]] [--] [PROGRAM_ARG1 ...]

The following single-character options are accepted:
 With arguments: -d
 Boolean (without arguments): -a -h

Options may be merged together.  -- stops processing of options.
Space is not required between options and their arguments.
  [Now continuing due to backward compatibility and excessive paranoia.
   See 'perldoc Getopt::Std' about $Getopt::Std::STANDARD_HELP_VERSION.]
$argc=0
@ARGV=
$opt_a=
$opt_d=
[root@hoge tmp]# 
これはちょっと余計なお世話と思って、調べたところ、次のように追加の記述を行うことで、出なくなりました。
[root@hoge tmp]# diff -u getopts-template.pl getopts-template.pl-2017-01-30 
--- getopts-template.pl 2017-01-30 22:00:17.954554580 +0900
+++ getopts-template.pl-2017-01-30 2017-01-30 22:02:00.828239598 +0900
@@ -11,6 +11,8 @@
 }
 
 print "@ARGV\n" ; ####DEBUG
+sub HELP_MESSAGE { usage_exit ; }
+sub VERSION_MESSAGE { ; }
 {
  local $SIG{__WARN__} = \&usage_exit ;
  if (getopts("ad:h") != 1) {
[root@hoge tmp]# ./getopts-template.pl-2017-01-30 --help
--help
Usage: getopts-template.pl [-a] [-d dir] item1 item2 ...
[root@hoge tmp]# 
Simple is best. ですね。

2011年8月21日日曜日

シェルスクリプトで排他制御(flock)

シェルスクリプトの一部を排他制御する必要があり、調べたところ、CentOS 5 以降なら、util-linux に含まれる flock コマンドを使えることがわかりました。オンラインマニュアルに典型的な使い方が書いてあり、これは、なかなか素晴らしいと思いました。最初に考えた人は、すごいなと思います。オンラインマニュアルからの抜粋です。
FLOCK(1)                              H. Peter Anvin                              FLOCK(1)

NAME
       flock - Manage locks from shell scripts

SYNOPSIS
       flock [-sxon] [-w timeout] lockfile [-c] command...

       flock [-sxon] [-w timeout] lockdir [-c] command...

       flock [-sxun] [-w timeout] fd

DESCRIPTION
       This utility manages flock(2) locks from within shell scripts or the command line.

       The first and second forms wraps the lock around the executing a command, in a man・
       er similar to su(1) or newgrp(1).  It locks a specified file or  directory,  which
       is created (assuming appropriate permissions), if it does not already exist.

       The  third form is convenient inside shell scripts, and is usually used the follow・
       ng manner:

       (
         flock -s 200
         # ... commands executed under lock ...
       ) 200>/var/lock/mylockfile
...
オンラインマニュアルの例では、サブシェルを起動していますが、次のようにすれば、シェルスクリプト全体をシリアライズすることも可能です。
#!/bin/bash

exec 9>>/var/lock/mylockfile

flock -n 9
if [ $? -ne 0 ] ; then
    # ロックできなければ、失敗(exit 1)させるかどうかは、処理の内容次第です。
    # ロック解放を待ち合わせるのなら、-n オプションを付けなければ良い。
fi

#
# 排他の必要な処理を記述。
#

flock -u 9  # スクリプトに対応するプロセスが終了すれば、
            # カーネルが自動的にロック解放しますが、明示的に記述する。
exit 0
exec 9>>/var/lock/mylockfile という書き方をすると、/var/lock/mylockfile を追記モードで open して、その fd を 9 番に割り当てます。シェル内部では、dup2(2) が使われます。

CentOS 4 以下では、flock コマンドはまだ提供されておらず、lockfile という似たコマンドがあるようです。しかし、lockfile のほうは、原始的なテクニックが使われており、flock の方が信頼性は高いと思われます。

2011-09-04追加
flock コマンドは、flock(2) システムコールに依存しており、NFS では動作しないので注意が必要なようです。オンラインマニュアル (man 2 flock) から抜粋です。
2011-09-08 実験してみたところでは動作しました。追記内容を参照してください。
NOTES
       flock(2)  does  not  lock files over NFS.  Use fcntl(2) instead: that does
       work over NFS, given a sufficiently recent version of Linux and  a  server
       which supports locking.
flock はファイルシステム毎に実装 (struct file operations の flock メンバ) することができ、確かに NFS では、次のように定義されていました。
以下、2.6.18-194.el5 (CentOS 5.5) から抜粋です。
     54 const struct file_operations nfs_file_operations = {
     55         .llseek         = nfs_file_llseek,
     56         .read           = do_sync_read,
     57         .write          = do_sync_write,
     58         .aio_read               = nfs_file_read,
     59         .aio_write              = nfs_file_write,
     60         .mmap           = nfs_file_mmap,
     61         .open           = nfs_file_open,
     62         .flush          = nfs_file_flush,
     63         .release        = nfs_file_release,
     64         .fsync          = nfs_fsync,
     65         .lock           = nfs_lock,
     66         .flock          = nfs_flock,
     67         .sendfile       = nfs_file_sendfile,
     68         .check_flags    = nfs_check_flags,
     69 };
...
    639 /*
    640  * Lock a (portion of) a file
    641  */
    642 static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
    643 {
    644         dprintk("NFS: nfs_flock(f=%s/%ld, t=%x, fl=%x)\n",
    645                         filp->f_dentry->d_inode->i_sb->s_id,
    646                         filp->f_dentry->d_inode->i_ino,
    647                         fl->fl_type, fl->fl_flags);
    648 
    649         /*
    650          * No BSD flocks over NFS allowed.
    651          * Note: we could try to fake a POSIX lock request here by
    652          * using ((u32) filp | 0x80000000) or some such as the pid.
    653          * Not sure whether that would be unique, though, or whether
    654          * that would break in other places.
    655          */
    656         if (!(fl->fl_flags & FL_FLOCK))
    657                 return -ENOLCK;
    658 
"fs/nfs/file.c"
flock(2) システムコールのコードは次のようになっています。
   1590 /**
   1591  *      sys_flock: - flock() system call.
   1592  *      @fd: the file descriptor to lock.
   1593  *      @cmd: the type of lock to apply.
   1594  *
   1595  *      Apply a %FL_FLOCK style lock to an open file descriptor.
   1596  *      The @cmd can be one of
   1597  *
   1598  *      %LOCK_SH -- a shared lock.
   1599  *
   1600  *      %LOCK_EX -- an exclusive lock.
   1601  *
   1602  *      %LOCK_UN -- remove an existing lock.
   1603  *
   1604  *      %LOCK_MAND -- a `mandatory' flock.  This exists to emulate Windows Share Modes.
   1605  *
   1606  *      %LOCK_MAND can be combined with %LOCK_READ or %LOCK_WRITE to allow other
   1607  *      processes read and write access respectively.
   1608  */
   1609 asmlinkage long sys_flock(unsigned int fd, unsigned int cmd)
   1610 {
   1611         struct file *filp;
   1612         struct file_lock *lock;
   1613         int can_sleep, unlock;
   1614         int error;
   1615 
   1616         error = -EBADF;
   1617         filp = fget(fd);
   1618         if (!filp)
   1619                 goto out;
   1620 
   1621         can_sleep = !(cmd & LOCK_NB);
   1622         cmd &= ~LOCK_NB;
   1623         unlock = (cmd == LOCK_UN);
   1624 
   1625         if (!unlock && !(cmd & LOCK_MAND) && !(filp->f_mode & 3))
   1626                 goto out_putf;
   1627 
   1628         error = flock_make_lock(filp, &lock, cmd);
   1629         if (error)
   1630                 goto out_putf;
   1631         if (can_sleep)
   1632                 lock->fl_flags |= FL_SLEEP;   1633 
   1634         error = security_file_lock(filp, cmd);
   1635         if (error)
   1636                 goto out_free;
   1637 
   1638         if (filp->f_op && filp->f_op->flock)
   1639                 error = filp->f_op->flock(filp,
   1640                                           (can_sleep) ? F_SETLKW : F_SETLK,
   1641                                           lock);
   1642         else
   1643                 error = flock_lock_file_wait(filp, lock);
...
"fs/locks.c"
NFS の場合は、f_op->flock が定義されているので、1639行目が実行され、最終的に ENOLOCK エラーが返却されることになるようです。2011-09-08 読み誤っていましたので、追記内容を参照してください。
ext3 等の場合は、f_op->flock が未定義なので、1643行目が実行されます。
なお、lock->fl_flags に FL_FLOCK を設定しているのは、1628行目です。
    278 /* Fill in a file_lock structure with an appropriate FLOCK lock. */
    279 static int flock_make_lock(struct file *filp, struct file_lock **lock,
    280                 unsigned int cmd)
    281 {
    282         struct file_lock *fl;
    283         int type = flock_translate_cmd(cmd);
    284         if (type < 0)
    285                 return type;
    286        
    287         fl = locks_alloc_lock();
    288         if (fl == NULL)
    289                 return -ENOMEM;
    290 
    291         fl->fl_file = filp;
    292         fl->fl_pid = current->tgid;
    293         fl->fl_flags = FL_FLOCK;
    294         fl->fl_type = type;
    295         fl->fl_end = OFFSET_MAX;
    296        
    297         *lock = fl;
    298         return 0;
    299 }
"fs/locks.c"

2011-09-08追記
fs/nfs/file.c の 656 行目を条件を読み間違えていました。ここは、FL_FLOCK が設定されていないのに、ここに来た場合に ENOLCK を返すというだけでした。
それで、続きを読んでみると、POSIX のロックを使ってシミュレートするよ とあります。
    639 /*
    640  * Lock a (portion of) a file
    641  */
    642 static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
    643 {
    644         dprintk("NFS: nfs_flock(f=%s/%ld, t=%x, fl=%x)\n",
    645                         filp->f_dentry->d_inode->i_sb->s_id,
    646                         filp->f_dentry->d_inode->i_ino,
    647                         fl->fl_type, fl->fl_flags);
    648 
    649         /*
    650          * No BSD flocks over NFS allowed.
    651          * Note: we could try to fake a POSIX lock request here by
    652          * using ((u32) filp | 0x80000000) or some such as the pid.
    653          * Not sure whether that would be unique, though, or whether
    654          * that would break in other places.
    655          */
    656         if (!(fl->fl_flags & FL_FLOCK))
    657                 return -ENOLCK;
    658 
    659         /* We're simulating flock() locks using posix locks on the server */
    660         fl->fl_owner = (fl_owner_t)filp;
    661         fl->fl_start = 0;
    662         fl->fl_end = OFFSET_MAX;
    663 
    664         if (fl->fl_type == F_UNLCK)
    665                 return do_unlk(filp, cmd, fl);
    666         return do_setlk(filp, cmd, fl);
    667 }
"fs/nfs/file.c"
それじゃあ、flock コマンドが NFS を越えて正しく動作するのかというと、POSIX ロックと BSD ロック (flock) の違い (POSIX ロックは fork を越えられない) により正しく動作しないのでは? っと思い、テストプログラムで確かめてみました。CentOS 5 で確認しています。
#!/bin/bash

exec 9>>./flock.test.lock

flock -n 9
if [ $? -ne 0 ] ; then
        echo "fail" 1>&2
        exit 1
fi

sleep 30

flock -u 9
exit 0
# df -TP .
Filesystem    Type 1024-blocks      Used Available Capacity Mounted on
xx.xx.xx.xx:/ nfs   5921856   5455872    165176      98% /mnt_nfs
# /tmp/flock.sh & /tmp/flock.sh 
[1] 6452
fail
2つ同時起動で、2つ目が失敗してますから、ちゃんとロック出来ているようです。
どうなっているのか理解出来ていませんが、NFS の開発者の方は、flock も視野に実装してくれたということでしょうかね。

2011-09-09追記
CentOS 6 を NFS サーバにして、flock を試すとうまく行かず、次のようなエラーになってしまいました。クライアントは CentOS 5 です。
# ./flock.sh 
flock: 9: No locks available
fail
サーバ側を見てみると、次のエラーが出ていました。
Sep  8 23:39:14 centos6 rpc.statd[6061]: No canonical hostname found for xx.xx.xx.xx
Sep  8 23:39:14 centos6 rpc.statd[6061]: STAT_FAIL to centos6 for SM_MON of xx.xx.xx.xx
Sep  8 23:39:14 centos6 kernel: lockd: cannot monitor xx.xx.xx.xx
調べてみると、次の情報が見つかりました。

http://dfwarden.blogspot.com/2011/02/psa-nfs-locking-in-rhel6-needs-reverse.html

確かに、サーバ側 (CentOS 6) の /etc/hosts に、クライアントの IP アドレスを登録したら、flock 出来るようになりました。信頼性・セキュリティの強化が図られているということでしょうかね。

なお、lockd: cannot monitor のメッセージは、クライアント側で nfslock サービスが上がっていない場合にも出るメッセージなようです。メモ。

2011年8月14日日曜日

Fedora 15 のカーネルを Core2 に最適化する

Linux 3.0 のビルド方法 を書きましたが、もう1歩進んで、カーネルのカスタマイズをしてみます。

初歩的なターゲットと思いますが、、、
Fedora 等のディストリビューションでは、古いマシンでも動くように、CPU 種類毎の最適化は行われないようです。CONFIG_GENERIC_CPU=y になっています。
# grep CONFIG_GENERIC_CPU /boot/config-2.6.40-4.fc15.x86_64 
CONFIG_GENERIC_CPU=y
わたしが今使っている ThinkPad X301 は、Core2 が載っているので、Core2 に最適化してみました。
# make menuconfig
CUI画面になるので、
Processor type and features
  ---> Processor family (Core 2/newer Xeon)
    ---> (X) Core 2/newer Xeon
を選択する。
menuconfig の操作方法は、いたって簡単で、矢印キーで移動、リターンで選択、ESC 2回でサブメニューを抜ける。というものです。
あとは、Fedora 15 で Linux 3.0 の手順(6)以降を行います。

最適化前後で、GRUB からログイン画面までの起動時間を比べてみましたが、、、
残念ながら、わたしの環境では、ほとんど違いはありませんでした。

2011年8月7日日曜日

CentOS 6.0 で NIC と ethX の対応を固定化する

CentOS 6.0 で NIC 交換 (オンボードの場合はマザーボード交換も含む) した場合でも、ethX が変化しないように、固定化する方法です。RHEL6 でも同様にして可能。
2015-01-18追記、CentOS7/RHEL7についてはこちらを参照。

CentOS 6.0 のデフォルトでは、MAC アドレスと ethX を対応付けており、交換により MAC が変化すると、自動的に新たな対応関係が作られるようになっているため、ethX がずれてしまいます。対応関係は、/etc/udev/rules.d/70-persistent-net.rules に設定されます。
このあたりについては、前に書いた記事を参照してください。

CentOS 6.0 で NIC 交換した場合の挙動
CentOS 6.0 で NIC と ethX の対応を変更する

以上が基礎知識ですが、まずは、この 70-persistent-net.rules を無効化します。
少々、荒っぽいやり方ですが、ルールが自動生成されないように、/dev/null へシンボリックリンクを張ってしまいます。
[root@centos6 ~]# ln -s -f /dev/null /etc/udev/rules.d/70-persistent-net.rules
[root@centos6 ~]# ls -l /etc/udev/rules.d/70-persistent-net.rules
lrwxrwxrwx 1 root root 9 Aug  7 12:07 /etc/udev/rules.d/70-persistent-net.rules -> /dev/null
このとき messages に、次のメッセージが出ますが、一時的なものなので、気にする必要はありません。
Aug  7 12:07:22 centos6 udevd[406]: can not read '/etc/udev/rules.d/70-persistent-net.rules'

次に、/etc/udev/rules.d/65-eth.rules ファイルを新規作成して、次のように記載します。
ACTION=="add", KERNEL=="eth*", ID=="0000:02:01.0", DRIVERS=="?*", ATTR{type}=="1", NAME="eth2", OPTIONS="last_rule"
ACTION=="add", KERNEL=="eth*", ID=="0000:02:04.0", DRIVERS=="?*", ATTR{type}=="1", NAME="eth3", OPTIONS="last_rule"
ここで、0000:02:01.0 や 0000:02:04.0 は PCI アドレスです。lspci で表示される値に、0000: (domain と呼ばれる管理番号ですが、普通のマシンは 0000 です) をつけた値です。この PCI アドレスは、ethtool コマンドで確認することもできます。
[root@centos6 ~]# ethtool -i eth2
driver: e1000
version: 7.3.21-k6-NAPI
firmware-version: N/A
bus-info: 0000:02:01.0
[root@centos6 ~]# ethtool -i eth3
driver: e1000
version: 7.3.21-k6-NAPI
firmware-version: N/A
bus-info: 0000:02:04.0
書き忘れそうでしたが、この方法を用いる場合は、/etc/sysconfig/network-scripts/ifcfg-ethX の HWADDR= は設定しないほうが良いです。設定した場合は、MAC のチェックが行われ、一致しなければ ifup が失敗します。

最後に、再起動します。設定内容によっては、立ち上げ時に、ethX の入れ替えを行ったことを示すメッセージが出力されます。
Aug  7 12:45:05 centos6 kernel: udev: renamed network interface eth0 to eth2
Aug  7 12:45:05 centos6 kernel: udev: renamed network interface eth1 to eth3
この環境では、NIC は 2本 (eth0, eth1) なのですが、ここで紹介した PCI アドレスによる固定化方法により、eth2 および eth3 に変更しています。

udev に関して、次の記事を一読されると理解が深まるものと思います。

参考URL:
http://gihyo.jp/dev/serial/01/sc-literacy/0015?page=1
http://donko.jp/LFS/LFS6.4jp/chapter07/network.html

2011-08-12追記
Fedora 15 では、biosdevname が使えるとか。参考メモ。
http://ja.community.dell.com/techcenter/b/weblog/archive/2011/08/03/fedora-15.aspx
2011-12-10追記、RHEL6.1 以降でもbiosdevnameが利用できるようです。DellマシンではデフォルトON、その他ベンダではデフォルトOFF。ブートパラメータとしてbiosdevname={0|1} を渡すことで切り替えも可能。HW が対応している必要があるので、事前に biosdevname コマンド(-i ethXX オプション)で名前解決可能かどうか確かめたほうがいいです。ただ、実際に試してみると、em1 や p1p1 みたいなインタフェース名って気色悪く感じました。あと、おそらく ethXX を決め打ちにしているアプリケーションがありそうなので、まだまだ業務サーバで利用はできないと思います。きっと普及までには5年ぐらいの歳月がかかるのではないかと思う。
なお、ブートパラメータとして指定する biosdevname=1 は、カーネルが解釈するわけではありません。Fedora 15 以降 または RHEL6.1 以降の環境で、/lib/udev/rules.d/71-biosdevname.rules を参照してください。この中から /proc/cmdline を読み取るという仕掛けのようです。/proc/cmdline を伝言板のように使うこの実装は醜いと思うんですがね。


2011-08-17追記
Fedora 15 で、上に書いた 65-eth.rules を使うと、ID= は将来の udev で廃止になるから使うなというようなメッセージが出るようです。
Aug 18 00:20:35 fedora15 udevd[358]: ID= will be removed in a future udev version, please use KERNEL= to match the event device, or KERNELS= to match a parent device, in /etc/udev/rules.d/65-eth.rules:2
そこで、別解です。次のルールであれば、Fedora 15 でも CentOS 6.0 でも使えるようです。
ACTION=="add", SUBSYSTEMS=="pci", KERNELS=="0000:02:01.0", DRIVERS=="?*", NAME="eth2", OPTIONS="last_rule"
ACTION=="add", SUBSYSTEMS=="pci", KERNELS=="0000:02:04.0", DRIVERS=="?*", NAME="eth3", OPTIONS="last_rule"
udev ルールに使える attribute は、次のようにして確認できます。
[root@centos6 ~]# udevadm info -a -p /sys/class/net/eth2

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/pci0000:00/0000:00:11.0/0000:02:01.0/net/eth2':
    KERNEL=="eth2"
    SUBSYSTEM=="net"
    DRIVER==""
    ATTR{addr_assign_type}=="0"
    ATTR{addr_len}=="6"
    ATTR{dev_id}=="0x0"
    ATTR{ifalias}==""
    ATTR{iflink}=="2"
    ATTR{ifindex}=="2"
    ATTR{features}=="0x10ba9"
    ATTR{type}=="1"
    ATTR{link_mode}=="0"
    ATTR{address}=="00:0c:29:xx:xx:xx"
    ATTR{broadcast}=="ff:ff:ff:ff:ff:ff"
    ATTR{carrier}=="1"
    ATTR{speed}=="1000"
    ATTR{duplex}=="full"
    ATTR{dormant}=="0"
    ATTR{operstate}=="up"
    ATTR{mtu}=="1500"
    ATTR{flags}=="0x1003"
    ATTR{tx_queue_len}=="1000"

  looking at parent device '/devices/pci0000:00/0000:00:11.0/0000:02:01.0':
    KERNELS=="0000:02:01.0"
    SUBSYSTEMS=="pci"
    DRIVERS=="e1000"
    ATTRS{vendor}=="0x8086"
    ATTRS{device}=="0x100f"
    ATTRS{subsystem_vendor}=="0x15ad"
    ATTRS{subsystem_device}=="0x0750"
    ATTRS{class}=="0x020000"
    ATTRS{irq}=="19"
    ATTRS{local_cpus}=="3"
    ATTRS{local_cpulist}=="0-1"
    ATTRS{modalias}=="pci:v00008086d0000100Fsv000015ADsd00000750bc02sc00i00"
    ATTRS{numa_node}=="-1"
    ATTRS{enable}=="1"
    ATTRS{broken_parity_status}=="0"
    ATTRS{msi_bus}==""

  looking at parent device '/devices/pci0000:00/0000:00:11.0':
    KERNELS=="0000:00:11.0"
    SUBSYSTEMS=="pci"
    DRIVERS==""
    ATTRS{vendor}=="0x15ad"
    ATTRS{device}=="0x0790"
    ATTRS{subsystem_vendor}=="0x0000"
    ATTRS{subsystem_device}=="0x0000"
    ATTRS{class}=="0x060401"
    ATTRS{irq}=="0"
    ATTRS{local_cpus}=="3"
    ATTRS{local_cpulist}=="0-1"
    ATTRS{modalias}=="pci:v000015ADd00000790sv00000000sd00000000bc06sc04i01"
    ATTRS{numa_node}=="-1"
    ATTRS{enable}=="1"
    ATTRS{broken_parity_status}=="0"
    ATTRS{msi_bus}=="1"

  looking at parent device '/devices/pci0000:00':
    KERNELS=="pci0000:00"
    SUBSYSTEMS==""
    DRIVERS==""

2015-01-16追記
RHEL7/CentOS7 の場合について、別の記事を書きました。

2011年8月6日土曜日

CentOS の bonding の arp_validate オプション

CentOS 5 および CentOS 6 の bonding で ARP 監視を行う場合、arp_validate というオプションを利用できます。
以下、その処理内容についてのメモです。

 Bladeサーバ            Blade収納ユニット
┌──────────┐──────────────┐
│              active│          ┌───┐Blade   │   Blade外部switch
│    ┌──┐  ┌──┤          │      │内部switch  ┌───┐
│    │    ├─┤eth0├─────┤ SW1  ├─────┤      │        
│    │    │  └──┤→…………………………………………┐    │  ┌───┐
│    bond0 │        │ARP要求   └───┘        │  │SW3 │  arp_ip_target
│    │192.168.50.111│broadcast ┌───┐        │  │    ├─┤192.168.50.222
│    │    │  ┌──┤←…………………………………………┴………→│      │
│    │    ├─┤eth1├─────┤ SW2  ├──────┤      │  └───┘
│    └──┘  └──┤          │      │        │  └───┘
│              backup│          └───┘        │
└──────────┘──────────────┘

Bladeサーバの場合、上図のように、収納ユニット内部にスイッチ (SW1 および SW2) が実装される構造になっていることがあります。この場合、部分の障害を検出できるように ARP 監視を用いることができます。
注記:内部スイッチが、部分のリンク障害時に、eth0---SW1 間のリンクを無効化するという機能(UFD: Uplink Failure Detection)を備えている場合があります。機能があるなら、併用するのがいいのではと思います。スイッチの障害対応機能が完璧に動くかどうか?という側面もあるため、スイッチ機能+MII 監視ではなく、スイッチ機能+ARP 監視が良いものと思います。
ただし、ARP 監視のデフォルト動作 (arp_validate=0) では、受信カウンタのチェックのみ行われるため、隣のBladeサーバが同様に ARP 監視 (ARP要求のbroadcast送信) を行ってしまうと、正しく障害検知出来なくなります。

arp_validate=3 を設定すると、受信した ARP パケットの正当性を確認する処理が動くようになります。active (上図eth0) では、arp_ip_target からの ARP 応答を受信したかどうかの確認が行われるようになります。一方、わかりにくいところですが、backup (上図eth1) では、active (上図eth0) から送信された ARP 要求 (broadcast) を受信したかどうかの確認が行われるようになります。ドライバソース上、この ARP 確認処理の核心部分は、drivers/net/bonding/bond_main.c の次の箇所です。
   2741 static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
   2742 {
...
   2789         /*
   2790          * Backup slaves won't see the ARP reply, but do come through
   2791          * here for each ARP probe (so we swap the sip/tip to validate
   2792          * the probe).  In a "redundant switch, common router" type of
   2793          * configuration, the ARP probe will (hopefully) travel from
   2794          * the active, through one switch, the router, then the other
   2795          * switch before reaching the backup.
   2796          */
   2797         if (slave->state == BOND_STATE_ACTIVE)
   2798                 bond_validate_arp(bond, slave, sip, tip);
   2799         else
   2800                 bond_validate_arp(bond, slave, tip, sip);
   2801
...
"drivers/net/bonding/bond_main.c"
2798行目が、active 側の確認 (arp_ip_target からの ARP 応答であるか?) です。
2800行目が、backup 側の確認 (arp_ip_target に対する ARP 要求であるか?) です。
掲載したソースは、2.6.18-164.el5 (CentOS 5.5 のカーネル) のものですが、2.6.32-71.el6 (CentOS 6.0 のカーネル) でも、全く同じです。

最後に、この記事の図に対応する設定例サンプル (/etc/sysconfig/network-scripts/ifcfg-bond0, ifcfg-eth0, ifcfg-eth1) を載せます。
#
# network-scripts/ifcfg-bond0
#
DEVICE=bond0
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.50.111
NETMASK=255.255.255.0
GATEWAY=192.168.50.254
BONDING_OPTS="mode=1 arp_interval=1200 arp_ip_target=192.168.50.222 arp_validate=3"
USERCTL=no
NM_CONTROLLED=no
arp_interval=1200 ミリ秒を勧めます。sysctl の net.ipv4.neigh.default.locktime (単位10ミリ秒) よりも長い値。
#
# network-scripts/ifcfg-eth0
#
DEVICE=eth0
MASTER=bond0
SLAVE=yes
BOOTPROTO=none
HWADDR=00:0C:29:xx:xx:xx
USERCTL=no
NM_CONTROLLED=no
#
# network-scripts/ifcfg-eth1
#
DEVICE=eth1
MASTER=bond0
SLAVE=yes
BOOTPROTO=none
HWADDR=00:0C:29:yy:yy:yy
USERCTL=no
NM_CONTROLLED=no

2011-08-08追記
どうやら Internet Explorer だと、テキストで書いた図が歪むようですので、PNG を貼ります。Chrome と Firefox ではきれいに見えます。IE の方は試してみては。

2011年8月5日金曜日

Fedora 15 で Linux 3.0

Fedora 15 の最新カーネル 2.6.40-4.fc15 は、実質は Linux 3.0 です。
rpm の changelog に次のようにあります。
# rpm -q --changelog kernel-2.6.40-4.fc15 | head -14
* Fri Jul 29 2011 Dave Jones  2.6.40-4
- Re-add utrace, which got accidentally dropped during the rebase.

* Thu Jul 28 2011 Dave Jones  2.6.40-3
- Fix module-init-tools conflict:

* Thu Jul 28 2011 Dave Jones  2.6.40-2
- fix crash in scsi_dispatch_cmd()

* Thu Jul 28 2011 Dave Jones  2.6.40-1
- Turn off debugging options. (make release)

* Tue Jul 26 2011 Dave Jones  2.6.40-0
- Rebase to final 3.0 (munge to 2.6.40-0)
おそらく、カーネルのバージョンにより処理内容を変えるアプリケーションへの配慮のためか、2.6.40 ということにしてあります。
以下、この 2.6.40-4.fc15 に含まれる config を使って、Linux 3.0 をビルドする手順です。バージョン表示だけのことではありますが、新鮮な気分を味わえます。

(1) Fedora 15 の動作環境を用意して、カーネル 2.6.40-4.fc15 までアップデートする。ただし、ビルドのためには、空き領域が 8GB 程度必要です。

(2) 開発用ツールをインストールする。
# yum groupinstall "Development Tools"

(3) http://www.kernel.org/ から linux-3.0.tar.bz2 を入手して展開する。
# cd /usr/src  ※これは例です。ほかの場所でも良い
# tar xf /tmp/linux-3.0.tar.bz2

(4) Makefile の EXTRAVERSION を設定する。
VERSION = 3
PATCHLEVEL = 0
SUBLEVEL = 0
EXTRAVERSION = -0.my15.x86_64  ※これは例です。自分の好みの文字列を指定すれば良い
...

(5) 2.6.40-4.fc15 の config をコピーする。
# cd /usr/src/linux-3.0
# cp /boot/config-2.6.40-4.fc15.x86_64 .config

(6) ビルドを実行する。もしも、2.6.38.8-35.fc15 等の少し古い config を用いた場合、新しく増えたオプションをどう設定するか問い合わせて来ます。2.6.40-4.fc15 であれば、オプション集合が一致しているので、何も聞かれることはなく、そのままコンパイルが進行します。
# make HOSTCXX="ccache g++" CC="ccache gcc"

(7) 出来上がったモジュールとカーネルをインストールする。
# make modules_install
# make install

(8) grub.conf の default を修正して再起動する。または、再起動時に GRUB メニューから作成した Linux 3.0 のエントリーを選択して起動する。

(9) uname -a で、カーネルバージョンを確認する!
# uname -a
Linux fedora15 3.0.0-0.my15.x86_64 #1 SMP Thu Aug 4 05:21:08 JST 2011 x86_64 x86_64 x86_64 GNU/Linux

2011-08-06追記
2.6.40-4.fc15 のアナウンス内容です。
http://lists.fedoraproject.org/pipermail/package-announce/2011-August/063271.html
Rebase to 3.0. Version reports as 2.6.40 for compatibility with older userspace.

だそうです。

2011-08-07追記
Linux のバージョン番号と歴史について、次の記事にまとめられています。
http://gihyo.jp/lifestyle/serial/01/ganshiki-soushi/0023

2011-08-09追記
手順 (6) で、make の並列化オプション --jobs=N (N は core 数 x 2 程度の自然数) を指定すると、ビルド時間を短縮できるようです。手元の 2core の環境で、--jobs=4 を指定したところ、確かにコンパイル時間が半分程度で済みました。

2011-08-14追記
ccache についての解説が下記にあります。
http://www.ibm.com/developerworks/jp/linux/library/l-ccache/

2011-09-09追記
Fedora 16 Alpha が出ており、そちらのカーネルは既に 3.0 になっているので、ビルドまではしたくない方は、そちらを試してみては。

2011年7月31日日曜日

CentOS 6.0 差し替え前のインストールDVDとの差分

CentOS 6.0 がリリースされる際、インストールイメージが差し替え(すり替え?)られるということがありました。そのことを非難する向きもありますが、わたしとしては、早く試す環境が得られれば良かったので、あまり気にせず、差し替え前のイメージでインストールした環境をいじっています。
とはいえ、何が差分だったのかは興味津々です。以下、x86_64版の差分を調べてみました。

まずは、2つのイメージをマウント。
[root@centos6 ~]# sha1sum /dev/scd0
9de87b0c696ebd72b952edb4cc06c24cbdc37d81  /dev/scd0  ※こちらがリリース版の x86_64 DVD1
[root@centos6 ~]# sha1sum /dev/scd1
82637f7d81a6d7dbacf0699dd3bdc72360555dba  /dev/scd1  ※差し替え前の版
[root@centos6 ~]# mkdir /mnt_cent6.0_dvd1_final
[root@centos6 ~]# mkdir /mnt_cent6.0_dvd1_xxxxx
[root@centos6 ~]# mount /dev/scd0 /mnt_cent6.0_dvd1_final
mount: block device /dev/sr0 is write-protected, mounting read-only
[root@centos6 ~]# mount /dev/scd1 /mnt_cent6.0_dvd1_xxxxx
mount: block device /dev/sr1 is write-protected, mounting read-only
[root@centos6 ~]# ls -ld /mnt_cent6.0_dvd1_*
drwxr-xr-x 7 root root 4096 Jul 10 01:53 /mnt_cent6.0_dvd1_final
drwxr-xr-x 7 root root 4096 Jul  7 22:14 /mnt_cent6.0_dvd1_xxxxx
次に、diff を採取。
[root@centos6 ~]# diff -r -u /mnt_cent6.0_dvd1_xxxxx /mnt_cent6.0_dvd1_final
diff -r -u /mnt_cent6.0_dvd1_xxxxx/CentOS_BuildTag /mnt_cent6.0_dvd1_final/CentOS_BuildTag
--- /mnt_cent6.0_dvd1_xxxxx/CentOS_BuildTag     2011-07-07 22:07:27.000000000 +0900
+++ /mnt_cent6.0_dvd1_final/CentOS_BuildTag     2011-07-10 01:46:25.000000000 +0900
@@ -1 +1 @@
-20110707-1307
+20110709-1646
diff -r -u /mnt_cent6.0_dvd1_xxxxx/.discinfo /mnt_cent6.0_dvd1_final/.discinfo
--- /mnt_cent6.0_dvd1_xxxxx/.discinfo   2011-07-07 22:11:58.000000000 +0900
+++ /mnt_cent6.0_dvd1_final/.discinfo   2011-07-10 01:51:03.000000000 +0900
@@ -1,4 +1,4 @@
-1310044044.532095
+1310229985.226287
 6.0
 x86_64
 1
Binary files /mnt_cent6.0_dvd1_xxxxx/images/efiboot.img and /mnt_cent6.0_dvd1_final/images/efiboot.img differ
Binary files /mnt_cent6.0_dvd1_xxxxx/images/efidisk.img and /mnt_cent6.0_dvd1_final/images/efidisk.img differ
Binary files /mnt_cent6.0_dvd1_xxxxx/images/install.img and /mnt_cent6.0_dvd1_final/images/install.img differ
Binary files /mnt_cent6.0_dvd1_xxxxx/images/pxeboot/initrd.img and /mnt_cent6.0_dvd1_final/images/pxeboot/initrd.img differ
Only in /mnt_cent6.0_dvd1_final/repodata: 2a7e0c1da38a40e2961c0cec6acca8b8446d974b1fc055216ebde88bb4a19eb9-c6-x86_64-comps.xml
Only in /mnt_cent6.0_dvd1_final/repodata: 419f44d9f4e345e677c6ab519b62e2228cbcdf9c523d01d6a029fa4f02f66406-primary.xml.gz
Only in /mnt_cent6.0_dvd1_xxxxx/repodata: 52ad0604bc6f4555d84bb0c32e2213a3a8008091993eae46d8100ade6117fbe4-c6-x86_64-comps.xml.gz
Only in /mnt_cent6.0_dvd1_xxxxx/repodata: 80381e2b79fcd2ca61a289c783fcf29e6715912fa2e28b3e955b94c6e6725691-primary.sqlite.bz2
Only in /mnt_cent6.0_dvd1_xxxxx/repodata: 947c06b3449529ce6dea6c2610150b5c6f029094ab83093210765a866a528c6c-c6-x86_64-comps.xml
Only in /mnt_cent6.0_dvd1_xxxxx/repodata: b5f258df6a887534bb37ef8de488f238726ef2e0b4ded2180d756c45c6d843dd-primary.xml.gz
Only in /mnt_cent6.0_dvd1_final/repodata: c89fe3615797af0f5fcf9f53ebb36a605e713680139da34f71c4fe198ba9699e-c6-x86_64-comps.xml.gz
Only in /mnt_cent6.0_dvd1_final/repodata: e601759c6eed524aa4d8c5267f087f6c72491e3d811b3c937438e7a9b0747130-c6-x86_64-comps.xml.gz
Only in /mnt_cent6.0_dvd1_xxxxx/repodata: ef31b9351dab1a0ae826b0f1ca136059acdb81227fd7ff7d78099c5c58cfa035-c6-x86_64-comps.xml.gz
Only in /mnt_cent6.0_dvd1_final/repodata: ffb0e227e2cdd8a2b3609b65d7f38f6c1e756b437405b2918d6d36ebe59a0cb4-primary.sqlite.bz2
diff -r -u /mnt_cent6.0_dvd1_xxxxx/repodata/repomd.xml /mnt_cent6.0_dvd1_final/repodata/repomd.xml
--- /mnt_cent6.0_dvd1_xxxxx/repodata/repomd.xml 2011-07-07 22:14:37.000000000 +0900
+++ /mnt_cent6.0_dvd1_final/repodata/repomd.xml 2011-07-10 01:53:42.000000000 +0900
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?repomd xmlns="http://linux.duke.edu/metadata/repo" xmlns:rpm="http://linux.duke.edu/metadata/rpm">
-  <?revision>1310044318<?/revision>
+  <?revision>1310230263<?/revision>
   <?data type="other_db">
-    <location xml:base="media://1310044044.532095#1" href="repodata/5abe055f4bf7a1670d7b48a8172bec678861f2bcb8bc001ec3afa934638edc91-other.sqlite.bz2"/>
+    <location xml:base="media://1310229985.226287#1" href="repodata/5abe055f4bf7a1670d7b48a8172bec678861f2bcb8bc001ec3afa934638edc91-other.sqlite.bz2"/>
     <checksum type="sha256">5abe055f4bf7a1670d7b48a8172bec678861f2bcb8bc001ec3afa934638edc91</checksum>
-    <timestamp>1310044455.53</timestamp>
+    <timestamp>1310230400.59</timestamp>
     <size>2409110</size>
     <open-size>10912768</open-size>
     <open-checksum type="sha256">0321c3769ba1328fb66df664d748810b9c4ad3a9c1b11b081954e4b3cffbb34f</open-checksum>
@@ -12,16 +12,16 @@
   </data>
   <data type="other">
     <checksum type="sha256">d4b0b37148f088ff02731d7c5d12af279fc6bfa1bde26aa2c4ccf18f2ac0d493</checksum>
-    <timestamp>1310044450</timestamp>
+    <timestamp>1310230395</timestamp>
     <size>2634277</size>
     <open-size>11604182</open-size>
     <open-checksum type="sha256">eab32f9a510d6ff5209aae12c1e44c4955a6de3e31dd0a12730ad5e4d8bdb47e</open-checksum>
-    <location xml:base="media://1310044044.532095#1" href="repodata/d4b0b37148f088ff02731d7c5d12af279fc6bfa1bde26aa2c4ccf18f2ac0d493-other.xml.gz"/>
+    <location xml:base="media://1310229985.226287#1" href="repodata/d4b0b37148f088ff02731d7c5d12af279fc6bfa1bde26aa2c4ccf18f2ac0d493-other.xml.gz"/>
   </data>
   <data type="filelists_db">
-    <location xml:base="media://1310044044.532095#1" href="repodata/d30e7a407b0f019826c949cbc814944e6d178242abcadb5e79a891002cfb0107-filelists.sqlite.bz2"/>
+    <location xml:base="media://1310229985.226287#1" href="repodata/d30e7a407b0f019826c949cbc814944e6d178242abcadb5e79a891002cfb0107-filelists.sqlite.bz2"/>
     <checksum type="sha256">d30e7a407b0f019826c949cbc814944e6d178242abcadb5e79a891002cfb0107</checksum>
-    <timestamp>1310044469.52</timestamp>
+    <timestamp>1310230414.65</timestamp>
     <size>5772387</size>
     <open-size>31381504</open-size>
     <open-checksum type="sha256">12d2719025bbae4f357ea0a33f008d3ecce3778c0369c2af87e902626aabf43a</open-checksum>
@@ -29,38 +29,38 @@
   </data>
   <data type="filelists">
     <checksum type="sha256">c3797545a90f38e0738506e2b19c9a61e465f777156e0e1418a094d9ee08f23a</checksum>
-    <timestamp>1310044450</timestamp>
+    <timestamp>1310230395</timestamp>
     <size>5201743</size>
     <open-size>66967944</open-size>
     <open-checksum type="sha256">f4b9c8299f4a11b899e5ab43467d8473bc06a844786c4391f3f816673d602413</open-checksum>
-    <location xml:base="media://1310044044.532095#1" href="repodata/c3797545a90f38e0738506e2b19c9a61e465f777156e0e1418a094d9ee08f23a-filelists.xml.gz"/>
+    <location xml:base="media://1310229985.226287#1" href="repodata/c3797545a90f38e0738506e2b19c9a61e465f777156e0e1418a094d9ee08f23a-filelists.xml.gz"/>
   </data>
   <data type="primary_db">
-    <location xml:base="media://1310044044.532095#1" href="repodata/80381e2b79fcd2ca61a289c783fcf29e6715912fa2e28b3e955b94c6e6725691-primary.sqlite.bz2"/>
-    <checksum type="sha256">80381e2b79fcd2ca61a289c783fcf29e6715912fa2e28b3e955b94c6e6725691</checksum>
-    <timestamp>1310044477.57</timestamp>
-    <size>4375011</size>
+    <location xml:base="media://1310229985.226287#1" href="repodata/ffb0e227e2cdd8a2b3609b65d7f38f6c1e756b437405b2918d6d36ebe59a0cb4-primary.sqlite.bz2"/>
+    <checksum type="sha256">ffb0e227e2cdd8a2b3609b65d7f38f6c1e756b437405b2918d6d36ebe59a0cb4</checksum>
+    <timestamp>1310230422.81</timestamp>
+    <size>4375882</size>
     <open-size>19915776</open-size>
-    <open-checksum type="sha256">76b00fa614ae6691a30f60a3acae9886ad84b248d72673746db6518c7e356376</open-checksum>
+    <open-checksum type="sha256">5bfd617cf5cf192dc2fd74d5f429d319313c04ccf96ce85718fe9515628a60e5</open-checksum>
     <database_version>10</database_version>
   </data>
   <data type="primary">
-    <checksum type="sha256">b5f258df6a887534bb37ef8de488f238726ef2e0b4ded2180d756c45c6d843dd</checksum>
-    <timestamp>1310044450</timestamp>
-    <size>2470731</size>
+    <checksum type="sha256">419f44d9f4e345e677c6ab519b62e2228cbcdf9c523d01d6a029fa4f02f66406</checksum>
+    <timestamp>1310230395</timestamp>
+    <size>2470810</size>
     <open-size>16478243</open-size>
-    <open-checksum type="sha256">b933586069940e14f96b8ce2c0cbdfbe648ae0f6986edd0bf78ace4f5313e975</open-checksum>
-    <location xml:base="media://1310044044.532095#1" href="repodata/b5f258df6a887534bb37ef8de488f238726ef2e0b4ded2180d756c45c6d843dd-primary.xml.gz"/>
+    <open-checksum type="sha256">9196e4de0ddeb13e4ca36532a5dc00f054dab340ceb799e8dd87cbf2ad77085f</open-checksum>
+    <location xml:base="media://1310229985.226287#1" href="repodata/419f44d9f4e345e677c6ab519b62e2228cbcdf9c523d01d6a029fa4f02f66406-primary.xml.gz"/>
   </data>
   <data type="group_gz">
-    <location xml:base="media://1310044044.532095#1" href="repodata/ef31b9351dab1a0ae826b0f1ca136059acdb81227fd7ff7d78099c5c58cfa035-c6-x86_64-comps.xml.gz"/>
-    <checksum type="sha256">ef31b9351dab1a0ae826b0f1ca136059acdb81227fd7ff7d78099c5c58cfa035</checksum>
-    <open-checksum type="sha256">947c06b3449529ce6dea6c2610150b5c6f029094ab83093210765a866a528c6c</open-checksum>
-    <timestamp>1310044477</timestamp>
+    <location xml:base="media://1310229985.226287#1" href="repodata/c89fe3615797af0f5fcf9f53ebb36a605e713680139da34f71c4fe198ba9699e-c6-x86_64-comps.xml.gz"/>
+    <checksum type="sha256">c89fe3615797af0f5fcf9f53ebb36a605e713680139da34f71c4fe198ba9699e</checksum>
+    <open-checksum type="sha256">2a7e0c1da38a40e2961c0cec6acca8b8446d974b1fc055216ebde88bb4a19eb9</open-checksum>
+    <timestamp>1310230422</timestamp>
   </data>
   <data type="group">
-    <location xml:base="media://1310044044.532095#1" href="repodata/947c06b3449529ce6dea6c2610150b5c6f029094ab83093210765a866a528c6c-c6-x86_64-comps.xml"/>
-    <checksum type="sha256">947c06b3449529ce6dea6c2610150b5c6f029094ab83093210765a866a528c6c</checksum>
-    <timestamp>1310044477</timestamp>
+    <location xml:base="media://1310229985.226287#1" href="repodata/2a7e0c1da38a40e2961c0cec6acca8b8446d974b1fc055216ebde88bb4a19eb9-c6-x86_64-comps.xml"/>
+    <checksum type="sha256">2a7e0c1da38a40e2961c0cec6acca8b8446d974b1fc055216ebde88bb4a19eb9</checksum>
+    <timestamp>1310230422</timestamp>
   </data>
 </repomd>
diff -r -u /mnt_cent6.0_dvd1_xxxxx/repodata/TRANS.TBL /mnt_cent6.0_dvd1_final/repodata/TRANS.TBL
--- /mnt_cent6.0_dvd1_xxxxx/repodata/TRANS.TBL  2011-07-07 22:14:38.000000000 +0900
+++ /mnt_cent6.0_dvd1_final/repodata/TRANS.TBL  2011-07-10 01:53:43.000000000 +0900
@@ -1,10 +1,10 @@
-F 52AD0604.GZ;1                                                                                                                                                                                                     52ad0604bc6f4555d84bb0c32e2213a3a8008091993eae46d8100ade6117fbe4-c6-x86_64-comps.xml.gz
+F 2A7E0C1D.XML;1                                                                                                                                                                                                    2a7e0c1da38a40e2961c0cec6acca8b8446d974b1fc055216ebde88bb4a19eb9-c6-x86_64-comps.xml
+F 419F44D9.GZ;1                                                                                                                                                                                                     419f44d9f4e345e677c6ab519b62e2228cbcdf9c523d01d6a029fa4f02f66406-primary.xml.gz
 F 5ABE055F.BZ2;1                                                                                                                                                                                                    5abe055f4bf7a1670d7b48a8172bec678861f2bcb8bc001ec3afa934638edc91-other.sqlite.bz2
-F 80381E2B.BZ2;1                                                                                                                                                                                                    80381e2b79fcd2ca61a289c783fcf29e6715912fa2e28b3e955b94c6e6725691-primary.sqlite.bz2
-F 947C06B3.XML;1                                                                                                                                                                                                    947c06b3449529ce6dea6c2610150b5c6f029094ab83093210765a866a528c6c-c6-x86_64-comps.xml
-F B5F258DF.GZ;1                                                                                                                                                                                                     b5f258df6a887534bb37ef8de488f238726ef2e0b4ded2180d756c45c6d843dd-primary.xml.gz
 F C3797545.GZ;1                                                                                                                                                                                                     c3797545a90f38e0738506e2b19c9a61e465f777156e0e1418a094d9ee08f23a-filelists.xml.gz
+F C89FE361.GZ;1                                                                                                                                                                                                     c89fe3615797af0f5fcf9f53ebb36a605e713680139da34f71c4fe198ba9699e-c6-x86_64-comps.xml.gz
 F D30E7A40.BZ2;1                                                                                                                                                                                                    d30e7a407b0f019826c949cbc814944e6d178242abcadb5e79a891002cfb0107-filelists.sqlite.bz2
 F D4B0B371.GZ;1                                                                                                                                                                                                     d4b0b37148f088ff02731d7c5d12af279fc6bfa1bde26aa2c4ccf18f2ac0d493-other.xml.gz
-F EF31B935.GZ;1                                                                                                                                                                                                     ef31b9351dab1a0ae826b0f1ca136059acdb81227fd7ff7d78099c5c58cfa035-c6-x86_64-comps.xml.gz
+F E601759C.GZ;1                                                                                                                                                                                                     e601759c6eed524aa4d8c5267f087f6c72491e3d811b3c937438e7a9b0747130-c6-x86_64-comps.xml.gz
+F FFB0E227.BZ2;1                                                                                                                                                                                                    ffb0e227e2cdd8a2b3609b65d7f38f6c1e756b437405b2918d6d36ebe59a0cb4-primary.sqlite.bz2
 F REPOMD.XML;1                                                                                                                                                                                                      repomd.xml
diff -r -u /mnt_cent6.0_dvd1_xxxxx/.treeinfo /mnt_cent6.0_dvd1_final/.treeinfo
--- /mnt_cent6.0_dvd1_xxxxx/.treeinfo   2011-07-07 22:07:21.000000000 +0900
+++ /mnt_cent6.0_dvd1_final/.treeinfo   2011-07-10 01:46:22.000000000 +0900
@@ -1,6 +1,6 @@
 [general]
 family = CentOS
-timestamp = 1310043725.69
+timestamp = 1310229667.07
 variant = 
 totaldiscs = 1
 version = 6.0
これを見ると、RPM が格納されている Packages ディレクトリ下には差分が無いですから、インストール後の実行環境には差が無いのでは? と思いましたが、はて?
EFI ブートで何かあったのかな?(efiboot.img が差し替わっているので)

DVD2 のほうも同様に。
[root@centos6 ~]# sha1sum /dev/scd0
5e3834621f11fbcca78cf7d70625c647045f45f5  /dev/scd0  ※こちらがリリース版の x86_64 DVD2
[root@centos6 ~]# sha1sum /dev/scd1
e09c6406c0658dbe28beec363c7adfb0c2c5c3f7  /dev/scd1  ※差し替え前の版
[root@centos6 ~]# mkdir /mnt_cent6.0_dvd2_final
[root@centos6 ~]# mkdir /mnt_cent6.0_dvd2_xxxxx
[root@centos6 ~]# mount /dev/scd0 /mnt_cent6.0_dvd2_final
mount: block device /dev/sr0 is write-protected, mounting read-only
[root@centos6 ~]# mount /dev/scd1 /mnt_cent6.0_dvd2_xxxxx
mount: block device /dev/sr1 is write-protected, mounting read-only
[root@centos6 ~]# ls -ld /mnt_cent6.0_dvd2_*
drwxr-xr-x 3 root root 2048 Jul 10 01:51 /mnt_cent6.0_dvd2_final
drwxr-xr-x 3 root root 2048 Jul  7 22:11 /mnt_cent6.0_dvd2_xxxxx
diff を採取。
[root@centos6 ~]# diff -r -u /mnt_cent6.0_dvd2_xxxxx /mnt_cent6.0_dvd2_final
diff -r -u /mnt_cent6.0_dvd2_xxxxx/CentOS_BuildTag /mnt_cent6.0_dvd2_final/CentOS_BuildTag
--- /mnt_cent6.0_dvd2_xxxxx/CentOS_BuildTag     2011-07-07 22:07:27.000000000 +0900
+++ /mnt_cent6.0_dvd2_final/CentOS_BuildTag     2011-07-10 01:46:25.000000000 +0900
@@ -1 +1 @@
-20110707-1307
+20110709-1646
diff -r -u /mnt_cent6.0_dvd2_xxxxx/.discinfo /mnt_cent6.0_dvd2_final/.discinfo
--- /mnt_cent6.0_dvd2_xxxxx/.discinfo   2011-07-07 22:11:58.000000000 +0900
+++ /mnt_cent6.0_dvd2_final/.discinfo   2011-07-10 01:51:03.000000000 +0900
@@ -1,4 +1,4 @@
-1310044044.532095
+1310229985.226287
 6.0
 x86_64
 2
diff -r -u /mnt_cent6.0_dvd2_xxxxx/.treeinfo /mnt_cent6.0_dvd2_final/.treeinfo
--- /mnt_cent6.0_dvd2_xxxxx/.treeinfo   2011-07-07 22:07:21.000000000 +0900
+++ /mnt_cent6.0_dvd2_final/.treeinfo   2011-07-10 01:46:22.000000000 +0900
@@ -1,6 +1,6 @@
 [general]
 family = CentOS
-timestamp = 1310043725.69
+timestamp = 1310229667.07
 variant = 
 totaldiscs = 1
 version = 6.0
というわけで、DVD2 については、単にISOイメージの再作成をしただけで、中身は変わらないみたいです。
人気ブログランキングへ にほんブログ村 IT技術ブログへ