Smart210学习记录-----linux定时器

1.内核定时器:

  Linux 内核所提供的用于操作定时器的数据结构和函数如下:

(1) timer_list

  在 Linux 内核中,timer_list 结构体的一个实例对应一个定时器

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

当定时器期满后,其中第 5 行的 function()成员将被执行,而第 4 行的 data 成员则是传入其中的参数,第 3 行的 expires 则是定时器到期的时间(jiffies)。

如下代码定义一个名为 my_timer 的定时器:
struct timer_list my_timer;

  (2)初始化定时器

    void init_timer(struct timer_list * timer);

    上述 init_timer()函数初始化 timer_list 的 entry 的 next 为 NULL,并给 base 指针赋值

    TIMER_INITIALIZER(_function, _expires, _data)宏用于赋值定时器结构体的function、expires、
    data 和 base 成员

    DEFINE_TIMER(_name,  _function,  _expires,  _data)宏是定义并初始化定时器成员的“快捷方
    式”。

    此外,static inline void setup_timer(struct timer_list * timer, void (*function)(unsigned long),unsigned long data)
    也可用于初始化定时器并赋值其成员

  (3)增加定时器

    void add_timer(struct timer_list* timer);

   (4)删除定时器

    int  del_timer(struct timer_list* timer);

  del_timer_sync()是 del_timer()的同步版,在删除一个定时器时需等待其被处理完,因此该函数的调用不能发生在中断上下文。

   (5).修改定时器的 expire

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

  上述函数用于修改定时器的到期时间,在新的被传入的 expires 到来后才会执行定时器函数。

2.内核中延时的工作delayed_work

注意,对于这种周期性的任务,Linux 内核还提供了一套封装好的快捷机制,其本质利用工作队列
和定时器实现,这套快捷机制是就是delayed_work,delayed_work结构体的定义如代码清单10.11所示。
代码清单 10.11   delayed_work 结构体
1  struct delayed_work {
2                struct work_struct work;
3                struct timer_list timer;
4  };
5  struct work_struct {

6                atomic_long_t data;
7  #define WORK_STRUCT_PENDING 0 
8  #define WORK_STRUCT_FLAG_MASK (3UL)
9  #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
10               struct list_head entry;
11               work_func_t func;
12 #ifdef CONFIG_LOCKDEP
13               struct lockdep_map lockdep_map;
14 #endif
15 };

我们可以通过如下函数调度一个 delayed_work 在指定的延时后执行:
int schedule_delayed_work(struct delayed_work *work, unsigned long delay);
当指定的 delay 到来时 delayed_work 结构体中 work 成员的 work_func_t 类型成员 func()会被
执行。work_func_t 类型定义为:
typedef void (*work_func_t)(struct work_struct *work);
其中 delay 参数的单位是 jiffies,因此一种常见的用法如下:
schedule_delayed_work(&work, msecs_to_jiffies(poll_interval));
其中的 msecs_to_jiffies()用于将毫秒转化为 jiffies。
如果要周期性的执行任务,通常会在 delayed_work 的工作函数中再次调用 schedule_delayed_
work(),周而复始。
如下函数用来取消 delayed_work:
int cancel_delayed_work(struct delayed_work *work);
int cancel_delayed_work_sync(struct delayed_work *work);

3.内核延时

Linux 内核中提供了如下 3 个函数分别进行纳秒、微秒和毫秒延迟:
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);
上述延迟的实现原理本质上是忙等待,它根据 CPU 频率进行一定次数的循环。

毫秒时延(以及更大的秒时延)已经比较大了,在内核中,最好不要直接使用 mdelay()函数,这将无谓地耗费 CPU 资源,对于毫秒级以上时延,内核提供了下述函数:
void msleep(unsigned int millisecs);
unsigned long msleep_interruptible(unsigned int millisecs);
void ssleep(unsigned int seconds);
上述函数将使得调用它的进程睡眠参数指定的时间,msleep()、ssleep()不能被打断,而msleep_interruptible()则可以被打断。

秒设备驱动程序:

#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/slab.h>
 #include <linux/platform_device.h>

static unsigned char timermajor = 0;
#define   TIMERNAME    "mytimer"

static struct class *timer_class;
static struct device *timer_device;

struct timer_dev {
    struct cdev cdev;
    atomic_t counter;
    struct timer_list  mytimer;
};

struct timer_dev *my_timer_dev;

static void my_timer_fun(unsigned int arg)
{
    mod_timer(&my_timer_dev->mytimer, jiffies + HZ);
    atomic_inc(&my_timer_dev->counter);
    printk(KERN_NOTICE "current jiffies is %ld\n", jiffies);
}

static int my_timer_open(struct inode * inode, struct file * file)
{
    init_timer( &my_timer_dev->mytimer);
    my_timer_dev->mytimer.expires = jiffies+ HZ;
    my_timer_dev->mytimer.function = & my_timer_fun;

    add_timer(&my_timer_dev->mytimer);
    atomic_set(&my_timer_dev->counter, 0);
    return 0;
}

static ssize_t my_timer_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
    int counter;

    counter = atomic_read(&my_timer_dev->counter);

    if(copy_to_user(buf, &counter, sizeof(int)))
            printk("copy_to_user error\n");

    return 0;
}

static int my_timer_close(struct inode *inode, struct file *file)
{
    del_timer( &my_timer_dev->mytimer);
    return 0;
}

struct file_operations timer_fops = {
    .owner    =    THIS_MODULE,
    .open     =    my_timer_open,
    .read     =    my_timer_read,
    .release  =    my_timer_close,
};

static void timer_setup_cdev(struct timer_dev *dev, int minor )
{
    unsigned char err;
    dev_t deno;
    deno = MKDEV(timermajor, minor);
    cdev_init(&dev->cdev, &timer_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops   = &timer_fops;
    err = cdev_add(&dev->cdev, deno, 1);
    if(err)
        printk(KERN_ALERT"cdev_addd error\n");
}

static int __init my_timer_init(void)
{
    int err = 0;
    dev_t deno;
    if(timermajor) {
        register_chrdev_region(deno, 1, TIMERNAME);
    } else {
        err = alloc_chrdev_region(&deno, 0, 1, TIMERNAME);
        timermajor = MAJOR(deno);
    }
    if(err)
        printk(KERN_ALERT"alloc_chrdev_region error\n");

    printk(KERN_ALERT"timermajor is %d\n", timermajor);

    my_timer_dev = kmalloc(sizeof(struct timer_dev), GFP_KERNEL);
    if(!my_timer_dev) {
        printk(KERN_ALERT"kmalloc error\n");
        return -ENOMEM;
    }
    memset(my_timer_dev, 0, sizeof(struct timer_dev) );

    timer_setup_cdev(my_timer_dev, 0);

    timer_class = class_create(THIS_MODULE, TIMERNAME);
    if(IS_ERR(timer_class)) {
        printk(KERN_ALERT"class_create error\n");
        return -EBUSY;
    }

    timer_device = device_create(timer_class, NULL, deno, NULL, TIMERNAME);
    if(IS_ERR(timer_device)) {
        printk(KERN_ALERT"device_create error\n");
        return -EBUSY;
    }

    return 0;
}

static void __exit my_key_exit(void)
{
    cdev_del(&my_timer_dev->cdev);
    unregister_chrdev_region(MKDEV(timermajor, 0), 1);
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("qigaohua");
module_init(my_timer_init);
module_exit(my_key_exit);

测试程序:

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

int main()
{
    int fd;
    int counter = 0;
    int old_counter = 0;
    fd = open("/dev/mytimer", O_RDWR);
    if(fd < 0) {
        printf("open /dev/mytimer error\n");
        return 0;
    }

    while(1) {
        read(fd, &counter, sizeof(int));
        if(counter != old_counter) {
            old_counter = counter;
            printf("counter is %d\n", counter);
        }
    }
}
时间: 2024-10-25 20:39:39

Smart210学习记录-----linux定时器的相关文章

Smart210学习记录-------linux内核模块

Linux 驱动工程师需要牢固地掌握 Linux 内核的编译方法以为嵌入式系统构建可运行的Linux 操作系统映像.在编译 LDD6410 的内核时,需要配置内核,可以使用下面命令中的 一个: #make config(基于文本的最为传统的配置界面,不推荐使用) #make menuconfig(基于文本菜单的配置界面) #make xconfig(要求 QT 被安装) #make gconfig(要求 GTK+被安装) 在配置Linux 2.6内核所使用的make config.make me

Smart210学习记录-----Linux i2c驱动

一:Linux i2c子系统简介: 1.Linux 的 I2C 体系结构分为 3 个组成部分: (1) I2C 核心. I2C 核心提供了 I2C 总线驱动和设备驱动的注册.注销方法,I2C 通信方法(即“algorithm”)上层的.与具体适配器无关的代码以及探测设备.检测设备地址的上层代码等. (2) I2C 总线驱动. I2C 总线驱动是对 I2C 硬件体系结构中适配器端的实现,适配器可由 CPU 控制,甚至可以直接集成在 CPU 内部. I2C 总线驱动主要包含了 I2C 适配器数据结构

Smart210学习记录------linux串口驱动

转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=27025492&id=327609 一.核心数据结构 串口驱动有3个核心数据结构,它们都定义在<#include linux/serial_core.h> 1.uart_driver uart_driver包含了串口设备名.串口驱动名.主次设备号.串口控制台(可选)等信息,还封装了tty_driver(底层串口驱动无需关心tty_driver). struct

Smart210学习记录-------linux驱动中断

Linux中断 1.申请和释放中断 申请中断 int request_irq(unsigned int irq, irq_handler_t handler,  unsigned long irqflags, const char *devname, void *dev_id) irq 是要申请的硬件中断号. handler 是向系统登记的中断处理函数(顶半部),是一个回调函数,中断发生时,系统调用 这个函数, dev_id 参数将被传递给它. irqflags是中断处理的属性,可以指定中断的触

Smart210学习记录------块设备

转自:http://bbs.chinaunix.net/thread-2017377-1-1.html 本章的目的用尽可能最简单的方法写出一个能用的块设备驱动.所谓的能用,是指我们可以对这个驱动生成的块设备进行mkfs,mount和读写文件.为了尽可能简单,这个驱动的规模不是1000行,也不是500行,而是100行以内. 这里插一句,我们不打算在这里介绍如何写模块,理由是介绍的文章已经满天飞舞了.如果你能看得懂.并且成功地编译.运行了这段代码,我们认为你已经达到了本教程的入学资格,当然,如果你不

我的学习记录--Linux (CentOS) 程序安装包管理,yum

此博客,仅仅只是记录本人学习Linux的学习笔记,和学习经验,本人此时也只是一个初学Linux的菜鸟,所以有写得不对的地方还望包涵.谢谢! 简介: Yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器.基于RPM包管理,能够从指定的服务器自动下载RPM包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软件包,无须繁琐地一次次下载.安装. yum配置文件: 主配置文件:/etc/yum.c

Smart210学习记录-----SD/MMC/SDIO驱动

转自:http://jingpin.jikexueyuan.com/article/23369.html 一.SD/MMC/SDIO概念区分 SD(SecureDigital)与 MMC(MultimediaCard) SD 是一种 flash memory card 的标准,也就是一般常见的 SD 记忆卡,而 MMC 则是较早的一种记忆卡标准,目前已经被 SD 标准所取代.在维基百科上有相当详细的 SD/MMC 规格说明:[http://zh.wikipedia.org/wiki/Secure

我的学习记录--Linux (CentOS) 程序安装包管理,rpm

概述: 因为一个应用程序是由很多文件所组成,所以安装卸载非常复杂,为了让用户更加方便的管理应用程序.所以引入了程序打包管理.主要包括:安装,卸载,升级,查询,检验. 常见的包管理器: .deb: Debian研发 .rpm: Red Hat研发(Linux上的包管理器公共标准,故我学习的为rpm) 常见的CPU平台:(所以选择应用程序安装的时候,一定选择和自己CPU所匹配的版本) x86:i386, i486, i586, i686 x86_64: x86_64, amd64 分包机制: 核心包

Smart210学习记录------nor flash驱动

nor flash驱动与nand flash驱动的差别不大,只是设置不同的结构体而已,, nor flash驱动代码: #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/device.h> #include