Skip to content

Commit 518d547

Browse files
committed
learning timer
1 parent 1a1891f commit 518d547

File tree

1 file changed

+219
-2
lines changed

1 file changed

+219
-2
lines changed

为了工作/Linux/Linux 设备驱动开发详解.md

Lines changed: 219 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ categories:
44
- Linux学习
55
abbrlink: 484892ff
66
date: 2024-10-24 15:00:00
7-
updated: 2024-11-25 19:20:00
7+
updated: 2024-11-26 19:10:00
88
---
99

1010
<meta name="referrer" content="no-referrer"/>
@@ -1923,7 +1923,7 @@ struct file_operations xxx_fops = {
19231923

19241924
参考文章:[https://blog.csdn.net/weixin_45264425/article/details/130718602](https://blog.csdn.net/weixin_45264425/article/details/130718602)
19251925

1926-
自己实现的 globalmem:[https://github.com/DavidingPlus/linux-kernel-learning/tree/globalmem](https://github.com/DavidingPlus/linux-kernel-learning/tree/globalmem)
1926+
自己实现的版本:[https://github.com/DavidingPlus/linux-kernel-learning/tree/globalmem](https://github.com/DavidingPlus/linux-kernel-learning/tree/globalmem)
19271927

19281928
# Linux 设备驱动中的并发控制
19291929

@@ -3706,3 +3706,220 @@ struct timer_list
37063706
};
37073707
```
37083708

3709+
2. 初始化定时器
3710+
3711+
`__init_timer` 是一个宏函数。用于初始化 timer_list 结构体,并设置其中的函数、flags。
3712+
3713+
```c
3714+
#define __init_timer(_timer, _fn, _flags) \
3715+
do { \
3716+
static struct lock_class_key __key; \
3717+
init_timer_key((_timer), (_fn), (_flags), #_timer, &__key);\
3718+
} while (0)
3719+
3720+
#define __init_timer_on_stack(_timer, _fn, _flags) \
3721+
do { \
3722+
static struct lock_class_key __key; \
3723+
init_timer_on_stack_key((_timer), (_fn), (_flags), \
3724+
#_timer, &__key); \
3725+
} while (0)
3726+
```
3727+
3728+
timer_setup 看定义应该是 `__init_timer` 的别名。
3729+
3730+
```c
3731+
#define timer_setup(timer, callback, flags) \
3732+
__init_timer((timer), (callback), (flags))
3733+
3734+
#define timer_setup_on_stack(timer, callback, flags) \
3735+
__init_timer_on_stack((timer), (callback), (flags))
3736+
```
3737+
3738+
`__TIMER_INITIALIZER(_function, _flags)` 宏用于赋值定时器结构体的 function、flags 成员。
3739+
3740+
DEFINE_TIMER 是定义并初始化名为 _name 的定时器快捷方式,从宏定义而知显而易见。
3741+
3742+
```c
3743+
#define __TIMER_INITIALIZER(_function, _flags) { \
3744+
.entry = { .next = TIMER_ENTRY_STATIC }, \
3745+
.function = (_function), \
3746+
.flags = (_flags), \
3747+
__TIMER_LOCKDEP_MAP_INITIALIZER( \
3748+
__FILE__ ":" __stringify(__LINE__)) \
3749+
}
3750+
3751+
#define DEFINE_TIMER(_name, _function) \
3752+
struct timer_list _name = \
3753+
__TIMER_INITIALIZER(_function, 0)
3754+
```
3755+
3756+
3. 添加定时器
3757+
3758+
此函数用于注册内核定时器,将定时器加入到内核动态定时器链表中。
3759+
3760+
```c
3761+
void add_timer(struct timer_list *timer);
3762+
```
3763+
3764+
4. 删除定时器
3765+
3766+
del_timer_sync() 是 del_timer() 的同步版,在删除一个定时器时需等待其被处理完,故其调用不能发生在中断上下文。
3767+
3768+
```c
3769+
int del_timer(struct timer_list * timer);
3770+
int del_timer_sync(struct timer_list *timer)
3771+
```
3772+
3773+
5. 修改定时器的到期时间 expires
3774+
3775+
此函数用于修改定时器的到期时间,在新的被传入的 expires 到来后才会执行定时器函数。
3776+
3777+
```c
3778+
int mod_timer(struct timer_list *timer, unsigned long expires);
3779+
```
3780+
3781+
定时器的时间基于 jiffies,在修改超时时间时,一般使用这 2 种方法:
3782+
3783+
```c
3784+
mod_timer(&timer, jiffies + xxx); // xxx 表示多少个滴答后超时,也就是 xxx * 10ms
3785+
mod_timer(&timer, jiffies + 2 * HZ); // HZ 等于 CONFIG_HZ,2 * HZ 就相当于 2 秒
3786+
```
3787+
3788+
在 Linux 5 版本以后,**mod_timer() 的含义不仅仅是更改定时器的到期时间,同时会将其添加到内核动态定时器链表中。**与 add_timer() 不同的是 add_timer() 使用的到期时间是其结构体自己内部预先设置的时间,mod_timer() 相当于做了赋值操作后再添加。关于 mod_timer() 新的含义,通过源码也可以猜到:
3789+
3790+
```c
3791+
void add_timer(struct timer_list *timer)
3792+
{
3793+
BUG_ON(timer_pending(timer));
3794+
__mod_timer(timer, timer->expires, MOD_TIMER_NOTPENDING);
3795+
}
3796+
EXPORT_SYMBOL(add_timer);
3797+
3798+
int mod_timer(struct timer_list *timer, unsigned long expires)
3799+
{
3800+
return __mod_timer(timer, expires, 0);
3801+
}
3802+
EXPORT_SYMBOL(mod_timer);
3803+
```
3804+
3805+
另外,**当定时器到期以后,执行完回调函数后,会从内核动态定时器链表中删除。如果需要周期性地执行,需要手动每次在过期以后重新添加。**从下面的模板中也可见一斑。
3806+
3807+
6. 使用模板
3808+
3809+
以下是一个使用内核定时器的模板:
3810+
3811+
```c
3812+
// xxx 设备结构体
3813+
struct xxx_dev
3814+
{
3815+
struct cdev cdev;
3816+
3817+
...
3818+
3819+
timer_list xxx_timer; // 设备要使用的定时器
3820+
};
3821+
3822+
// xxx 驱动中的加载函数
3823+
xxx_init(…)
3824+
{
3825+
struct xxx_dev *dev = filp->private_data;
3826+
...
3827+
3828+
// 初始化定时器
3829+
timer_setup(&dev->xxx_timer, xxx_do_timer, 0);
3830+
// 设备结构体指针作为定时器处理函数参数
3831+
dev->xxx_timer.expires = jiffies + delay;
3832+
// 添加(注册)定时器
3833+
add_timer(&dev->xxx_timer);
3834+
3835+
...
3836+
}
3837+
3838+
// xxx 驱动中的卸载函数
3839+
xxx_exit(…)
3840+
{
3841+
...
3842+
3843+
// 删除定时器
3844+
del_timer(&dev->xxx_timer);
3845+
3846+
...
3847+
}
3848+
3849+
// 定时器处理函数
3850+
static void xxx_do_timer(unsigned long arg)
3851+
{
3852+
struct xxx_device *dev = (struct xxx_device *)(arg);
3853+
3854+
...
3855+
3856+
// 调度定时器再执行
3857+
// 在实际使用中,定时器的到期时间往往是在目前 jiffies 的基础上添加一个时延,若为 Hz,则表示延迟 1s。
3858+
dev->xxx_timer.expires = jiffies + delay;
3859+
// 在定时器处理函数中,在完成相应的工作后,往往会延后 expires 并将定时器再次添加到内核定时器链表中,以便定时器能再次被触发。
3860+
add_timer(&dev->xxx_timer);
3861+
3862+
...
3863+
}
3864+
```
3865+
3866+
Linux 内核在支持 tickless 和 NO_HZ 模式后,内核也包含对 hrtimer(高精度定时器)的支持,它可以支持到微秒级别的精度。内核也定义了 hrtimer 结构体,hrtimer_set_expires()、hrtimer_start_expires()、hrtimer_forward_now()、hrtimer_restart() 等类似的 API 来完成 hrtimer 的设置、时间推移以及到期回调。
3867+
3868+
### delayed_work
3869+
3870+
对于周期性的任务,除定时器以外,还可利用一套封装得很好的快捷机制,本质是利用工作队列和定时器实现,即 delayed_work。
3871+
3872+
```c
3873+
struct delayed_work {
3874+
struct work_struct work;
3875+
struct timer_list timer;
3876+
3877+
/* target workqueue and CPU ->timer uses to queue ->work */
3878+
struct workqueue_struct *wq;
3879+
int cpu;
3880+
};
3881+
3882+
struct work_struct {
3883+
atomic_long_t data;
3884+
struct list_head entry;
3885+
work_func_t func;
3886+
#ifdef CONFIG_LOCKDEP
3887+
struct lockdep_map lockdep_map;
3888+
#endif
3889+
};
3890+
```
3891+
3892+
可以通过如下函数调度一个 delayed_work 在指定的延时以后运行:
3893+
3894+
```c
3895+
bool schedule_delayed_work(struct delayed_work *dwork, unsigned long delay);
3896+
```
3897+
3898+
当延时 delay 到来以后,delayed_work 结构体的 work_struct 类型成员 dwork 的 work_func_t 类型成员 func() 函数会被执行,work_func_t 定义为:
3899+
3900+
```c
3901+
void (*work_func_t)(struct work_struct *work);
3902+
```
3903+
3904+
另外,delay 参数的单位是 jiffies,一种常见的用法如下:
3905+
3906+
```c
3907+
// msecs_to_jiffies() 用于将毫秒转化为 jiffies。
3908+
schedule_delayed_work(&work, msecs_to_jiffies(poll_interval));
3909+
```
3910+
3911+
同定时器,如果需要周期性的执行任务,一般需要手动在工作函数再次调用 schedule_delayed_work(),周而复始。
3912+
3913+
以下函数用于取消 delay_work:
3914+
3915+
```c
3916+
bool cancel_delayed_work(struct delayed_work *dwork);
3917+
bool cancel_delayed_work_sync(struct delayed_work *dwork);
3918+
```
3919+
3920+
### 秒字符设备
3921+
3922+
编写一个字符设备 second(即“秒”)的驱动。它被打开的时候初始化一个定时器,并添加到内核定时器链表中,每秒在内核日志中输出一次当前的 jiffies,读取该字符设备的时候返回当前定时器的秒数。
3923+
3924+
自己实现的版本:[https://github.com/DavidingPlus/linux-kernel-learning/tree/second](https://github.com/DavidingPlus/linux-kernel-learning/tree/second)
3925+

0 commit comments

Comments
 (0)