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 が淘汰されるでしょうかね。。。
人気ブログランキングへ にほんブログ村 IT技術ブログへ