この記事では、いささかオーバースペック(と言うか諸々のオーバーヘッドを加味すると無意味かも? )ですが、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 件のコメント:
コメントを投稿