`
carmark
  • 浏览: 161317 次
  • 性别: Icon_minigender_1
  • 来自: 大连->北京
社区版块
存档分类
最新评论

fs/timerfd.c内核分析

阅读更多

/*

 *  fs/timerfd.c

 *

 *  Copyright (C) 2007  Davide Libenzi <davidel@xmailserver.org>

 *

 *

 *  Thanks to Thomas Gleixner for code reviews and useful comments.

 *

 */

 

#include <linux/file.h>

#include <linux/poll.h>

#include <linux/init.h>

#include <linux/fs.h>

#include <linux/sched.h>

#include <linux/kernel.h>

#include <linux/list.h>

#include <linux/spinlock.h>

#include <linux/time.h>

#include <linux/hrtimer.h>

#include <linux/anon_inodes.h>

#include <linux/timerfd.h>

#include <linux/syscalls.h>

 

struct timerfd_ctx {

       struct hrtimer tmr;

       ktime_t tintv;

       wait_queue_head_t wqh;

       u64 ticks;

       int expired;

       int clockid;

};

 

//该函数是timerfd的定时器超时函数。在timerfd超时时,该函数会设置定时器

 //超时标记位;增加定时器超时次数(在设置定时器循环模式时,可能会出现多//次超时没有被处理的情况);唤醒一个等待队列,从而唤醒可能存在的正被阻//塞的readselect

static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr)

{

       struct timerfd_ctx *ctx = container_of(htmr, struct timerfd_ctx, tmr);

       unsigned long flags;

       //spin_lock_irqsave函数的作用是采用锁机制保证变量的正确性,但是在过程

       //中可能会出现死锁的情况,所以采用一个中断状态来解决这样的问题,与

       //下面的spin_lock_irqrestore相对应。

       spin_lock_irqsave(&ctx->wqh.lock, flags);

     ctx->expired = 1;// 设置定时器超时标记位

       ctx->ticks++; //增加定时器超时次数

       wake_up_locked(&ctx->wqh); //唤醒一个等待队列

       spin_unlock_irqrestore(&ctx->wqh.lock, flags);

 

       return HRTIMER_NORESTART;

}

 

static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx)

{

       ktime_t remaining;

 

       remaining = hrtimer_expires_remaining(&ctx->tmr);

       return remaining.tv64 < 0 ? ktime_set(0, 0): remaining;

}

 

//一些初始化的工作

static void timerfd_setup(struct timerfd_ctx *ctx, int flags,

                       const struct itimerspec *ktmr)

{

       enum hrtimer_mode htmode;

       ktime_t texp;

 

       htmode = (flags & TFD_TIMER_ABSTIME) ?

              HRTIMER_MODE_ABS: HRTIMER_MODE_REL;

 

       texp = timespec_to_ktime(ktmr->it_value);

       ctx->expired = 0;

       ctx->ticks = 0;

       ctx->tintv = timespec_to_ktime(ktmr->it_interval);

       hrtimer_init(&ctx->tmr, ctx->clockid, htmode);

       hrtimer_set_expires(&ctx->tmr, texp);

       ctx->tmr.function = timerfd_tmrproc;

       if (texp.tv64 != 0)

              //hrtimer_start函数将一个hrtimer加入到一个按照到期时间排序的红黑树中

              hrtimer_start(&ctx->tmr, texp, htmode);

}

 

//timerfd_release函数释放timerfd_create函数中申请的资源,删除已分配的定时//器。

static int timerfd_release(struct inode *inode, struct file *file)

{

       struct timerfd_ctx *ctx = file->private_data;

       //hrtimer_cancel函数的作用是删除一个正在排队的定时器。这里分三种情况,一种是定

//时器已到期,并且设置了软中断模式;第二种是没有到期,还在红黑树中;第三种是

//定时器正在执行。

       hrtimer_cancel(&ctx->tmr);

       kfree(ctx); //释放内核空间

       return 0;

}

 

/*timerfd_polltimerfd的等待队列登记到一个poll_table,从而在定时器超时时能唤醒select系统调用。

*/

static unsigned int timerfd_poll(struct file *file, poll_table *wait)

{

       struct timerfd_ctx *ctx = file->private_data;

       unsigned int events = 0;

       unsigned long flags;

 

       poll_wait(file, &ctx->wqh, wait);  //增加一个等待队列到poll_table

 

       spin_lock_irqsave(&ctx->wqh.lock, flags);

       if (ctx->ticks)

              events |= POLLIN;

       spin_unlock_irqrestore(&ctx->wqh.lock, flags);

 

       return events;

}

 

/*

*timerfd_read函数是文件操作read的内核实现,读到的是定时器的超时次数。

*该函数在阻塞模式下会把自身挂到timerfd的等待队列中,等待定时器超时时

*被唤醒。

*/

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;

       DECLARE_WAITQUEUE(wait, current);

 

       if (count < sizeof(ticks))

              return -EINVAL;

       spin_lock_irq(&ctx->wqh.lock);

       res = -EAGAIN;

       if (!ctx->ticks && !(file->f_flags & O_NONBLOCK)) {

              __add_wait_queue(&ctx->wqh, &wait);

              for (res = 0;;) {

                     set_current_state(TASK_INTERRUPTIBLE);

                     if (ctx->ticks) {

                            res = 0;

                            break;

                     }

                     if (signal_pending(current)) {

                            res = -ERESTARTSYS;

                            break;

                     }

                     spin_unlock_irq(&ctx->wqh.lock);

                     schedule();

                     spin_lock_irq(&ctx->wqh.lock);

              }

              __remove_wait_queue(&ctx->wqh, &wait);

              __set_current_state(TASK_RUNNING);

       }

       if (ctx->ticks) {

              ticks = ctx->ticks;

              if (ctx->expired && ctx->tintv.tv64) {

                     /*

                      * If tintv.tv64 != 0, this is a periodic timer that

                      * needs to be re-armed. We avoid doing it in the timer

                      * callback to avoid DoS attacks specifying a very

                      * short timer period.

                      */

                     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;

}

 

static const struct file_operations timerfd_fops = {

       .release          = timerfd_release,

       .poll        = timerfd_poll,

       .read             = timerfd_read,

};

 

//根据文件描述符获得一个file的结构体

static struct file *timerfd_fget(int fd)

{

       struct file *file;

 

       file = fget(fd);

       if (!file)

              return ERR_PTR(-EBADF);

       if (file->f_op != &timerfd_fops) {

              fput(file);

              return ERR_PTR(-EINVAL);

       }

 

       return file;

}

/*

*做一些定时器的初始化工作;

*调用hrtimer_init初始化一个hrtimer

*调用anon_inode_getfd分配一个dentry,并得到一个文件号fd,同时传入timerfd

*的文件操作指针struct file_operations timerfd_fopsanno_inode_getfd是文件系统

*anon_inodefs的一个帮助函数。anon文件系统比较简单,整个文件系统只有一

*inode节点,其实现代码可以在fs/anon_inodes.c中找到。

*/

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);

 

       ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx,

                            flags & TFD_SHARED_FCNTL_FLAGS);

       if (ufd < 0)

              kfree(ctx);

 

       return ufd;

}

 

//timerfd_settime最终会调用hrtimer_start启动定时器,其超时函数被设置为timerfd_tmrproc

/*此函数用于设置新的超时时间,并开始计时。

*参数ufdtimerfd_create返回的文件句柄。

*参数flags1代表设置的是绝对时间;为0代表相对时间。

*参数utmr为需要设置的时间。

*参数otmr为定时器这次设置之前的超时时间。

*函数返回0代表设置成功。

*/

SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags,

              const struct itimerspec __user *, utmr,

              struct itimerspec __user *, otmr)

{

       struct file *file;

       struct timerfd_ctx *ctx;

       struct itimerspec ktmr, kotmr;

 

       if (copy_from_user(&ktmr, utmr, sizeof(ktmr)))

              return -EFAULT;

 

       if ((flags & ~TFD_SETTIME_FLAGS) ||

           !timespec_valid(&ktmr.it_value) ||

           !timespec_valid(&ktmr.it_interval))

              return -EINVAL;

 

       file = timerfd_fget(ufd);

       if (IS_ERR(file))

              return PTR_ERR(file);

       ctx = file->private_data;

 

       /*

        * We need to stop the existing timer before reprogramming

        * it to the new values.

        */

       for (;;) {

              spin_lock_irq(&ctx->wqh.lock);

              if (hrtimer_try_to_cancel(&ctx->tmr) >= 0)

                     break;

              spin_unlock_irq(&ctx->wqh.lock);

              cpu_relax();

       }

 

       /*

        * If the timer is expired and it's periodic, we need to advance it

        * because the caller may want to know the previous expiration time.

        * We do not update "ticks" and "expired" since the timer will be

        * re-programmed again in the following timerfd_setup() call.

        */

       if (ctx->expired && ctx->tintv.tv64)

              hrtimer_forward_now(&ctx->tmr, ctx->tintv);

 

       kotmr.it_value = ktime_to_timespec(timerfd_get_remaining(ctx));

       kotmr.it_interval = ktime_to_timespec(ctx->tintv);

 

       /*

        * Re-program the timer to the new value ...

        */

       timerfd_setup(ctx, flags, &ktmr);

 

       spin_unlock_irq(&ctx->wqh.lock);

       fput(file);

       if (otmr && copy_to_user(otmr, &kotmr, sizeof(kotmr)))

              return -EFAULT;

 

       return 0;

}

//此函数用于获得定时器距离下次超时还剩下的时间。

//如果调用时定时器已经到期,并且该定时器处于循环模式,那么调用此函数之后定时器重新开始计时。

SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr)

{

       struct file *file;

       struct timerfd_ctx *ctx;

       struct itimerspec kotmr;

 

       file = timerfd_fget(ufd);

       if (IS_ERR(file))

              return PTR_ERR(file);

       ctx = file->private_data;

 

       spin_lock_irq(&ctx->wqh.lock);

       if (ctx->expired && ctx->tintv.tv64) {

              ctx->expired = 0;

              ctx->ticks +=

                     hrtimer_forward_now(&ctx->tmr, ctx->tintv) - 1;

              hrtimer_restart(&ctx->tmr);

       }

       kotmr.it_value = ktime_to_timespec(timerfd_get_remaining(ctx));

       kotmr.it_interval = ktime_to_timespec(ctx->tintv);

       spin_unlock_irq(&ctx->wqh.lock);

       fput(file);

 

       return copy_to_user(otmr, &kotmr, sizeof(kotmr)) ? -EFAULT: 0;

}

0
8
分享到:
评论

相关推荐

    hbase 启动regionserver日志报错: Wrong FS: hdfs:// .regioninfo, expected: file:///

    NULL 博文链接:https://bnmnba.iteye.com/blog/2322332

    FS_C_复数_www./fs.c.com_

    压缩包中的文件"FS_C"可能是包含了上述`Complex`类实现的源代码文件。如果要深入了解这个程序的工作原理,你可以解压文件并查看源代码,了解具体的实现细节。 总之,这个项目涉及的知识点包括: 1. 复数的概念及其...

    nodejs异步IO的实现 转:http://cnodejs.org/topic/4f16442ccae1f4aa2700113b

    本文将深入探讨Node.js异步IO的实现,并结合给定的资源进行分析。 首先,异步I/O是Node.js的核心特性之一,它允许程序在等待I/O操作完成时继续执行其他任务,从而提高了整体的执行效率和系统资源利用率。在传统的...

    fs-4.181.1507.upt.jpg

    小雉系统升级包-4.181.1507,系统tcp使用bbr,提升tcp效率;系统增加邮件系统,可收发邮件;详细信息请访问: &lt;a href="http://...

    yaffs2压缩包

    使用命令即可实现./patch-ker.sh c /home/linux-2.6.32.2 上面这个命令完成了三件事情(也可以手动修改实现) (1)修改内核fs/Kconfig,增加了两行 # Patched by YAFFS source "fs/yaffs2/Kconfig" (2)修改内核fs/...

    NaviOnePC.rar

    有框主程序,可以移动http://page18.ctfile.com/fs/gNL152134545 无框主程序,不能移动http://page18.ctfile.com/fs/WKz152134542 2、再下载分省图资(下载后将2个文件拷入NaviOne目录下就可以了) ...

    Linux_Syscall_quickref

    在C语言编程中,通常通过在`libc`(标准C库)中定义的函数来访问这些系统调用。`libc`为许多系统调用提供了封装,使得开发者可以更方便地调用它们。关于系统调用的更多信息可以在手册页(man pages)的第二节找到,...

    esp_littlefs:用于ESP-IDF的LittleFS端口.zip

    在给定的压缩包"esp_littlefs:用于ESP-IDF的LittleFS端口.zip"中,包含了一个名为"esp_littlefs-master"的项目,这是ESP-IDF对LittleFS文件系统的移植。 LittleFS是一种轻量级的文件系统,设计用于嵌入式设备,...

    LINUX System Call Quick Reference

    | 8 | creat | 创建文件或设备(更多信息参见`man2 open`) | `fs/open.c` | | 9 | link | 为文件创建新的名称 | `fs/namei.c` | | 10 | unlink | 删除文件名及其可能指向的文件 | `fs/namei.c` | | 11 | execve | ...

    Linux system call quick reference

    - 源码位置:`fs/open.c` 6. **close** - 描述:关闭文件描述符。 - 源码位置:`fs/open.c` 7. **waitpid** - 描述:等待子进程结束。 - 源码位置:`kernel/exit.c` 8. **creat** - 描述:创建文件或设备(更...

    GPIO模拟I2C程序实现

    GPIO模拟I2C的程序实现 #include &lt;linux/module.h&gt; #include &lt;linux/config.h&gt; #include &lt;linux/errno.h&gt; #include &lt;linux/miscdevice.h&gt; #include &lt;linux/fcntl.h&gt; #include &lt;linux/init.h&gt; #include &lt;linux/delay...

    基于C的firewall

    这是一个简单的基于C语言的防火墙,#include &lt;linux/kernel.h&gt; #include &lt;linux/ip.h&gt; #include &lt;linux/module.h&gt; #include &lt;linux/netdevice.h&gt; #include &lt;linux/netfilter.h&gt; #include &lt;linux/netfilter_ipv4.h&gt; #...

    linux_net.pdf

    linux tcp/ip分析 两台主机建立udp通信所走过的函数列表: | sys_read fs/read_write.c | sock_read net/socket.c | sock_recvmsg net/socket.c | inet_recvmsg net/ipv4/af_inet.c | udp_recvmsg net/ipv4/udp.c | ...

    Linux操作系统源代码详细分析.pdf

    本文件旨在深入分析Linux操作系统的源代码,通过对核心部分的解读来帮助读者更好地理解Linux内核的工作原理。 #### 二、Linux操作系统特性 1. **抢先式多任务处理**:Linux支持真正的多任务处理,能够同时运行多个...

    LINUX2.6.26.6内核下的第一个LED驱动程序测试成功!

    ### Linux 2.6.26.6 内核下的 LED 驱动程序分析 在深入探讨这个特定的 LED 驱动程序之前,我们首先来了解一下 Linux 内核和驱动开发的基本概念。 #### Linux 内核简介 Linux 是一个开源的操作系统内核,由 Linus ...

    linux内核 0.11版本源码 带中文注释

    ├─fs │ bitmap.c │ block_dev.c │ buffer.c │ char_dev.c │ exec.c │ fcntl.c │ file_dev.c │ file_table.c │ inode.c │ ioctl.c │ Makefile │ namei.c │ open.c │ pipe.c │ read_write.c │ stat....

    linux内核指导相关源代码

    #include &lt;linux/fs.h&gt; #include &lt;linux/proc_fs.h&gt; #include &lt;linux/errno.h&gt; #include &lt;linux/types.h&gt; #include &lt;linux/fcntl.h&gt; #include &lt;linux/init.h&gt; #include &lt;linux/poll.h&gt; #include &lt;asm/uaccess.h&gt; #...

Global site tag (gtag.js) - Google Analytics