無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

timerfd


ファイル読み込みでタイマ満了通知を取得します。timerfdファイルIDでreadすることで、タイマ満了しなければwaitします。

サンプルを参考にしました。まず4秒そして2秒周期でタイマ満了しています。

#include <sys/timerfd.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

#define TV2SEC(tv) ((double)((tv).tv_sec) + (double)((tv).tv_usec / 1000000.0))

int main(int argc, char ** argv ) {

   struct timespec cur;
   struct timeval  tv1,tv2;
   uint64_t read_cnt;
   int     cnt = 0;
   double  rtn;

   clock_gettime( CLOCK_MONOTONIC, &cur );

   struct itimerspec val;
   val.it_value.tv_sec     = cur.tv_sec + 4;
   val.it_value.tv_nsec    = cur.tv_nsec;

   val.it_interval.tv_sec  = 2;
   val.it_interval.tv_nsec = 0;

   int fd = timerfd_create( CLOCK_MONOTONIC, 0 );
   timerfd_settime( fd, TFD_TIMER_ABSTIME, &val, 0 );

   gettimeofday( &tv1, 0 );
   while (1) {
       read( fd, &read_cnt, sizeof( uint64_t ));
       gettimeofday( &tv2, 0 );
       rtn = TV2SEC( tv2 ) - TV2SEC( tv1 );
       printf( "timerfd %d  time:[%f]  %d\n", cnt + 1, rtn , read_cnt);
       tv1 = tv2;
       cnt++;
   }
   close( fd );
}

[root@localhost kitamura]# ./a.out
timerfd 1  time:[4.000367]  1
timerfd 2  time:[2.000236]  1
timerfd 3  time:[2.000139]  1
timerfd 4  time:[2.000367]  1
timerfd 5  time:[1.999956]  1
timerfd 6  time:[1.999231]  1
timerfd 7  time:[2.000228]  1
timerfd 8  time:[2.000384]  1
ファイル読み込みと言うので、負荷がどうなのかと思ってしまいますが、実態はファイルシステムを介しておらず、ただFILEオペレーションコールのインターフェースを使っているにすぎません。

このファイルIDを複数のプロセスで共有することで、同一タイマー処理をマルチプロセスとして実装できます。1秒単位に処理しなければならなく、その1処理が2秒かかる処理みたいに、マルチプロセスによる実装が必要となるケースが想定されます。

anon_inode_getfd()でファイルIDを取得します。[timerfd]はdentry名で、timerfd_fopsはFILEコールバックオペレーションです。ファイルシステムを介してなくても、FILE構造体の設定情報としてdentryが必要となります。ctxがタイマをコントロールする情報で、file->private_dataに設定されます。
static const struct file_operations timerfd_fops = {
       .release        = timerfd_release,
       .poll           = timerfd_poll,
       .read           = timerfd_read,
       .llseek         = noop_llseek,
};

SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
{
       int ufd;
       struct timerfd_ctx *ctx;

       /* Check the TFD_* constants for consistency.  */
       BUILD_BUG_ON(TFD_CLOEXEC != O_CLOEXEC);
       BUILD_BUG_ON(TFD_NONBLOCK != O_NONBLOCK);

       if ((flags & ~TFD_CREATE_FLAGS) ||
           (clockid != CLOCK_MONOTONIC &&
            clockid != CLOCK_REALTIME))
               return -EINVAL;

       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
       if (!ctx)
               return -ENOMEM;

       init_waitqueue_head(&ctx->wqh);
       ctx->clockid = clockid;
       hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS);
       ctx->moffs = ktime_get_monotonic_offset();

       ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx,
                              O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS));
       if (ufd < 0)
               kfree(ctx);

       return ufd;
}

int anon_inode_getfd(const char *name, const struct file_operations *fops,
                    void *priv, int flags)
{
       int error, fd;
       struct file *file;

       error = get_unused_fd_flags(flags);
       if (error < 0)
               return error;
       fd = error;

       file = anon_inode_getfile(name, fops, priv, flags);
       if (IS_ERR(file)) {
               error = PTR_ERR(file);
               goto err_put_unused_fd;
       }
       fd_install(fd, file);

       return fd;

err_put_unused_fd:
       put_unused_fd(fd);
       return error;
}
timerfd_read()は読み込みのコールバック関数です。タイマ満了毎にctx->ticksがインクリメントさ、wait_event_interruptible_locked_irq()は、ctx->ticks!=0までループウエイトし、ctx->ticksをput_user()でchar __user *bufして復帰します。ctx->ticksはタイマ満了毎にインクリメントされます。

if (ctx->expired && ctx->tintv.tv64)は、インターバルタイマでの満了通知であると言うことで、コメントによると、短いインターバルタイム設定によるDoSアタックの回避という事で、タイマーコールバック関数内で、再度タイマスタートしないよう、次の満了通知までタイマを進めるといった処理のようですが・・・。
static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count,
                           loff_t *ppos)
{
       struct timerfd_ctx *ctx = file->private_data;
       ssize_t res;
       u64 ticks = 0;

       if (count < sizeof(ticks))
               return -EINVAL;
       spin_lock_irq(&ctx->wqh.lock);
       if (file->f_flags & O_NONBLOCK)
               res = -EAGAIN;
       else
               res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);

       if (timerfd_canceled(ctx)) {
               ctx->ticks = 0;
               ctx->expired = 0;
               res = -ECANCELED;
       }

       if (ctx->ticks) {
               ticks = ctx->ticks;

               if (ctx->expired && ctx->tintv.tv64) {
                       ticks += hrtimer_forward_now(&ctx->tmr,
                                                    ctx->tintv) - 1;
                       hrtimer_restart(&ctx->tmr);
               }
               ctx->expired = 0;
               ctx->ticks = 0;
       }
       spin_unlock_irq(&ctx->wqh.lock);
       if (ticks)
               res = put_user(ticks, (u64 __user *) buf) ? -EFAULT: sizeof(ticks);
       return res;
}

最終更新 2014/04/19 19:24:21 - north
(2014/04/19 19:22:42 作成)