点击查看《Xenomai/IPIPE源代码情景解析》
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!
5.4.6 场景5之Linux线程调用Xenomai系统调用bind
本文耗时1周才梳理清楚,读者估计至少需要2小时能搞清楚流程,一定要耐心一步一步走完流程。
参考《5.2.2 用户层:用latency演示Xenomai系统调用》,latency 是 Xenomai/Cobalt 实时内核提供的一个工具,用于测量系统的实时性能,特别是上下文切换和中断响应的延迟。它启动后,会新建一个display线程用于打印信息;会新建一个sampling线程用于延迟采样。
通过条件断点,在ipipe_handle_syscall第1200行处,Linux进程运行在Root domain,调用Xenomai系统调用bind,完成对应的cobalt process的创建。
- 第一步:ipipe_handle_syscall
第1186行,条件1(nr >= nr_syscalls)成立,因为从用户层传下来的bind系统调用号0x10000000远大于Linux自身的系统调用数量nr_syscalls(294),具体参考《5.2 ARM64 Xenomai系统调用》。条件2(local_flags & _TIP_HEAD)不成立,因为local_flags即ti->ipipe_flags,值为0。参考《5.3.2 ipipe_flags之TIP_HEAD》,_TIP_HEAD代表线程当前运行在head domain,很明显在当前场景下是不可能的。条件1和条件2是逻辑与的关系,所以第1186行的if判断不成立,不会走到快速路径ipipe_fastcall_hook。
第1199行,条件1(local_flags & _TIP_NOTIFY)明显不成立,因为local_flags即ti->ipipe_flags,值为0。参考《5.3.3 ipipe_flags之TIP_NOTIFY》,只要是绑定到xenomai core的进程或线程,一定有TIP_NOTIFY标记!条件2(nr >= nr_syscalls)成立,上面已经分析了。条件1和条件2是逻辑或的关系,所以第1199行的if判断成立,走到慢速路径__ipipe_notify_syscall。
- 第二步,__ipipe_notify_syscall
第1224行,通过__ipipe_current_domain获取当前所在的domain,存入变量caller_domain和this domain。从左上角的local变量的值能够确认,当前所在domain是ipipe_root即root domain。
第1225行,设置变量ipd为ipipe_head_domain即head domain。
第1226行,此处定义了标签next:,在不同的场景下可能会通过goto next跳转过来,形成循环。
第1227行,通过ipd(head domain)获取当前cpu对应的struct ipipe_percpu_domain_data head指针,存入p。
第1228行,参考《5.3.3 ipipe_flags之TIP_NOTIFY》,此处判断必然成立。
第1229行,设置当前cpu的上下文struct ipipe_percpu_data中的curr指向存在p中保存的ipipe_percpu_domain_data head指针。
第1232行,调用ipipe_syscall_hook,在图中也展示了此函数的定义内容。在ipipe_syscall_hook中,如果判断当前是secondary domain(即root domain)则调用handle_root_syscall。而因为在__ipipe_notify_syscall的第1229行已经把当前domain设置为head domain,所以ipipe_syscall_hook最终会调用handle_head_syscall。
- 第三步,handle_head_syscall
handle_head_syscall函数比较长,分成两个部分来解释
(1)handle_head_syscall第483~第513行
第498行,通过__xn_syscall得到bind在xenomai中的系统调用号0。具体原理参考《5.2.3 内核层:Xenomai系统调用的流程》。
第512行,从Xenomai系统调用表中,找出bind系统调用的CoBaLt_bind函数指针。
第513行,从Xenomai系统模式表中,找出bind系统调用的调用模式。参考《5.2.4 内核层:Xenomai系统调用的定义》,在kernel/xenomai/posix/syscall_entries.h找到bind系统调用模式为__COBALT_MODE(bind, lostage) ,代表必须在Linux domain中运行。
kernel/xenomai/posix/syscall.c:
/* Syscall must run into the Linux domain. */
#define __xn_exec_lostage 0x1
(2)handle_head_syscall第551~第557行
第558行,因为bind系统调用模式为__COBALT_MODE(bind, lostage),条件成立。
第562行,caller_is_relaxed是true,所有会跳转到第557行。
第577行,返回KEVENT_PROPAGATE。
这里非常有意思,handle_head_syscall并没有调用CoBaLt_bind系统调用函数,返回KEVENT_PROPAGATE(0)到第二步中的__ipipe_notify_syscall第1232行。
- 第四步,返回KEVENT_PROPAGATE到__ipipe_notify_syscall第1232行
第1232行,ipipe_syscall_hook返回KEVENT_PROPAGATE(0)。
第1235行,因为在第1232行ipipe_syscall_hook执行过程中,当前domain一直是ipd所指向的head domain(第二步中初始化),所以第1235行条件不成立,跳转到第1239行。
第1239行,设置当前domain为this_domain即root domain(第二步中初始化)。
第1242行,根据1239行,判断条件成立,走到第1243行。
第1243行,条件1(ipd != ipipe_root_domain)成立;条件2(ret == 0)成立,因为ipipe_syscall_hook返回KEVENT_PROPAGATE(0)。所以跳转到第1244行。
第1244~1245行,设置ipd为root domain,通过goto next跳转到第1226行。
第1229行,设置当前domain为root domain。
第1232行,进入ipipe_syscall_hook之后,因为当前domain为root domain,所以指向handle_root_syscall函数。
- 第五步,handle_root_syscall函数
(1)第712~713行
第712行,handler指向系统调用函数CoBaLt_bind
第713行,sysflags的值为1,因为bind系统调用模式为__COBALT_MODE(bind, lostage)。
(2)第746~791行
第746行,struct task_struct *p 指向当前进程的task_struct结构体。
第747行,得到传递给系统调用bind的参数,存入args。
第749行,调用系统调用CoBaLt_bind。
第791行,return KEVENT_STOP(1)到第二步中的__ipipe_notify_syscall第1232行。
- 第六步,返回KEVENT_STOP到__ipipe_notify_syscall第1232行
第1232行,ipipe_syscall_hook返回KEVENT_STOP(1)。
第1242行,根据1239行,判断条件成立,走到第1243行。
第1243行,条件1(ipd != ipipe_root_domain)不成立;条件2(ret == 0)不成立,因为ipipe_syscall_hook返回KEVENT_STOP(1)。所以不可能走到1245行执行goto next,至此next/go next循环结束。
第1259行,返回KEVENT_STOP(1)到ipipe_handle_syscall第1200行。
- 第七步,返回KEVENT_STOP(1)到ipipe_handle_syscall第1200行
第1204行,因为ret等于KEVENT_STOP(1),所以跳转到第1205行,返回-1到el0_svc_common第110行。
- 第八步,返回-1到el0_svc_common第110行
第111行,因为ret等于-1,条件成立,最终在第117行返回,完成系统调用!
第131行,此行完全没有机会也没有必要执行了!
总结:这一章虽然是本章的最后一章,其实是最先走通的,然后其它章节才顺理成章的写出来。完全没有想到Xenomai系统调用bind的调用路径这么复杂,断断续续看了一周才算把流程整理出来。
点击查看《Xenomai/IPIPE源代码情景解析》
原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!