【驱动】内核定时器的使用

链接:https://blog.csdn.net/jidonghui/article/details/7449546

LINUX内核定时器是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,其实现位于 <linux/timer.h> 和 kernel/timer.c 文件中。被调度的函数肯定是异步执行的,它类似于一种“软件中断”,而且是处于非进程的上下文中,所以调度函数必须遵守以下规则:

1) 没有 current 指针、不允许访问用户空间。因为没有进程上下文,相关代码和被中断的进程没有任何联系。

2) 不能执行休眠(或可能引起休眠的函数)和调度。

3) 任何被访问的数据结构都应该针对并发访问进行保护,以防止竞争条件。

内核定时器的调度函数运行过一次后就不会再被运行了(相当于自动注销),但可以通过在被调度的函数中重新调度自己来周期运行。

在SMP系统中,调度函数总是在注册它的同一CPU上运行,以尽可能获得缓存的局域性。

定时器API

内核定时器的数据结构

struct timer_list {

struct list_head entry;

unsigned long expires;

void (*function)(unsigned long);

unsigned long data;

struct tvec_base *base;

/* ... */

};

其中 expires 字段表示期望定时器执行的 jiffies 值,到达该 jiffies 值时,将调用 function 函数,并传递 data 作为参数。当一个定时器被注册到内核之后,entry 字段用来连接该定时器到一个内核链表中。base 字段是内核内部实现所用的。

需要注意的是 expires 的值是32位的,因为内核定时器并不适用于长的未来时间点。

初始化

在使用 struct timer_list 之前,需要初始化该数据结构,确保所有的字段都被正确地设置。初始化有两种方法。

方法一:

DEFINE_TIMER(timer_name, function_name, expires_value, data);

该宏会静态创建一个名叫 timer_name 内核定时器,并初始化其 function, expires, name 和 base 字段。

方法二:

struct timer_list mytimer;

setup_timer(&mytimer, (*function)(unsigned long), unsigned long data);

mytimer.expires = jiffies + 5*HZ;

方法3:

struct timer_list mytimer;

init_timer(&mytimer);

mytimer ->timer.expires = jiffies + 5*HZ;

mytimer ->timer.data = (unsigned long) dev;

mytimer ->timer.function = &corkscrew_timer; /* timer handler */

通过init_timer()动态地定义一个定时器,此后,将处理函数的地址和参数绑定给一个timer_list,

注意,无论用哪种方法初始化,其本质都只是给字段赋值,所以只要在运行 add_timer() 之前,expires, function 和 data 字段都可以直接再修改。

关于上面这些宏和函数的定义,参见 include/linux/timer.h。

注册

定时器要生效,还必须被连接到内核专门的链表中,这可以通过 add_timer(struct timer_list *timer) 来实现。

重新注册

要修改一个定时器的调度时间,可以通过调用 mod_timer(struct timer_list *timer, unsigned long expires)。mod_timer() 会重新注册定时器到内核,而不管定时器函数是否被运行过。

注销

注销一个定时器,可以通过 del_timer(struct timer_list *timer) 或 del_timer_sync(struct timer_list *timer)。其中 del_timer_sync 是用在 SMP 系统上的(在非SMP系统上,它等于del_timer),当要被注销的定时器函数正在另一个 cpu 上运行时,del_timer_sync() 会等待其运行完,所以这个函数会休眠。另外还应避免它和被调度的函数争用同一个锁。对于一个已经被运行过且没有重新注册自己的定时器而言,注销函数其实也没什么事可做。

int timer_pending(const struct timer_list *timer)

这个函数用来判断一个定时器是否被添加到了内核链表中以等待被调度运行。注意,当一个定时器函数即将要被运行前,内核会把相应的定时器从内核链表中删除(相当于注销)

一个简单的例子

#include <linux/module.h>

#include <linux/timer.h>

#include <linux/jiffies.h>

struct timer_list mytimer;

static void myfunc(unsigned long data)

{

printk("%s/n", (char *)data);

mod_timer(&mytimer, jiffies + 2*HZ);

}

static int __init mytimer_init(void)

{

setup_timer(&mytimer, myfunc, (unsigned long)"Hello, world!");

mytimer.expires = jiffies + HZ;

add_timer(&mytimer);

return 0;

}

static void __exit mytimer_exit(void)

{

del_timer(&mytimer);

}

module_init(mytimer_init);

module_exit(mytimer_exit);

例子2

static struct timer_list power_button_poll_timer;

static void power_button_poll(unsigned long dummy)

{

if (gpio_line_get(N2100_POWER_BUTTON) == 0) {

ctrl_alt_del();

return;

}

power_button_poll_timer.expires = jiffies + (HZ / 10);

add_timer(&power_button_poll_timer);

}

static void __init n2100_init_machine(void)

{

init_timer(&power_button_poll_timer);

power_button_poll_timer.function = power_button_poll;

power_button_poll_timer.expires = jiffies + (HZ / 10);

add_timer(&power_button_poll_timer);

}

例子3

设备open时初始化和注册定时器

static int corkscrew_open(struct net_device *dev)

{

init_timer(&vp->timer);

vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;

vp->timer.data = (unsigned long) dev;

vp->timer.function = &corkscrew_timer; /* timer handler */

add_timer(&vp->timer);

}

定时器超时处理函数,对定时器的超时时间重新赋值

static void corkscrew_timer(unsigned long data)

{

vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;

add_timer(&vp->timer);

}

设备close时删除定时器

static int corkscrew_close(struct net_device *dev)

{

del_timer(&vp->timer);

}

例子4

本例子用DEFINE_TIMER静态创建定时器

#include <linux/module.h>

#include <linux/jiffies.h>

#include <linux/kernel.h>

#include <linux/init.h>

#include <linux/timer.h>

#include <linux/leds.h>

static void ledtrig_ide_timerfunc(unsigned long data);

DEFINE_LED_TRIGGER(ledtrig_ide);

static DEFINE_TIMER(ledtrig_ide_timer, ledtrig_ide_timerfunc, 0, 0);

static int ide_activity;

static int ide_lastactivity;

void ledtrig_ide_activity(void)

{

ide_activity++;

if (!timer_pending(&ledtrig_ide_timer))

mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));

}

EXPORT_SYMBOL(ledtrig_ide_activity);

static void ledtrig_ide_timerfunc(unsigned long data)

{

if (ide_lastactivity != ide_activity) {

ide_lastactivity = ide_activity;

led_trigger_event(ledtrig_ide, LED_FULL);

mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));

} else {

led_trigger_event(ledtrig_ide, LED_OFF);

}

}

static int __init ledtrig_ide_init(void)

{

led_trigger_register_simple("ide-disk", &ledtrig_ide);

return 0;

}

static void __exit ledtrig_ide_exit(void)

{

led_trigger_unregister_simple(ledtrig_ide);

}

module_init(ledtrig_ide_init);

module_exit(ledtrig_ide_exit);

==================================================================================

add_timer() -- 将定时器添加到定时器等待队列中

2007年08月04日 星期六 15:30


用add_timer()函数来看timer_base的作用

static inline void add_timer(struct timer_list *timer)
{
    BUG_ON(timer_pending(timer));
    __mod_timer(timer, timer->expires);
}

int __mod_timer(struct timer_list *timer, unsigned long expires)
{
    tvec_base_t *base, *new_base;
    unsigned long flags;
    int ret = 0;

timer_stats_timer_set_start_info(timer);
    BUG_ON(!timer->function);

base = lock_timer_base(timer, &flags);

如果timer已经放到定时链表中,则释放开
|--------------------------------|
|   if (timer_pending(timer)) { -|
|       detach_timer(timer, 0); -|
|       ret = 1;                 |
|   }                            |
|--------------------------------|

获取当前CPU的timer base
|-----------------------------------------|
|   new_base = __get_cpu_var(tvec_bases); |
|-----------------------------------------|

如果当前CPU的timer base不是当前timer中的base, 更新timer的base
|----------------------------------------------------|
|   if (base != new_base) {                          |
|       if (likely(base->running_timer != timer)) { -|
|           timer->base = NULL;                      |
|           spin_unlock(&base->lock);                |
|           base = new_base;                         |
|           spin_lock(&base->lock);                  |
|           timer->base = base;                      |
|       }                                            |
|   }                                                |
|----------------------------------------------------|

给定时器timer设置超时时间;并添加该时钟
|-------------------------------------|
|   timer->expires = expires;         |
|   internal_add_timer(base, timer); -|
|-------------------------------------|
    spin_unlock_irqrestore(&base->lock, flags);
    return ret;
}

MODULE_AUTHOR("Richard Purdie <[email protected]>");

MODULE_DESCRIPTION("LED IDE Disk Activity Trigger");

MODULE_LICENSE("GPL");

总的来说,timer的用法还是很简单的。主要需要定义一个timer_list变量timer、先初始化timer 
  init_timer(&timer); 
  then 对timer的相关参数赋值: 
  timer.function = fun; 
  timer.expires = jiffies + TIMER_DELAY; 
  add_timer(&timer); 
  在定时器时间到的时候,会执行fun,如果继续定时,可以通过 
  在fun中执行 
  mod_timer(&timer, jiffies + TIMER_DELAY); 
  在不需要的时候通过调用 
  del_timer(&timer); 
  删除定时器。 
  简单吧。这样一个简单的定时器就完成了。 
  呵呵。 
  附程序: 
  
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/fs.h>
  #include <linux/errno.h>
  #include <linux/mm.h>
  #include <linux/sched.h>
  #include <linux/init.h>
  #include <linux/cdev.h>
  #include <asm/io.h>
  #include <asm/system.h>
  #include <asm/uaccess.h>
  #include <linux/timer.h>
  #include <asm/atomic.h>
  #define SECOND_MAJOR 0
  static int second_major = SECOND_MAJOR;
  struct second_dev
  {
  struct cdev cdev;
  atomic_t counter;
  struct timer_list s_timer;
  };
  struct second_dev *second_devp;
  static void second_timer_handle(unsigned long arg)
  {
  mod_timer(&second_devp->s_timer, jiffies + HZ);
  atomic_inc(&second_devp->counter);
  printk(KERN_ERR "current jiffies is %ld/n",jiffies);
  }
  int second_open(struct inode *inode, struct file *filp)
  {
  init_timer(&second_devp->s_timer);
  second_devp->s_timer.function = &second_timer_handle;
  second_devp->s_timer.expires = jiffies + HZ;
  add_timer(&second_devp->s_timer);
  atomic_set(&second_devp->counter, 0);
  return 0; 
  }
  int second_release(struct inode *inode, struct file *filp)
  {
  del_timer(&second_devp->s_timer);
  return 0;
  }
  static ssize_t second_read(struct file *filp, char __user *buf, size_t count, 
  loff_t *ppos)
  {
  int counter;
  counter = atomic_read(&second_devp->counter);
  if (put_user(counter, (int *)buf))
  {
  return -EFAULT;
  }else
  {
  return sizeof(unsigned int);
  }
  }
  static const struct file_operations second_fops =
  {
  .owner = THIS_MODULE,
  .open = second_open,
  .release = second_release,
  .read = second_read,
  };
  static void second_setup_cdev(struct second_dev *dev, int index)
  {
  int err, devno = MKDEV(second_major, index);
  cdev_init(&dev->cdev, &second_fops);
  dev->cdev.owner = THIS_MODULE;
  dev->cdev.ops = &second_fops;
  err = cdev_add(&dev->cdev, devno, 1);
  if (err)
  {
  printk(KERN_NOTICE "Error %d add second%d", err, index);
  }
  }
  int second_init(void)
  {
  int ret;
  dev_t devno = MKDEV(second_major, 0);
  if (second_major)
  {
  ret = register_chrdev_region(devno, 1, "second");
  }else
  {
  ret = alloc_chrdev_region(&devno, 0, 1, "second");
  second_major = MAJOR(devno);
  }
  if (ret < 0)
  {
  return ret;
  }
  second_devp = kmalloc(sizeof(struct second_dev), GFP_KERNEL);
  if (!second_devp)
  {
  ret = -ENOMEM;
  goto fail_malloc;
  }
  memset(second_devp, 0, sizeof(struct second_dev));
  second_setup_cdev(second_devp, 0);
  return 0;
  fail_malloc:
  unregister_chrdev_region(devno, 1);
  }
  void second_exit(void)
  {
  cdev_del(&second_devp->cdev);
  kfree(second_devp);
  unregister_chrdev_region(MKDEV(second_major, 0), 1);
  }
  MODULE_AUTHOR("Song Baohua");
  MODULE_LICENSE("Dual BSD/GPL");
  module_param(second_major, int, S_IRUGO);
  module_init(second_init);
  module_exit(second_exit);
  附上用户端的测试程序:
  #include <stdio.h>
  #include <unistd.h>
  #include <fcntl.h>
  int main(void)
  {
  int fd, i;
  int data;
  fd = open("/dev/second",O_RDONLY);
  if (fd < 0)
  {
  printf("open /dev/second error/n");
  }
  for(i = 0; i < 20; i++)
  {
  read(fd, &data, sizeof(data));
  printf("read /dev/second is %d/n",data);
  sleep(1);
  }
  close(fd);
  }

原文地址:https://www.cnblogs.com/xiaohujian/p/9996861.html

时间: 2024-11-10 18:24:25

【驱动】内核定时器的使用的相关文章

linux驱动之定时器的介绍和内核时间的学习

本文章摘自下面的网友: http://blog.sina.com.cn/s/blog_6e5b342e0100m87d.html 一.内核中如何记录时间 任何程序都需要时间控制,其主要目的是: 测量时间流逝和比较时间 知道当前时间 指定时间量的延时操作 为达到这个目的,应用程序使用日历时间(年月日时分秒)或者自1970年1月1日零时零分零秒到当前的秒数来度量时间的流逝,但内核中需要更加有精度的时间度量,因此内核使用时钟嘀嗒来记录时间.时钟中断发生后内核内部时间计数器增加1(即:增加1个时钟嘀嗒)

Linux 内核开发 - 内核定时器

时间差的度量 系统的定时器硬件以固定的频率产生时钟中断,产生始终中断的间隔以HZ 常量来决定,通常在50~1200之间,x86默认是1000,HZ可以根据不同的内核来配置. Linux 采用jiffies (unsigned long)来对时钟中断进行计数,每当发生时钟中断时jiffies的值将+1,因此jiffies就记录了系统开机以来的时钟中断总次数.在驱动开发过程中经常会使用时钟中断来计算不同事件的时间间隔. 延迟执行 对于不精确的时间延迟要求,可以使用while 循环来计算延迟的时间.

Linux内核——定时器和时间管理

定时器和时间管理 系统定时器是一种可编程硬件芯片.它能以固定频率产生中断.该中断就是所谓的定时器中断.它所相应的中断处理程序负责更新系统时间,还负责执行须要周期性执行的任务. 系统定时器和时钟中断处理程序是Linux系统内核管理机制中的中枢. 另外一个关注的焦点是动态定时器--一种用来推迟运行程序的工具. 比方说.假设软驱马达在一定时间内都未活动,那么软盘驱动程序会使用动态定时器关闭软驱马达. 内核能够动态创建或销毁动态定时器. 内核中的时间观念 内核在硬件的帮助下计算和管理时间. 硬件为内核提

linux2.4.18内核定时器的使用

Linux2.4下驱动中定时器的应用 我的内核是2.4.18的.Linux的内核中定义了一个定时器的结构: #include<linux/timer.h> struct timer_list { struct list_head list; unsigned long expires; //定时器到期时间 unsigned long data; //作为参数被传入定时器处理函数 void (*function)(unsigned long); }; 利用这个结构我们可以在驱动中很方便的使用定时

把握linux内核设计(七):内核定时器和定时执行

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 前面章节说到了把工作推后到除现在以外的时间执行的机制是下半部机制,但是当你需要将工作推后到某个确定的时间段之后执行,使用定时器是很好的选择. 上一节内核时间管理中讲到内核在始终中断发生执行定时器,定时器作为软中断在下半部上下文中执行.时钟中断处理程序会执行update_process_times函数,在该函数中运行run_local_timers()函数来标记一个软中断去处理

linux 内核定时器详解

原文摘自:http://www.linux-cn.com/html/linux/kernel/20070412/1886.shtml Linux内核2.4版中去掉了老版本内核中的静态定时器机制,而只留下动态定时器.相应地在timer_bh()函数中也不再通过run_old_timers()函数来运行老式的静态定时器.动态定时器与静态定时器这二个概念是相对于Linux内核定时器机制的可扩展功能而言的,动态定时器是指内核的定时器队列是可以动态变化的,然而就定时器本身而言,二者并无本质的区别.考虑到静

模仿linux内核定时器代码,用python语言实现定时器

大学无聊的时候看过linux内核的定时器,现在已经想不起来了,也不知道当时有没有看懂,现在想要模仿linux内核的定时器,用python写一个定时器,已经想不起来它的设计原理了,找了一篇blog,linux 内核定时器 timer_list详解. 看了好一会才有些明白,开始参照着用python写了一个.如果在设计服务器的时候,有大量需要精确到秒和秒以下的事件,自己写一个定时器,维护一个类似与内核timer_vec的数据结构,处理服务的定时事件,还是蛮高效的. 附上python代码,github:

4.内核定时器的使用

下面代码是本人在项目中遇到使用定时器的一个例子,可以编译运行正确的结果 1 #include <linux/timer.h> 2 3 struct timer_list timer; //内核定时器全局变量 4 5 void timer_init() //放在适当的地方,以便被正确地调用 6 { 7 timer.data = 0; 8 timer.expires = jiffies + 1*HZ; 9 timer.function = (int* )timer_handler; 10 add_

一个内核定时器模块

/* 内核定时器测试模块 */ #include <linux/kernel.h> //内核 #include <linux/module.h> //内核模块 #include <linux/init.h> //模块初始化和注销 #include <linux/slab.h> //屋里内存分配管理 #include <linux/list.h> //内核链表 #include <linux/timer.h> //内核定时器 //内核模