曲径通幽论坛

标题: 内核定时器简单实例 [打印本页]

作者: beyes    时间: 2009-10-9 16:22
标题: 内核定时器简单实例
使用内核定时器控制打印机端口上的 LED 。程序代码如下:
#include <linux/init.h>                                                                 
#include <linux/module.h>                                                               
#include <linux/kernel.h>                                                               

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>

#include <asm/uaccess.h>
#include <asm/io.h>    

#include <linux/time.h>
#include <linux/timer.h>

#define KERNELTIMER_WRITE_ADDR  0x378

#define TIME_STEP       (2*HZ / 10)



typedef struct {
        struct timer_list timer;
        unsigned long led;     
}__attribute__((packed)) KERNEL_TIMER_MANAGER;

static KERNEL_TIMER_MANAGER *ptrmng = NULL;

void kerneltimer_timeover (unsigned long arg);


void kerneltimer_registertimer (KERNEL_TIMER_MANAGER *pdata, unsigned long timeover)
{                                                                                  
        init_timer (&(pdata->timer));   /*初始化 timer_list 结构*/                 
        pdata->timer.expires = get_jiffies_64() + timeover;                        
        pdata->timer.data = (unsigned long) pdata;      /*指针地址转换为整数暂存*/ 

        /*设置超时后处理函数,(unsigned long)型pdata变量超时后自动传入kerneltimer_timerover函数作为其参数*/
        pdata->timer.function = kerneltimer_timeover;                                                    

        add_timer (&(pdata->timer));                    /*注册 timer 结构体*/

}

void kerneltimer_timeover (unsigned long arg)
{                                           
        KERNEL_TIMER_MANAGER *pdata = NULL

        if (arg) {
                pdata = (KERNEL_TIMER_MANAGER *) arg;

                outb ((unsigned char) (pdata->led & 0xFF), KERNELTIMER_WRITE_ADDR);

                pdata->led = ~(pdata->led);

                kerneltimer_registertimer(pdata, TIME_STEP);
        }                                                  
}                                                          

int kerneltimer_init (void)
{                         
        ptrmng = kmalloc (sizeof (KERNEL_TIMER_MANAGER), GFP_KERNEL);
        if (ptrmng == NULL) return -ENOMEM;

        memset (ptrmng, 0, sizeof (KERNEL_TIMER_MANAGER));
        ptrmng->led = 0;

        kerneltimer_registertimer (ptrmng, TIME_STEP);

        return 0;
}

void kerneltimer_exit (void)
{
        if (ptrmng != NULL) {
                del_timer (&(ptrmng->timer));
                kfree (ptrmng);
        }
        outb (0x00, KERNELTIMER_WRITE_ADDR);
}

module_init (kerneltimer_init);
module_exit (kerneltimer_exit);

MODULE_LICENSE ("Dual BSD/GPL");
说明
在 kerneltimer_init() 函数中
ptrmng 指向地址上分配了 KERNEL_TIMER_MANAGER 结构体内存空间。
ptrmng->led = 0 表示一开始 LED 设置为关闭状态。
最后调用 kerneltimer_registertimer() 函数,此函数的主要目的是初始化内核定时器相关变量,并在内核定时器目录上注册内核定时器函数。

在 kerneltimer_registertimer() 函数中:
首先使用 init_timer() 函数,该函数初始化 struct timer_list 结构,这个结构一般在设备驱动程序里无需关心其内部结构。

pdata
->timer.expires = get_jiffies_64() + timeover;  这条语句设定了超时时间,get_jiffies_64() 得到当前时间,timerover 是超时时间,这里由 TIME_STEP 宏指定,是 0.2s 时长。

pdata->timer.data = (unsigned long) pdata;  这条语句把原来指向结构体的指针强制转换为 unsigned long 类型存到 pdata->timer.data 中,这个 timer.data 的值在发生内核超时中断时,会自动作为超时处理函数的参数传入超时处理函数中。

pdata->timer.function = kerneltimer_timeover;   //设置超时候处理函数 kerneltimer_timerover ,并通过 add_timer() 函数实现注册。当 jiffies_64 的值大于或等于 expires 域时,会调用到这个函数。kerneltimer_timerover() 会最先确认其参数 arg 上有无传送的值(在本例中,arg 的值就是注册到内核定时器和包含 led 状态结构体的地址)。当检查到 arg 上有传送值时,就把该值强制转换(casting)后再代入到 pdata 变量上。

最后,再调用 kerneltimer_registertimer() 函数,实现自身的再次注册,于是又一轮新的循环开始,这个循环周期就是 TIME_STEP 为 0.2s 。

在 insmod 模块到内核中时,会看到并口上的 LED 灯以 0.2s 的间隔闪烁。当 rmmod 模块时,就会相应的调用 kerneltimer_exit() 函数,实现了内核模块的注销。kerneltimer_exit() 函数里要检查 ptrmng 全局变量上是否有分配内存,如果有,则要通过 kfree() 将其释放,以免内存泄露。最后调用 del_timer() 函数终止掉 kerneltimer_timerover() 函数的调用,kerneltimer_timerover() 函数就会处于结束运行并已注销的状态。




欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/) Powered by Discuz! X3.2