内核定时器

Linux内核定时器是timer_list,下边我们详尽介绍定时器的使用。

1.简介

内核定时器是内核拿来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,其实现坐落和kernel/timer.c文件中。

被调度的函数肯定是异步执行的,它类似于一种“软件中断”,并且是处于非进程的上下文中,所以调度函数必须遵循以下规则:

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

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

2.数据结构

(1)内核定时器的数据结构

    struct timer_list {
      struct list_head entry, /*定时器列表*/
      unsigned long expires, /*定时器到期时间*/
      void (*function) (unsigned long), /*定时器处理函数*/
      unsigned long data,/*作为参数被传入定时器处理函数*/
      struct timer_base_s *base,
      ...
    };

其中expires数组表示期望定时器执行的jiffies值,抵达该jiffies值时,将调用function函数,并传递data作为参数。

jiffies当一个定时器被注册到内核以后,entry数组拿来联接该定时器到一个内核数组中。

base数组是内核内部实现所用的。

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

(2)初始化定时器

方式一:

DEFINE_TIMER(timer_name, function_name, expires_value, data);

该宏会定义一个名叫timer_name内核定时器,并初始化其function,expires,name和base数组。

技巧二:

struct timer_list mytimer;
void init_timer(struct timer_list *timer);

上述init_timer函数将初始化structtimer_list的entry的next为NULL调用linux内核函数,并为base表针形参

(3)降低定时器定时器要生效,还必须被联接到内核专门的数组中,这可以通过add_timer(structtimer_list*timer)来实现。

void add_timer (struct timer_list *timer);

(4)删掉定时器注销一个定时器linux培训机构linux运维招聘,可以通过del_timer(structtimer_list*timer)或del_timer_sync(structtimer_list*timer)。

int del_timer (struct timer_list *timer);
int del_timer_sync(struct timer_list *timer)

其中del_timer_sync是用在SMP系统上的(在非SMP系统上,它等于del_timer)调用linux内核函数,当要被注销的定时器函数正在另一个cpu上运行时,del_timer_sync()会等待其运行完,所以这个函数会休眠。

另外还应防止它和被调度的函数中用同一个锁。对于一个早已被运行过且没有重新注册自己的定时器而言,注销函数显然也没哪些事可做。

int timer_pending(const struct timer_list *timer);

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

(5)更改定时器的expire要更改一个定时器的调度时间,可以通过调用mod_timer(structtimer_list*timer,unsignedlongexpires)。mod_timer()会重新注册定时器到内核,而不管定时器函数是否被运行过。

int mod_timer (struct timer_list *timer, unsigned long expires);

(6)对于周期性的任务,linux内核还提供了一种delayed_work机制来完成,本质上用工作队列和定时器实现。

3.举例例1:实现每隔1秒向内核log中复印一条信息

/* 实现每隔一秒向内核log中打印一条信息 */
#include 
#include 
#include 
#include 
#include 

调用linux内核函数_linux内核函数和系统调用_linux内核调试方法总结

static struct timer_list tm; struct timeval oldtv; void callback(unsigned long arg) {     struct timeval tv;     char *strp = (char*)arg;          printk("%s: %lu, %sn", __func__, jiffies, strp);     do_gettimeofday(&tv);     printk("%s: %ld, %ldn", __func__,         tv.tv_sec - oldtv.tv_sec,        //与上次中断间隔 s         tv.tv_usec- oldtv.tv_usec);        //与上次中断间隔 ms          oldtv = tv;     tm.expires = jiffies+1*HZ;         add_timer(&tm);        //重新开始计时 } static int __init demo_init(void) {     printk(KERN_INFO "%s : %s : %d - ok.n", __FILE__, __func__, __LINE__);     init_timer(&tm);    //初始化内核定时器     do_gettimeofday(&oldtv);        //获取当前时间     tm.function= callback;            //指定定时时间到后的回调函数     tm.data    = (unsigned long)"hello world";        //回调函数的参数     tm.expires = jiffies+1*HZ;        //定时时间     add_timer(&tm);        //注册定时器     return 0; } static void __exit demo_exit(void) {     printk(KERN_INFO "%s : %s : %d - ok.n", __FILE__, __func__, __LINE__);     del_timer(&tm);        //注销定时器 } module_init(demo_init); module_exit(demo_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("yikoupeng"); MODULE_DESCRIPTION("timerlist");

例2:秒字符设备

second_drv.c

1 #include 
  2 #include 
  3 #include 
  4 #include 
  5 #include 
  6 #include 
  7 #include 
  8 #include 
  9 #include 
 10 #include 
 11 #include 
 12 #include 
 13 

调用linux内核函数_linux内核调试方法总结_linux内核函数和系统调用

 14 #define SECOND_MAJOR 248  15   16 static int second_major = SECOND_MAJOR;  17   18 struct second_dev {  19     struct cdev cdev;  20     atomic_t counter;  21     struct timer_list s_timer;  22 };  23   24 struct second_dev *second_devp;  25 static void second_timer_handle (unsigned long arg)  26 {  27     mod_timer (&second_devp->s_timer, jiffies + HZ);  28     atomic_inc (&second_devp->counter);  29     printk (KERN_NOTICE "current jiffies is %ldn", jiffies);  30 }  31 int second_open (struct inode *inode, struct file *filp)  32 {  33     init_timer (&second_devp->s_timer);  34     second_devp->s_timer.function = &second_timer_handle;  35     second_devp->s_timer.expires = jiffies + HZ;  36     add_timer (&second_devp->s_timer);  37     atomic_set (&second_devp->counter, 0);  38     return 0;  39 }  40 int second_release (struct inode *inode, struct file *filp)  41 {  42     del_timer (&second_devp->s_timer);  43     return 0;  44 }  45 static ssize_t second_read (struct file *filp, char __user *buf,  46         size_t count, loff_t *ppos)  47 {  48     int counter;  49     counter = atomic_read (&second_devp->counter);  50     if (put_user (counter, (int *)buf))  51         return -EFAULT;  52     else  53         return sizeof (unsigned int);  54 }  55 static const struct file_operations second_fops = {  56     .owner = THIS_MODULE,  57     .open = second_open,

调用linux内核函数_linux内核调试方法总结_linux内核函数和系统调用

 58     .release = second_release,  59     .read = second_read,  60 };  61 static void second_setup_cdev (struct second_dev *dev, int index)  62 {  63     int err, devno = MKDEV (second_major, index);  64     cdev_init (&dev->cdev, &second_fops);  65     dev->cdev.owner = THIS_MODULE;  66     err = cdev_add (&dev->cdev, devno, 1);  67     if (err)  68         printk (KERN_NOTICE "Error %d adding CDEV %d", err, index);  69 }  70 int second_init (void)  71 {  72     int ret;  73     dev_t devno = MKDEV (second_major, 0);  74     if (second_major)  75         ret = register_chrdev_region (devno, 1, "second");  76     else {  77         return alloc_chrdev_region (&devno, 0, 1, "second");  78         second_major = MAJOR (devno);  79     }  80     if (ret cdev);  97     kfree (second_devp);  98     unregister_chrdev_region (MKDEV (second_major, 0), 1);  99 } 100 MODULE_AUTHOR ("yikoupeng"); 101 MODULE_LICENSE ("Dual BSD/GPL");

linux内核函数和系统调用_调用linux内核函数_linux内核调试方法总结

102 module_param (second_major, int, S_IRUGO); 103 module_init (second_init); 104 module_exit (second_exit);

second_test.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main (void)
{
    int fd;
    int counter = 0;
    int old_counter = 0;
    fd = open ("/dev/second", O_RDONLY);
    if (fd != -1) {
        while (1) {
            read (fd, &counter, sizeof (unsigned int));
            if (counter != old_counter) {
                printf ("seconds after open /dev/second: %dn", 
                        counter);
                old_counter = counter;
            }
        }
    } else {
        printf ("Device open failuren");
    }
    return 0;
}

4.其他时间函数、宏介绍1)节拍率

系统定时器的频度;通过静态预处理定义的——HZ;系统启动根据HZ值对硬件进行设置。体系结构不同,HZ值也不同;HZ可变的。

//内核时间频度

#define HZ 1000

增强节拍率中断形成愈加频繁带来的益处:

增强节拍率带来的副作用:

所以:节拍率HZ值须要在其中进行平衡。

2)jiffies概念jiffies:全局变量,拿来记录自系统启动以来形成的节拍总量。启动时内核将该变量初始化为0;

linux内核调试方法总结_linux内核函数和系统调用_调用linux内核函数

随后每次时钟中断处理程序降低该变量的值。

每一秒钟中断次数HZ,jiffies1秒内降低HZ。

系统运行时间=jiffie/HZ.

jiffies用途:估算流逝时间和时间管理

头文件:

linux/jiffies.h

定义:

extern u64 jiffies_64;
extern unsigned long volatile jiffies;     //位长更系统有关32/64

可以估算一下:32位:497天后溢出

64位:天文数字

举例:0.5秒后超时

//0.5秒后超时
unsigned long timeout = jiffies + HZ/2;
//注意jiffies值溢出回绕用宏time_before 而非 直timeout > jiffies
if(time_before(jiffies,timeout)){
       //没有超时
}else{
       //超时
}

3)时间函数do_gettimeofday简介:在Linux中可以使用函数do_gettimeofday()函数来得到精确时间。它的精度可以达到微妙,是与C标准库中gettimeofday()用法相同的函数。在Linux内核中获得时间的函数。函数原型:

linux/time.h
void do_gettimeofday(struct timeval *tv)

说明:do_gettimeofday()会把目前的时间用tv结构体返回,当地时区的信息则放在tz所指的结构中结构体:timeval结构体定义:

struct timeval { 
  time_t tv_sec;       /* seconds */
  suseconds_t tv_usec; /* microseconds */ 
};

struct  timeval   tv_begin,tv_end;
do_gettimeofday(&tv_begin,NULL);
…………
do_gettimeofday(&tv_end,NULL);
printk(“tv_begin_sec:%dn”,tv_begin.tv_sec);
printk(“tv_begin_usec:%dn”,tv_begin.tv_usec);
printk(“tv_end_sec:%dn”,tv_end.tv_sec);
printk(“tv_end_usec:%dn”,tv_end.tv_usec);

Author

这篇优质的内容由TA贡献而来

刘遄

《Linux就该这么学》书籍作者,RHCA认证架构师,教育学(计算机专业硕士)。

发表回复