1268 字
6 分钟
Linux内核定时器

新版本Kernel下定时器API得到了比较好的优化,以正点原子阿尔法基于4.4版本kernel的旧版API为例做对比:

定时器回调函数:#

旧版:#

void timer_function(unsigned long arg)
{
struct timer_dev *dev = (struct timer_dev *)arg;
static int sta = 1;
/* ....... */
}

新版:#

void timer_function(struct timer_list *t)
{
struct timer_dev *dev = from_timer(dev, t, timer); /* 从定时器实例获取父级结构体 */
static int sta = 1;
/* ....... */
}

新版定时器回调函数形参直接为timer_list实例,而定时器父级结构体不再通过arg获取,kernel提供了from_timer函数:

#define from_timer(var, callback_timer, timer_fieldname) \
container_of(callback_timer, typeof(*var), timer_fieldname)

这个宏自container_of()扩展而来:

/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
* WARNING: any const qualifier of @ptr is lost.
*/
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
static_assert(__same_type(*(ptr), ((type *)0)->member) || \
__same_type(*(ptr), void), \
"pointer type mismatch in container_of()"); \
((type *)(__mptr - offsetof(type, member))); })

container_of能从结构体成员指针推导出其父级结构体的指针:

  • ptr:指向结构体 member 成员的指针。
  • type:包含 member 成员的结构体类型。
  • membertype 结构体中的成员变量名。

扩展后,from_timer能根据以下形参计算出其所属的父级结构体(这里为timer_dev)的指针:

  • var:目标结构体的变量(不是指针,而是变量名)。
  • callback_timer:指向 timer_list 结构体的指针(定时器回调传入的参数)。
  • timer_fieldnametimer_listvar 结构体中的字段名。

定时器初始化函数#

旧版#

旧版API比较复杂,还要手动设置结构体成员:

init_timer(&timerdev.timer);
timerdev.timer.function = timer_function;
timerdev.timer.data = (unsigned long)&timerdev;

新版#

Kernel提供timer_setup(),这个宏实际上由init_timer_key()函数扩展而来:

#define timer_setup(timer, callback, flags) \
__init_timer((timer), (callback), (flags))
#define __init_timer(_timer, _fn, _flags) \
init_timer_key((_timer), (_fn), (_flags), NULL, NULL)
void init_timer_key(struct timer_list *timer, void (*func)(struct timer_list*), unsigned int flags, const char *name, struct lock_class_key *key)
usage: initialize a timer
Parameters:
struct timer_list *timer
the timer to be initialized
void (*func)(struct timer_list *)
timer callback function
unsigned int flags
timer flags
const char *name
name of the timer
struct lock_class_key *key
lockdep class key of the fake lock used for tracking timer sync lock dependencies
Description:
init_timer_key() must be done to a timer prior to calling any of the other timer functions.

实际上timer_setup(&timerdev.timer, timer_function, 0)就等于:

init_timer_key(&timerdev.timer, timer_function, 0, NULL, NULL);

注意这里的timer_function必须使用timer_list*类型作为形参,不能用旧版的arg去传递。

从正点原子阿尔法第五十章示例代码修改而来的新代码#

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/libata.h> /* 新版kernel不再支持ide.h */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#define TIMER_CNT 1
#define TIMER_NAME "timer"
#define CLOSE_CMD (_IO(0xEF, 0x1))
#define OPEN_CMD (_IO(0xEF, 0x2))
#define SETPERIOD_CMD (_IO(0xEF, 0x3))
#define LED_ON 1
#define LED_OFF 0
/* Timer device struct */
struct timer_dev{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
struct device_node *nd;
int led_gpio;
int timeperiod; /* 定时器周期(ms) */
struct timer_list timer; /* 定时器 */
spinlock_t lock; /* 自旋锁 */
};
struct timer_dev timerdev; /* 声明定时器实例 */
static int led_init(void)
{
int ret = 0;
/* 从设备树查找led节点 */
timerdev.nd = of_find_node_by_path("/gpioled");
if (timerdev.nd == NULL){
return -EINVAL;
}
/* 从节点查找gpiopin */
timerdev.led_gpio = of_get_named_gpio(timerdev.nd, "led-gpio", 0);
if (timerdev.led_gpio < 0){
printk("Unable get led gpio\r\n");
return -EINVAL;
}
/* 申请GPIO */
gpio_request(timerdev.led_gpio, "led");
/* 上拉输出 */
ret = gpio_direction_output(timerdev.led_gpio, 1);
if (ret < 0){
printk("cannot set gpio\r\n");
return ret;
}
return 0;
}
static int timer_open(struct inode *inode, struct file *filp)
{
int ret = 0;
filp->private_data = &timerdev; /* 设置私有变量以保存结构体 */
timerdev.timeperiod = 1000; /* 默认定时器周期1000ms */
/* 初始化LED */
ret = led_init();
if (ret < 0){
printk("led initialize failed\r\n");
return ret;
}
return 0;
}
static long timer_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct timer_dev *dev = (struct timer_dev *)filp->private_data; /* 从fops获取保存的timerdev */
int timerperiod;
unsigned long flags;
switch (cmd) {
/* 关闭定时器 */
case CLOSE_CMD:
del_timer_sync(&dev->timer);
break;
/* 打开定时器 */
case OPEN_CMD:
spin_lock_irqsave(&dev->lock, flags); /* 上锁 */
timerperiod = dev->timeperiod; /* 保护状态下修改timerperiod为默认定时器周期 */
spin_unlock_irqrestore(&dev->lock, flags); /* 解锁 */
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(timerperiod)); /* 重启定时器 */
break;
/* 修改定时器周期 */
case SETPERIOD_CMD:
spin_lock_irqsave(&dev->lock, flags); /* 上锁 */
dev->timeperiod = arg; /* 保护状态下从用户空间获取定时器周期并赋值 */
spin_unlock_irqrestore(&dev->lock, flags); /* 解锁 */
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(arg)); /* 重启定时器 */
break;
default:
break;
}
return 0;
}
static struct file_operations timer_fops = {
.owner = THIS_MODULE,
.open = timer_open,
.unlocked_ioctl = timer_unlocked_ioctl,
};
/* 定时器回调 */
void timer_function(struct timer_list *t)
{
struct timer_dev *dev = from_timer(dev, t, timer); /* 从定时器实例获取父级结构体 */
static int sta = 1;
int timerperiod;
unsigned long flags;
/* 翻转LED */
sta = !sta;
gpio_set_value(dev->led_gpio, sta);
spin_lock_irqsave(&dev->lock, flags); /* 上锁 */
timerperiod = dev->timeperiod; /* 保护状态下获取当前定时器周期 */
spin_unlock_irqrestore(&dev->lock, flags); /* 解锁 */
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod)); /* 重启定时器 */
}
static int __init timer_init(void)
{
/* 初始化自旋锁 */
spin_lock_init(&timerdev.lock);
/* 注册设备 */
if (timerdev.major){
timerdev.devid = MKDEV(timerdev.major, 0);
register_chrdev_region(timerdev.devid, TIMER_CNT, TIMER_NAME);
}else{
alloc_chrdev_region(&timerdev.devid, 0, 1, TIMER_NAME);
timerdev.major = MAJOR(timerdev.devid);
timerdev.minor = MINOR(timerdev.devid);
}
printk("Device reg ok, major = %d, minor = %d\r\n", timerdev.major, timerdev.minor);
/* 配置CDEV*/
timerdev.cdev.owner = THIS_MODULE;
cdev_init(&timerdev.cdev, &timer_fops);
cdev_add(&timerdev.cdev, timerdev.devid, TIMER_CNT);
/* 配置CLASS */
timerdev.class = class_create(TIMER_NAME);
if (IS_ERR(timerdev.class)){
return PTR_ERR(timerdev.class);
}
/* 配置DEVICE */
timerdev.device = device_create(timerdev.class, NULL, timerdev.devid, NULL, TIMER_NAME);
if (IS_ERR(timerdev.device))
{
return PTR_ERR(timerdev.device);
}
/* 初始化定时器 */
timer_setup(&timerdev.timer, timer_function, 0);
return 0;
}
static void __exit timer_exit(void)
{
gpio_set_value(timerdev.led_gpio, 1);
del_timer_sync(&timerdev.timer);
cdev_del(&timerdev.cdev);
unregister_chrdev_region(timerdev.devid, TIMER_CNT);
device_destroy(timerdev.class, timerdev.devid);
class_destroy(timerdev.class);
}
module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("aki");
Linux内核定时器
https://arkmostima.pages.dev/posts/linux内核定时器/
作者
Aki
发布于
2025-03-26
许可协议
CC BY-NC-SA 4.0