timeシステムコールその1?
Linux Kernel DevelopmentのP221ページ読んでいたときに、「The kernel also implements the time() system call」という文章を見つけた。
ん?time()ってシステムコールだったっけ?
しょうがないので、ソースをgrepしてみた。
すると、確かにkern/time.cに以下のソースを見つけた。
なんだかifdefから察すると、アーキテクチャごとにあったりなかったりなシステムコールらしい。
#ifdef __ARCH_WANT_SYS_TIME /* * sys_time() can be implemented in user-level using * sys_gettimeofday(). Is this for backwards compatibility? If so, * why not move it into the appropriate arch directory (for those * architectures that need it). */ SYSCALL_DEFINE1(time, time_t __user *, tloc) { time_t i = get_seconds(); if (tloc) { if (put_user(i,tloc)) return -EFAULT; } force_successful_syscall_return(); return i; }
良く見ると、Linux本に注釈があり、
Some architectures , however , do not implement sys_time() and instead specify that it is emulated in the C library through the use of gettimeofday().
と書いてある。
ということはlibcのtime()相当なのか?
気になるときはやっぱりソース読もう。
まずはget_secondsから。
unsigned long get_seconds(void) { return xtime_cache.tv_sec; } EXPORT_SYMBOL(get_seconds);
何だ、このxtime_cacheって。調べてみると、以下の関数でのみ更新されるみたいだ。
static struct timespec xtime_cache __attribute__ ((aligned (16))); void update_xtime_cache(u64 nsec) { xtime_cache = xtime; timespec_add_ns(&xtime_cache, nsec); }
xtimeもxtime_cacheもRAM上にある変数だよな?なぜにxtimeとxtime_「cache」の違いがあるんだろう?
ちなみにxtimeとはLinux本220ページに記載があるように、epoch(1970年1月1日からの経過秒数)を格納しているカーネル内変数である。
疑問点は一度脇に避けておいて、update_xtime_cache()を呼び出している箇所を調べてみる。すると以下4点の関数であることがわかる。
do_settimeofday()
timekeeping_init()
timekeeping_resume()
update_wall_time()
いずれもxtimeの更新も行っている。そして、いずれもxtimeの更新前にxtime_lockを掛けている。
※update_wall_timeに関しては、冒頭コメントに「Called from the timer interrupt, must hold a write on xtime_lock.」とあり、xtime_lockを掛けた上でコールしていることが伺える。
ソースを読んだ限りの推測だが、xtimeは書き込みなどのアクセスを行う際にロック(xtime_lock)を掛ける必要がある。
一方でxtime_cacheは特にロックを掛けることなしに読み出しを行っている。
つまり、ロックを獲得するコストなしに読み出すことのできる「リードオンリー」な変数ということが言えそうだ。たぶんcacheというのはこの事を指しているのではないか?
もう一度システムコール本体に戻る。
tlocって何だろう?
SYSCALL_DEFINE1(time, time_t __user *, tloc) { time_t i = get_seconds(); if (tloc) { if (put_user(i,tloc)) return -EFAULT; }
わからないので、put_userを見てみる。これはアーキ依存なマクロでコメントによるとカーネル空間からユーザ空間に値をコピーするためのものらしい。
This macro copies a single simple value from kernel space to user
* space.
(arch/mips/include/asm/uaccess.h より抜粋)
このことから、tlocに何らかのアドレスが入っていたら、取得したepoch値を直接tlocが指すアドレスにコピーすると推定できる。
得られたepoch値をreturnしているのに、なぜこんな面倒なことをするのだろうか?
鍵はSYSCALL_DEFINE1マクロにあるのか?システムコールの呼び出しはどのようにして行っているのか?
疑問が次々湧いてくる。
だからカーネルソースリーディングは止められない!
※結局、time()システムコールはlibcのtime()と同じくepochを返すだけのシステムコールでした。