目录
1、软中断
2、tasklet
3、工作队列
总结
目前有三种中断的三种机制:
软中断
tasklet
工作队列
1、软中断
软中断是一组静态定义的下半部接口,有 32 个,可以在所有处理器上同时执行,类型相同也可以;在编译时静态注册。
软中断的相关函数:
注册软中断 open_softirq
触发软中断 raise_softirq
执行软中断 do_softirq
Linux Kernel中定义的软中断:
- (linux/include/linux/interrupt.h)
- enum
- {
- HI_SOFTIRQ=0,
- TIMER_SOFTIRQ,
- NET_TX_SOFTIRQ,
- NET_RX_SOFTIRQ,
- BLOCK_SOFTIRQ,
- IRQ_POLL_SOFTIRQ,
- TASKLET_SOFTIRQ,
- SCHED_SOFTIRQ,
- HRTIMER_SOFTIRQ,
- RCU_SOFTIRQ, / Preferable RCU should always be the last softirq /
- NR_SOFTIRQS
- };
软中断执行函数如下:
- (linux/kernel/softirq.c)
- asmlinkage __visible void do_softirq(void)
- {
- __u32 pending;
- unsigned long flags;
- if (in_interrupt())
- return;
- local_irq_save(flags);
- pending = local_softirq_pending();
- if (pending && !ksoftirqd_running(pending))
- do_softirq_own_stack();
- local_irq_restore(flags);
- }
代码一上来就判断是否在中断处理中,如果在立刻退出函数。这说明如果有其他软中断触发,则立即返回。所以,软中断不能被另外一个软中断抢占!唯一可以抢占软中断的是中断处理程序,所以软中断允许响应中断。虽然不能在本处理器上抢占,但是其他的软中断甚至同类型可以再其他处理器上同时执行。由于这点,所以对临界区需要加锁保护。
软中断给对时间要求最严格的下半部使用。目前只有网络,内核定时器和 tasklet 建立在软中断上。
2、tasklet
Tasklet是建立在软中断之上的下半部机制,tasklet和软中断很类似,但是tasklet的接口更简单,也不需要严格的锁机制。因为tasklet是使用软中断来实现的,所以tasklet本身就是软中断。
tasklet使用两种软中断来实现:HI_SOFTIRQ和TASKLET_SOFTIRQ。两者的唯一区别在于优先级,前者优先级更高,总是先于后者执行。
tasklet使用tasklet_struct结构来表示,每个结构体表示一个唯一的tasklet,定义在<linux/interrupt.h>中
- (linux/include/linux/interrupt.h)
- struct tasklet_struct
- {
- struct tasklet_struct *next;
- unsigned long state;
- atomic_t count;
- bool use_callback;
- union {
- void (*func)(unsigned long data);
- void (callback)(struct tasklet_struct t);
- };
- unsigned long data;
- };
Tasklet的使用:
(1)、声明一个新的tasklet
struct tasklet_struct my_tasklet = { NULL, 0, ATOMIC_INIT(0), my_tasklet_handler, dev };
或者
tasklet_init(t, tasklet_handler, dev); / dynamically as opposed to statically /
(2)、tasklet的处理程序
void tasklet_handler(unsigned long data)
注意:和软中断类似,tasklet不能睡眠(阻塞),因为软中断是运行在中断上下文中的,而tasklet是使用软中断来实现的。
(3)、tasklet的调度
使用tasklet_schedule()进行调度(类似软中断的触发),传入参数为指向tasklet_struct的指针。tasklet被调度后,内核会在合适的时机执行该taskelt。(详见前面的tasklet调度的实现)。如果一个tasklet在执行前被调度了多次,还是只会执行一次(tasklet链表中不会有重复的tasklet)。如果一个tasklet在运行中被调度了(比如被另一个处理器上执行的代码调度了),那么这个tasklet会被重新调度并在下次内核处理tasklet的时候再次执行。
3、工作队列
工作队列是和软中断或者tasklet不同的一种下半部机制。工作队列将工作推迟,交给内核线程执行(所以工作队列总是运行在进程上下文中)。工作队列的这种实现可以很好的利用进程上下文的优势,最重要的就是可以睡眠也可以被调度(抢占)。与之相反的是,软中断和tasklet是不能睡眠和被调度的。
可以自己创建工作队列,但是大部分驱动都会使用系统提供的缺省的工作队列类型events,该类型的工作队列的内核线程名字为 events/n,n为处理器编号,每个处理器对应一个内核线程。如果下半部的工作是处理器密集型并且对性能敏感的,可以考虑创建自己的内核线程。比如XFS文件系统就自己创建了两种内核线程。
工作队列的使用:–(缺省工作队列events)
(1)、创建工作(Creaing Work)
- DECLARE_WORK(name, void (func)(void ), void *data); //静态
- INIT_WORK(struct work_struct work, void (func)(void ), void data); //动态
(2)、定义工作队列处理函数
void work_handler(void *data)
(3)、对工作(work)进行调度
内核提供了两个函数对使用缺省工作队列events的工作进行调度
schedule_work(&work);
schedule_delayed_work(&work, delay);
这个函数会创建所有的内核线程(每个处理器一个),并且做些准备好让这些内核线程可以处理工作
(2)、工作队列调度
- int queue_work(struct workqueue_struct wq, struct work_struct work)
- int queue_delayed_work(struct workqueue_struct *wq,
- struct work_struct *work,
- unsigned long delay)
总结
《ARMv8/ARMv9架构学习系列课程》全系列,共计51节课,超15h的视频课程
添加威♥:sami01_2023,回复ARM中文,领取ARM中文手册