この記事では、いささかオーバースペック(と言うか諸々のオーバーヘッドを加味すると無意味かも? )ですが、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=2bash 組み込みの time で計った経過時間 2.503 と一致することが確認できます。
利用したシステムコール clock_gettime(CLOCK_MONOTONIC) については、次の記事を参照してください。
経過時間の計測方法
0 件のコメント:
コメントを投稿