わたしの過去の 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でサブルーチンを動的に定義しようとしてハマりました
0 件のコメント:
コメントを投稿