Linux中断调用流程:
//ARM64 Linux硬件中断注册流程
__git_init_bases()
set_handle_irq(gic_handle_irq)// 使handle_arch_irq 指向 gic_handle_irq()
//ARM64 Linux中断流程
kernel_entery // 保存中断上下文
irq_handler //中断处理
irq_stack_entry // 设置栈地址
handle_arch_irq = gic_handle_irq()
handle_domain_irq() //Linux内核中断处理入口
irq_enter() //显式地告知内核现在进入中断上下文
preempt_count_add(HARDIRQ_OFFSET) // 增加当前进程的thread_infod中preempt_count成员里HARDIRQ域的值
irq_find_mapping() //通过硬件中断号找到IRQ号
generic_handle_irq()
handle_fasteoi_irq() // 如果没有指定action描述符或者该中断关闭了IRQD_IRQ_DISABLED,则设置中断状态为IRQ_PENDING,
// 然后调用中断控制器的irq_chip中的irq_mask()回调函数屏蔽该终端
// 如果该中断不支持中断嵌套,则调用mask_irq()函数关闭该中断源
handle_irq_event() // 中断处理的核心函数
handle_irq_event_percpu() // 对应cpu的中断处理入口
__handle_irq_event_percpu() // 同上
action->handler() // 执行中断处理,若需要唤醒中断的内核线程,则返回IRQ_WAKE_THREAD
__irq_wake_thread() //若需要,则唤醒中断的内核线程
irq_exit() //显式地告知内核现在退出中断上下文
preempt_count_sub(HARDIRQ_OFFSET) //配对地递减preempt_count中HARDIRQ域地计数
invoke_softirq()
__do_softirq() // 处理软中断
irq_stack_exit // 从中断栈切换回中断进程地内核栈
kernel_exit // 恢复中断上下文并退出中断
// __irq_wake_thread()唤醒中断线程后的执行过程
irq_thread() //中断线程的执行函数,设置handler_fn()回调
irq_thread_fn() // 执行注册中断时的thread_fn()函数
atcion->thread_fn() //最终的中断处理入口
irq_finalize_oneshot() //对IRQS_ONESHOT类型中断的处理
Linux软中断:
由中断处理中可以发现,Linux的软中断处理实际上也是在中断调用流程中实现的。
open_softirq(int nr, void(*action)(struct softirq_action *)) //注册一个软中断,其中nr是软中断的序号
//触发软中断接口
#ifndef local_softirq_pending_ref
#define local_softirq_pending_ref irq_stat.__softirq_pending
#endif
#define local_softirq_pending() (__this_cpu_read(local_softirq_pending_ref))
#define set_softirq_pending(x) (__this_cpu_write(local_softirq_pending_ref, (x)))
#define or_softirq_pending(x) (__this_cpu_or(local_softirq_pending_ref, (x)))
// 设置irq_stat数据结构中__soft_pending成员的第nr位,nr表示中断序号。
// 在中断返回时,该CPU会检查此信息,若不为0,则说明有pending的软中断需处理
void __raise_softirq_irqoff(unsigned int nr)
{
trace_softirq_raise(nr);
or_softirq_pending(1UL << nr);
}
inline void raise_softirq_irqoff(unsigned int nr) // 主动关闭本地中断,允许在进程上下文中调用
{
__raise_softirq_irqoff(nr);
if (!in_interrupt())
wakeup_softirqd();
}
void raise_softirq(unsigned int nr)
{
unsigned long flags;
local_irq_save(flags);
raise_softirq_irqoff(nr);
local_irq_restore(flags);
}
Tasklet
Tasklet本质上是软中断的一个实现形式,运行在软中断的上下文中
//驱动中tasklet使用
// 定义
// 静态声明
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data } // 初始化并默认使能
#define DECLARE_TASKLET_DISABLED(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data } // 初始化并默认禁用
//动态声明
void tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data)
{
t->next = NULL;
t->state = 0;
atomic_set(&t->count, 0);
t->func = func;
t->data = data;
}
// 使用Tasklet
//在驱动中调用tasklet_schedule()函数
// Tasklet执行,遵循软中断上下文逻辑,因此不是在调用__tasklet_schedule_common时就立即执行,而是需要等待调度时执行
static void __tasklet_schedule_common(struct tasklet_struct *t,
struct tasklet_head __percpu *headp,
unsigned int softirq_nr)
{
struct tasklet_head *head;
unsigned long flags;
local_irq_save(flags);
head = this_cpu_ptr(headp);
t->next = NULL;
*head->tail = t;
head->tail = &(t->next);
raise_softirq_irqoff(softirq_nr);
local_irq_restore(flags);
}
void __tasklet_schedule(struct tasklet_struct *t)
{
__tasklet_schedule_common(t, &tasklet_vec,
TASKLET_SOFTIRQ);
}
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
中断管理看完后,下一节的内容便是着重介绍工作队列(workqueue)部分。workqueue的引入是为了解决软中断和tasklet的天然硬伤,由于其处理优先级比系统调度要高,因此在软中断较多的场景下,系统调度的及时性会大大降低。而工作队列将任务交由特定线程完成,也就是说,工作队列的任务处理是在系统调度中实现,也就规避了系统调度被长时间阻塞的问题。
工作队列核心代码的实现如下:
//工作队列初始化, 系统初始化时自动运行, 驱动开发者不需要操作
// 图像化的流程见附图2.10
start_kernel()
workqueue_init_early() // 用于初始化默认的几个工作队列
arch_call_rest_init()
rest_init()
kernel_thread(kernel_init, NULL, CLONE_FS);
kernel_init()
kernel_init_freeable()
workqueue_init()
create_worker() // 创建工作队列
worker_attach_to_pool() // 把刚分配的工作线程挂入worker_poolworker_pool中,并且设置这个工作线程允许运行的cpumask
// 创建工作队列
// 其中 __alloc_workqueue_key流程见附图2.11
#ifdef CONFIG_LOCKDEP
#define alloc_workqueue(fmt, flags, max_active, args...) \
({ \
static struct lock_class_key __key; \
const char *__lock_name; \
\
__lock_name = "(wq_completion)"#fmt#args; \
\
__alloc_workqueue_key((fmt), (flags), (max_active), \
&__key, __lock_name, ##args); \
})
#else
#define alloc_workqueue(fmt, flags, max_active, args...) \
__alloc_workqueue_key((fmt), (flags), (max_active), \
NULL, NULL, ##args)
#endif
#define alloc_ordered_workqueue(fmt, flags, args...) \
alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | \
__WQ_ORDERED_EXPLICIT | (flags), 1, ##args)
#define create_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, 1, (name))
#define create_freezable_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_FREEZABLE | WQ_UNBOUND | \
WQ_MEM_RECLAIM, 1, (name))
#define create_singlethread_workqueue(name) \
alloc_ordered_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, name)
//添加工作队列
#define ATOMIC_LONG_INIT(i) ATOMIC_INIT(i)
#define WORK_DATA_INIT() ATOMIC_LONG_INIT((unsigned long)WORK_STRUCT_NO_POOL)
#ifdef CONFIG_LOCKDEP
#define __INIT_WORK(_work, _func, _onstack) \
do { \
static struct lock_class_key __key; \
\
__init_work((_work), _onstack); \
(_work)->data = (atomic_long_t) WORK_DATA_INIT(); \
lockdep_init_map(&(_work)->lockdep_map, "(work_completion)"#_work, &__key, 0); \
INIT_LIST_HEAD(&(_work)->entry); \
(_work)->func = (_func); \
} while (0)
#else
#define __INIT_WORK(_work, _func, _onstack) \
do { \
__init_work((_work), _onstack); \
(_work)->data = (atomic_long_t) WORK_DATA_INIT(); \
INIT_LIST_HEAD(&(_work)->entry); \
(_work)->func = (_func); \
} while (0)
#endif
#define INIT_WORK(_work, _func) \
__INIT_WORK((_work), (_func), 0)
#define INIT_WORK_ONSTACK(_work, _func) \
__INIT_WORK((_work), (_func), 1)
//调度工作队列
//__queue_work()为调度工作队列的核心实现,其大致实现流程见图2.12
//需注意,调度工作队列,实际上并未执行工作队列,工作队列的具体执行见工作队列初始化中的create_worker()函数中调用的worker_thread()函数
//简化后的worker_thread()函数实现如下:
worker_thread()
{
recheck:
if(不需要更多的工作线程?)
goto sleep;
if(需要创建更多的工作线程? && 创建工作线程)
goto recheck;
do {
处理工作;
}(还有工作未完成 && 活跃的工作线程 <= 1)
sleep:
schedule();
}
bool queue_work_on(int cpu, struct workqueue_struct *wq,
struct work_struct *work)
{
bool ret = false;
unsigned long flags;
local_irq_save(flags);
if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) {
__queue_work(cpu, wq, work);
ret = true;
}
local_irq_restore(flags);
return ret;
}
static inline bool queue_work(struct workqueue_struct *wq,
struct work_struct *work)
{
return queue_work_on(WORK_CPU_UNBOUND, wq, work);
}
static inline bool schedule_work(struct work_struct *work)
{
return queue_work(system_wq, work);
}
//取消一个workqueue
cancel_work_sync() // 取消一个work,但会等到work执行完毕
__cancel_work_timer()
try_to_grab_pending() // 尝试从线程池中拿到所需要取消的work,若未拿到,则继续执行__cancel_work_timer()
__flush_work() // 等待work执行完毕
总结
1. 或许是之前并未深入研究linux内核源码的缘故,阅读第二章的知识内容,仅仅从怎么使用的角度去理解,都有一种较为吃力的感觉,此感觉在workqueue部分特别明显,明显感觉需要反复对照源码才能理解逻辑。因此此章节的知识点还需要反复阅读学习,并配以项目实践来纠正和加深理解。
2. 在反复看前两章内容的过程中,明显感觉缺乏卷1内容的情况下直接看卷2,有不少知识点盲区。因此看此书时最好备有卷1,以便出现涉及到卷1内容时快速查阅。
3. CMWQ机制目前还没理解透, 还需继续深入阅读理解。