@@ -4,7 +4,7 @@ categories:
44 - Linux学习
55abbrlink: 484892ff
66date: 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