timerfd是linux为用户提供的基于文件描述符的定时器接口,主要的系统调用有三个。分别如下:
timerfd_create -- 新建一个fd
timerfd_settime -- 设置fd的超时时间
timerfd_gettime -- 获得fd的超时时间
一般用户设置fd的超时时间后就可以通过read来读取fd来poll当前的时间.
timerfd 基本就是一个内存文件。这点可以从timerfd_create的实现中看到.
可以看道timerfd_create 有两个输入蚕食分别是clockid 和 flags
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);
#这里看到clockid只能去这个if中的固定值
if ((flags & ~TFD_CREATE_FLAGS) ||
(clockid != CLOCK_MONOTONIC &&
clockid != CLOCK_REALTIME &&
clockid != CLOCK_REALTIME_ALARM &&
clockid != CLOCK_BOOTTIME &&
clockid != CLOCK_BOOTTIME_ALARM))
return -EINVAL;
if ((clockid == CLOCK_REALTIME_ALARM ||
clockid == CLOCK_BOOTTIME_ALARM) &&
!capable(CAP_WAKE_ALARM))
return -EPERM;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
init_waitqueue_head(&ctx->wqh);
spin_lock_init(&ctx->cancel_lock);
ctx->clockid = clockid;
#是不是alarm
if (isalarm(ctx))
alarm_init(&ctx->t.alarm,
ctx->clockid == CLOCK_REALTIME_ALARM ?
ALARM_REALTIME : ALARM_BOOTTIME,
timerfd_alarmproc);
else
hrtimer_init(&ctx->t.tmr, clockid, HRTIMER_MODE_ABS);
ctx->moffs = ktime_mono_to_real(0);
#这里就是timerfd的本质,即是内存中的一个文件
ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx,
O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS));
if (ufd < 0)
kfree(ctx);
return ufd;
}
当用户通过timerfd_sertime之后就会通过read来poll这个fd,我们看看timerfd的read函数实现如下:
static const struct file_operations timerfd_fops = {
.release = timerfd_release,
.poll = timerfd_poll,
.read = timerfd_read,
.llseek = noop_llseek,
.show_fdinfo = timerfd_show,
.unlocked_ioctl = timerfd_ioctl,
};
可以看到read的实现函数为timerfd_read
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);
#这里可以看到在创建timerfd的时候可以选择是否阻塞,如果是非阻塞的话,调用read的函数时候如果时间
#没有到的话就直接返回了
if (file->f_flags & O_NONBLOCK)
res = -EAGAIN;
else
#如果是阻塞方式,则调用read的时候,就会通过下面的wait函数阻塞在这里
res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);
/*
* If clock has changed, we do not care about the
* ticks and we do not rearm the timer. Userspace must
* reevaluate anyway.
*/
if (timerfd_canceled(ctx)) {
ctx->ticks = 0;
ctx->expired = 0;
res = -ECANCELED;
}
spin_unlock_irq(&ctx->wqh.lock);
if (ticks)
res = put_user(ticks, (u64 __user *) buf) ? -EFAULT: sizeof(ticks);
return res;
}