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を返すだけのシステムコールでした。