点击查看《Xenomai/IPIPE源代码情景解析》
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!
6.4.3 Xenomai xnthread_relax原理
实时线程有时需要离开head域,转而在root域中执行,主要场景有两个:
(1)执行非实时工作:运行涉及由Linux内核处理的常规系统调用的非时间关键(带内)工作。
(2)处理CPU异常:从CPU异常中恢复,例如处理主要内存访问故障,对于这类故障,无需关注响应时间,也无需在实时核心中重复处理。
xnthread_relax,顾名思义,是一个放松过程(可以联想到脱离硬实时)。它把任务从Xenomai迁移到Linux中共分为4个步骤,核心调用堆栈如下,用4种颜色标识。
1. pipeline_post_inband_work(&wakework)
pipeline_post_inband_work是一个宏定义,它直接调用ipipe_post_work_root。
关于ipipe_post_work_root,回到第三章,参考《3.4.4 __ipipe_init_early之再论虚拟中断》,它调用__ipipe_set_irq_pending(&ipipe_root, irq)把__ipipe_work_virq虚拟中断记录到root domain的interrupt log。直到interrupt log被回放,在root domain中调用虚拟中断响应程序__ipipe_do_work来完成工作项。
通过虚拟中断__ipipe_work_virq,给root domain传递任意的工作项struct ipipe_work_header,定义如下。
include/linux/ipipe.h:
struct ipipe_work_header {
size_t size;
void (*handler)(struct ipipe_work_header *work);
};
pipeline_post_inband_work(&wakework)传入的参数为wakework,其定义如下。最终在root域中的回调函数是lostage_task_wakeup。
struct lostage_wakeup wakework = {
.inband_work = PIPELINE_INBAND_WORK_INITIALIZER(wakework,
lostage_task_wakeup),
.task = p,
};
struct lostage_wakeup {
struct pipeline_inband_work inband_work; /* Must be first. */
struct task_struct *task;
};
struct pipeline_inband_work {
struct ipipe_work_header work;
};
pipeline_post_inband_work(&wakework)虽然向root domain发送了一个任务,但是当前Xenomai线程正在xnthread_relax函数上下文中运行呢,此时root domain是不可能执行此任务的。
2. xnthread_suspend(thread, suspension, XN_INFINITE, XN_RELATIVE, NULL)
结合上下文,可知传入的第二参数suspension,必然在上一个函数pipeline_leave_oob_prepare()中置位了XNRELAX标记。
第943~945行,xnthread_clear_state(thread, XNREADY)清除xnthread的XNREADY标识。
第960行,xnsched_set_resched(sched)设置调度器的重新调度标志,表示需要进行重新调度。
第970,__xnsched_run(sched) 执行调度器的重新调度操作,从当前任务“小沙”的xnthread切换到新的xnthread线程“小飞”。
至此,当前任务”小沙”的xnthread会一直处于XNRELAX阻塞状态,不可能被Xenomai调度器调度了,就像被按下了暂停键。而任务“小沙”是一体两面的,它在Linux中的task_struct还处于TASK_INTERRUPTIBLE可中断睡眠状态中,怎么唤醒它呢?
还记得第1步中向root域发送的任务wakework吗?当Xenomai所在的head域空闲的时候,会执行root域的中断回放,就会回调任务wakework中定义的lostage_task_wakeup函数。
3. lostage_task_wakeup
当lostage_task_wakeup函数被回调时,它处于root域中断回放的上下文中。它的核心就是调用wake_up_process§来唤醒指定的进程,其参数p就指向任务“小沙”的task_struct。
wake_up_process 是 Linux 内核中用于唤醒休眠进程的关键函数,其核心逻辑如下:
(1)状态转换
将目标进程从休眠状态(如 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUPTIBLE)切换到可运行状态(TASK_RUNNING),使其有资格被调度器选中执行。
(2)加入调度队列
将进程添加到 CPU 的运行队列(runqueue)中。
(3)抢占CPU并运行
如果被唤醒的进程和当前进程属于相同的调度类,那么调度调度类的check_preempt_curr方法以检查是否可以抢占当前进程。
如果被唤醒的进程所属调度类的优先级高于当前进程所属调度类的优先级,那么给当前进程设置需要重新调度的标志。
4. pipeline_leave_oob_finish
xnthread_relax得以从第2步暂停的地方,继续运行,从xnthread_suspend函数返回。注意此时是task_struct在root domain中运行。
接下来走到pipeline_leave_oob_finish,它调用__ipipe_reenter_root()来完成最后的收尾工作。
(1)ipipe_clear_thread_flag(TIP_HEAD);
参考《5.3.2 ipipe_flags之TIP_HEAD》,IPIPE在struct thread_info中新增了ipipe_flags,用于Xenomai/Linux双内核之间进行交互。其中TIP_HEAD代表进程当前运行在Head域。
(2)finish_task_switch§
该函数的主要功能是在任务切换完成后,进行一些必要的清理和更新操作,确保系统状态的一致性和正确性。
点击查看《Xenomai/IPIPE源代码情景解析》
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!