6.1 Xenomai进程的创建流程
6.1.1 Xenomai3:libcobalt库和API/Skin分析
结合官方网址Overview :: Xenomai 3的一章图来分析。里面的颜色和文字都是精工细琢的。

(1)蓝色部分Cobalt core
如图中蓝色部分所示,”Cobalt core”是Xenomai3中实时内核的名字,运行在kernel space。Cobalt是金属元素钴的英文名称,它的元素符号是Co,刚好与co-kernel相同,有点意思吧!大家注意过吗,Xenoami官网的主题色就是蓝色的。
(2)红色部分libcobalt
如图中红色部分所示,Xenomai3在用户层定义了libcobalt库,通过系统调用与内核层的Cobalt core沟通。系统调用的定义参考《5.2 ARM64 Xenomai系统调用》。
特别注意一下旁边括号里面的文字备注(POSIX subset + extensions)。其一,POSIX subset表明仅支持POSIX的子集,以lib/cobalt/thread.c中创建线程接口为例,通过COBALT_IMPL宏来定义pthread_create的语义(“http://www.opengroup.org/onlinepubs/000095399/functions/pthread_create.html”),展开后,实际定义了一个函数为__wrap_pthread_create,同时定义了一个别名__cobalt_pthread_create。其二,extensions表明cobalt有自己的扩展,例如进一步调用pthread_create_ex函数!
总而言之,libcobalt为应用程序提供了posix接口,也被称为posix skin!后面都用posix skin的术语。

(3)橙色部分copperplate interface
libcobalt提供了posix skin,那如果现有的应用程序是基于Xenomai2/native、vxworks、psos等non-POSIX real-time API写的,怎么办?移植起来工作量很大!
Xenomai3实现一个接口转换层即copperplate interface,可以把其它接口(也称为skin)转换为libcobalt提供的posix skin。默认情况下,Copperplate会自动初始化(Auto-initialization),其它接口可以正常工作。
单词copperplate的本意是铜版印刷体,是一种优美圆润的英文书法字体,感觉这个名字非常的无厘头。但是单看copper时,就恍然大悟了。单词copper是金属铜的意思,纯铜是柔软的金属,表面刚切开时为红橙色。三原色中的红色混合黄色,得到的就是橙色,与上图中表达的意思完全一致!另外,铜元素也是过渡元素,这个“过渡”二字是不是非常符合copperplate interface的定位?
(4)黄色部分non-POSIX real-time API
从字面理解,non-POSIX real-time API就很简单了,包括Xenomai2/native、vxworks、psos等接口。在Xenomai3中实现了这些接口,只不过是用了“模拟”的手段,实际是通过copperplate interface转换为posix skin的调用!不同的接口,也称为不同skin,列如native skin、vxworks skin、psos skin等。
以Xenomai2/native接口为例,此接口在Xenomai3中的名字是alchemy(炼金术),定义在lib/alchemy目录。仍然以创建线程为例:rt_task_create调用copperplate_create_thread,再间接调用pthread_create_ex函数。

(5)白色部分Applications
Applications有很多种skin可以选择,包括libcobalt提供的原生posix skin,以及模拟出来的alchemy skin、native skin、vxworks skin、psos skin等等。
在编译和链接阶段,具体要怎么使用不同的skin呢? xeno-config横空出世!
xeno-config是一个辅助脚本,用于为使用Xenomai库的应用程序提供正确的编译和链接标志。通过这个工具,开发者可以轻松获取所需的编译器选项和链接器参数,确保应用程序能够正确地集成到Xenomai环境中。对于不同的API或特性集,如Cobalt、POSIX、Alchemy等,xeno-config可以通过传递特定的参数来调整输出的编译和链接标志。
注意,如果是交叉编译环境,在执行xeno-config之前,需让DESTDIR指向xenomai的安装目录。下面的例子中,xenomai-v3.2.1编译后,通过make install安装到了~/ipipie-arm64/xenomai_install目录。

6.1.2 Xenomai3:使用xeno-config获取编译和链接参数
xeno-config是一个辅助脚本,用于为使用Xenomai库的应用程序提供正确的编译和链接标志。通过这个工具,开发者可以轻松获取所需的编译器选项和链接器参数,确保应用程序能够正确地集成到Xenomai环境中。对于不同的API或特性集,如Cobalt、POSIX、Alchemy等,xeno-config可以通过传递特定的参数来调整输出的编译和链接标志。
注意,如果是交叉编译环境,在执行xeno-config之前,需让DESTDIR指向xenomai的安装目录。下面的例子中,xenomai-v3.2.1编译后,通过make install安装到了~/ipipie-arm64/xenomai_install目录。

场景一:cobalt skin
应用程序直接调用libcobalt库中定义的__cobalt_pthread_create/__wrap_pthread_create类似的API。一般不这么干!
- 获取编译参数
root@u2204:~/ipipe-arm64/xenomai_install# DESTDIR=$(pwd) usr/xenomai/bin/xeno-config --skin=cobalt --cflags
-I/root/ipipe-arm64/xenomai_install/usr/xenomai/include/cobalt -I/root/ipipe-arm64/xenomai_install/usr/xenomai/include -march=armv8-a -D_GNU_SOURCE -D_REENTRANT -fasynchronous-unwind-tables -D__COBALT__
- 获取链接参数
root@u2204:~/ipipe-arm64/xenomai_install# DESTDIR=$(pwd) usr/xenomai/bin/xeno-config --skin=cobalt --ldflags
-Wl,--no-as-needed -Wl,@/root/ipipe-arm64/xenomai_install/usr/xenomai/lib/modechk.wrappers /root/ipipe-arm64/xenomai_install/usr/xenomai/lib/xenomai/bootstrap.o -Wl,--wrap=main -Wl,--dynamic-list=/root/ipipe-arm64/xenomai_install/usr/xenomai/lib/dynlist.ld -L/root/ipipe-arm64/xenomai_install/usr/xenomai/lib -lcobalt -lmodechk -lpthread -lrt -march=armv8-a
场景二:posix skin
应用程序使用posix接口来编程,例如pthread_create。
- 获取编译参数
root@u2204:~/ipipe-arm64/xenomai_install# DESTDIR=$(pwd) usr/xenomai/bin/xeno-config --skin=posix --cflags
-I/root/ipipe-arm64/xenomai_install/usr/xenomai/include/cobalt -I/root/ipipe-arm64/xenomai_install/usr/xenomai/include -march=armv8-a -D_GNU_SOURCE -D_REENTRANT -fasynchronous-unwind-tables -D__COBALT__ -D__COBALT_WRAP__
- 获取链接参数
root@u2204:~/ipipe-arm64/xenomai_install# DESTDIR=$(pwd) usr/xenomai/bin/xeno-config --skin=posix --ldflags
-Wl,--no-as-needed -Wl,@/root/ipipe-arm64/xenomai_install/usr/xenomai/lib/cobalt.wrappers -Wl,@/root/ipipe-arm64/xenomai_install/usr/xenomai/lib/modechk.wrappers /root/ipipe-arm64/xenomai_install/usr/xenomai/lib/xenomai/bootstrap.o -Wl,--wrap=main -Wl,--dynamic-list=/root/ipipe-arm64/xenomai_install/usr/xenomai/lib/dynlist.ld -L/root/ipipe-arm64/xenomai_install/usr/xenomai/lib -lcobalt -lmodechk -lpthread -lrt -march=armv8-a
与cobalt skin的区别是什么?来看最主要的区别,在链接参数中,多了一个标志:-Wl,@/root/xenomai/xenomai-v3.2.1/lib/cobalt/cobalt.wrappers。其中 lib/cobalt/cobalt.wrappers 的文件内容如下:
# cat lib/cobalt/cobalt.wrappers
--wrap pthread_attr_init
--wrap pthread_create
--wrap pthread_setschedparam
--wrap pthread_getschedparam
--wrap pthread_setschedprio
--wrap pthread_yield
--wrap sched_yield
--wrap sched_get_priority_min
--wrap sched_get_priority_max
--wrap sched_setscheduler
--wrap sched_getscheduler
--wrap pthread_kill
--wrap pthread_join
--wrap pthread_setname_np
--wrap sem_init
--wrap sem_destroy
--wrap sem_post
--wrap sem_timedwait
--wrap sem_wait
--wrap sem_trywait
--wrap sem_getvalue
--wrap sem_open
--wrap sem_close
--wrap sem_unlink
--wrap clock_getres
--wrap clock_gettime
--wrap clock_settime
--wrap clock_adjtime
--wrap clock_nanosleep
--wrap nanosleep
--wrap pthread_mutex_init
--wrap pthread_mutex_destroy
--wrap pthread_mutex_lock
--wrap pthread_mutex_trylock
--wrap pthread_mutex_timedlock
--wrap pthread_mutex_unlock
--wrap pthread_mutex_setprioceiling
--wrap pthread_mutex_getprioceiling
--wrap pthread_cond_init
--wrap pthread_cond_destroy
--wrap pthread_cond_wait
--wrap pthread_cond_timedwait
--wrap pthread_cond_signal
--wrap pthread_cond_broadcast
--wrap mq_open
--wrap mq_close
--wrap mq_unlink
--wrap mq_getattr
--wrap mq_setattr
--wrap mq_send
--wrap mq_timedsend
--wrap mq_receive
--wrap mq_timedreceive
--wrap mq_notify
--wrap open
--wrap open64
--wrap __open_2
--wrap __open64_2
--wrap socket
--wrap close
--wrap ioctl
--wrap read
--wrap write
--wrap recvmsg
--wrap recvmmsg
--wrap sendmsg
--wrap sendmmsg
--wrap recvfrom
--wrap sendto
--wrap recv
--wrap send
--wrap getsockopt
--wrap setsockopt
--wrap bind
--wrap connect
--wrap listen
--wrap accept
--wrap getsockname
--wrap getpeername
--wrap shutdown
--wrap timer_create
--wrap timer_delete
--wrap timer_settime
--wrap timer_getoverrun
--wrap timer_gettime
--wrap timerfd_create
--wrap timerfd_gettime
--wrap timerfd_settime
--wrap select
--wrap vfprintf
--wrap vprintf
--wrap fprintf
--wrap printf
--wrap puts
--wrap fputs
--wrap fputc
--wrap putchar
--wrap fwrite
--wrap fclose
--wrap syslog
--wrap vsyslog
--wrap gettimeofday
--wrap __vfprintf_chk
--wrap __vprintf_chk
--wrap __fprintf_chk
--wrap __printf_chk
--wrap __vsyslog_chk
--wrap __syslog_chk
--wrap sigwait
--wrap sigwaitinfo
--wrap sigtimedwait
--wrap sigpending
--wrap sigqueue
--wrap kill
--wrap sleep
--wrap usleep
--wrap mmap
--wrap mmap64
--wrap time
--wrap fcntl
查看cobalt.wrappers的内容,以–wrap pthread_create为例,将应用程序中对 pthread_create函数的所有引用重定向到名为 __wrap_pthread_create 的函数,并将 pthread_create函数本身重命名或重新定义为 __real_pthread_create。最终的调用堆栈为:
Application->pthread_create
->重定向到__wrap_pthread_create //lib/cobalt/thread.c
->pthread_create_ex //lib/cobalt/thread.c
->__real_pthread_create //lib/cobalt/wrappers.c
->pthread_create //回归到标准libpthread库函数
上述例子涉及到复用Linux线程创建的逻辑,非常复杂。并不是所有的库函数都会回归到标准库函数。例如clock_getres函数,直接调用Xenomai系统调用sc_cobalt_clock_getres,完事!
Application->clock_getres
->重定向到__wrap_clock_getres // lib/cobalt/clock.c
->XENOMAI_SYSCALL2(sc_cobalt_clock_getres, clock_id, tp)
场景三:Alchemy skin
- 获取编译参数
root@u2204:~/ipipe-arm64/xenomai_install# DESTDIR=$(pwd) usr/xenomai/bin/xeno-config --skin=alchemy --cflags
-I/root/ipipe-arm64/xenomai_install/usr/xenomai/include/cobalt -I/root/ipipe-arm64/xenomai_install/usr/xenomai/include -march=armv8-a -D_GNU_SOURCE -D_REENTRANT -fasynchronous-unwind-tables -D__COBALT__ -I/root/ipipe-arm64/xenomai_install/usr/xenomai/include/alchemy
- 获取链接参数
root@u2204:~/ipipe-arm64/xenomai_install# DESTDIR=$(pwd) usr/xenomai/bin/xeno-config --skin=alchemy --ldflags
-Wl,--no-as-needed -Wl,@/root/ipipe-arm64/xenomai_install/usr/xenomai/lib/modechk.wrappers -lalchemy -lcopperplate /root/ipipe-arm64/xenomai_install/usr/xenomai/lib/xenomai/bootstrap.o -Wl,--wrap=main -Wl,--dynamic-list=/root/ipipe-arm64/xenomai_install/usr/xenomai/lib/dynlist.ld -L/root/ipipe-arm64/xenomai_install/usr/xenomai/lib -lcobalt -lmodechk -lpthread -lrt -march=armv8-a
在posix skin的基础上,链接时新增了-lalchemy -lcopperplate:链接Alchemy库和Copperplate库。Alchemy库提供了alchemy skin,copperplate库可以把alchemy skin转换为对posix skin的调用!
场景四:兼容Xenomai2/native接口
参考Migrating to Xenomai 3 :: Xenomai 3,如果应用程序是使用Xenomai2/native接口编写的,现在想要运行在Xenomai3,不能简单的使用alchemy skin,需要加上–compat,或者直接使用natvie skin.
- 获取编译参数
root@u2204:~/ipipe-arm64/xenomai_install# DESTDIR=$(pwd) usr/xenomai/bin/xeno-config --skin=native --cflags
-I/root/ipipe-arm64/xenomai_install/usr/xenomai/include/trank -D__XENO_COMPAT__ -I/root/ipipe-arm64/xenomai_install/usr/xenomai/include/cobalt -I/root/ipipe-arm64/xenomai_install/usr/xenomai/include -march=armv8-a -D_GNU_SOURCE -D_REENTRANT -fasynchronous-unwind-tables -D__COBALT__ -I/root/ipipe-arm64/xenomai_install/usr/xenomai/include/alchemy
- 获取链接参数
root@u2204:~/ipipe-arm64/xenomai_install# DESTDIR=$(pwd) usr/xenomai/bin/xeno-config --skin=native --ldflags
-Wl,--no-as-needed -ltrank -Wl,@/root/ipipe-arm64/xenomai_install/usr/xenomai/lib/modechk.wrappers -lalchemy -lcopperplate /root/ipipe-arm64/xenomai_install/usr/xenomai/lib/xenomai/bootstrap.o -Wl,--wrap=main -Wl,--dynamic-list=/root/ipipe-arm64/xenomai_install/usr/xenomai/lib/dynlist.ld -L/root/ipipe-arm64/xenomai_install/usr/xenomai/lib -lcobalt -lmodechk -lpthread -lrt -march=armv8-a
主要变化是增加了-ltrank,引入了trank库。
trank是啥意思呢?
在源码中翻到了解释,是Transition Kit的缩写。它包含了一系列的包装器和服务,帮助开发者更轻松地将使用Xenomai 2.x开发的应用程序迁移到Xenomai 3.x。这个工具包提供了源代码级别的兼容性,使得原本依赖于Xenomai 2.x的POSIX和原生API的应用程序能够在Xenomai 3.x环境中继续运行。
如何编写自己的用户层代码和makefile呢?下一章节将会举实际的例子。
6.1.3 Xenomai3: hello world演示应用编译与启动
从shell启动hello world,那么hello world会变成实时进程吗?
最简单的hello world实例。
root@u2204:~/ipipe-arm64/helloworld # cat hello.c
#include <stdio.h>
int main()
{
printf("Hello World!\n");
return 0;
}
使用posix skin来进行编译与链接,makefile内容如下。
root@u2204:~/ipipe-arm64/helloworld# cat makefile
# 指定编译器为针对aarch64架构的GNU编译器集合中的C++编译器
CC = aarch64-linux-gnu-g++
# 定义Xenomai安装目录的位置
XENO_DESTDIR=/root/ipipe-arm64/xenomai_install
# 定义一个变量来保存Xenomai配置工具的路径,该工具可以生成编译和链接标志
XENO_CONFIG =$(XENO_DESTDIR)/usr/xenomai/bin/xeno-config
# 使用Xenomai配置工具获取POSIX皮肤的编译标志,并存储在变量中
XENO_POSIX_CFLAGS =$(shell DESTDIR=$(XENO_DESTDIR) $(XENO_CONFIG) --skin=posix --cflags)
# 使用Xenomai配置工具获取POSIX皮肤的链接标志,并存储在变量中
XENO_POSIX_LDFLAGS =$(shell DESTDIR=$(XENO_DESTDIR) $(XENO_CONFIG) --skin=posix --ldflags)
# 设置项目路径为当前目录(.)
PROJPATH = .
# 定义最终生成的可执行文件名为hello
EXECUTABLE := hello
# 通过通配符找到所有以.c结尾的源代码文件,并将它们的名称赋值给src变量
src = $(wildcard ./*.c)
# 将找到的源文件名转换为目标文件(.o),并将其列表赋值给obj变量
obj = $(patsubst %.c, %.o, $(src))
# 默认目标:构建所有目标中最先出现的一个,这里是$(EXECUTABLE),即构建'hello'可执行文件
all: $(EXECUTABLE)
# 规则用于创建最终的可执行文件。它依赖于所有的目标文件(.o),使用链接标志进行链接
$(EXECUTABLE): $(obj)
$(CC) -g -o $@ $^ $(XENO_POSIX_LDFLAGS)
# 规则用于从每个源文件(.c)创建对应的目标文件(.o)。这里使用编译标志进行编译
%.o:%.c
$(CC) -g -o $@ -c $< $(XENO_POSIX_CFLAGS)
# 清理规则,删除生成的可执行文件和目标文件,用于清理工作目录
.PHONY: clean
clean:
rm -f $(EXECUTABLE) $(obj)
执行make V=1完成交叉编译与链接,生成可执行文件hello。
root@u2204:~/ipipe-arm64/helloworld# make V=1
aarch64-linux-gnu-g++ -g -o hello.o -c hello.c -I/root/ipipe-arm64/xenomai_install/usr/xenomai/include/cobalt -I/root/ipipe-arm64/xenomai_install/usr/xenomai/include -march=armv8-a -D_GNU_SOURCE -D_REENTRANT -fasynchronous-unwind-tables -D__COBALT__ -D__COBALT_WRAP__
aarch64-linux-gnu-g++ -g -o hello hello.o -Wl,--no-as-needed -Wl,@/root/ipipe-arm64/xenomai_install/usr/xenomai/lib/cobalt.wrappers -Wl,@/root/ipipe-arm64/xenomai_install/usr/xenomai/lib/modechk.wrappers /root/ipipe-arm64/xenomai_install/usr/xenomai/lib/xenomai/bootstrap.o -Wl,--wrap=main -Wl,--dynamic-list=/root/ipipe-arm64/xenomai_install/usr/xenomai/lib/dynlist.ld -L/root/ipipe-arm64/xenomai_install/usr/xenomai/lib -lcobalt -lmodechk -lpthread -lrt -march=armv8-a
把可执行文件hello拷贝到QEMU ARM64中,使用gdb中断hello的执行:
(1)通过(gdb) break hello.c:5 在return语句定义一个断点。
(2)执行(gdb) run,走到return。
(3)通过(gdb) info threads打印出当前线程信息,存在2个线程。“hello”线程是符合预期的,线程号是782。怎么多出了一个线程号785的"cobalt_printf"线程呢?

(4)通过(gdb) shell cat /proc/xenomai/sched/stat,执行shell命令,查看xenomai的线程调度情况。发现“hello”进程已经是一个Xenomai进程了,而不是普通的Linux进程。这是怎么做到的呢?

(5)通过(gdb) shell top -n 1 -p 782,785,执行shell命令,查看Linux下线程的信息。发现“hello”进程和"cobalt_printf"线程的优先级PR都是20(即20+NI)。加上一个参数启动hello:./hello --main-prio=,则可以设定“hello”在Linux中的优先级。

总结一下:通过上面的演示,得到两个问题,接下来探索一下。
“hello”进程已经是一个Xenomai进程了,而不是普通的Linux进程。这是怎么做到的呢?
怎么多出了一个线程号785的"cobalt_printf"线程呢?
6.1.4 Xenomai3: hello world如何变成Xenomai实时进程
“hello”进程已经是一个Xenomai进程了,而不是普通的Linux进程。这是怎么做到的呢?
怎么多出了一个线程号785的"cobalt_printf"线程呢?
首先来梳理一下,在Linux Shell下启动一个普通的应用程序,整体流程是什么样的:
(1)Fork 函数:Shell 使用 fork() 函数,调用sys_fork系统调用来创建一个新的子进程。此时,子进程是父进程(通常是 Shell)的一个副本,包括内存空间、文件描述符等。
(2)Exec 系列函数:在子进程中,Shell 调用 execve() 或其他 exec 系列函数之一来加载并执行指定的 ELF 文件。这一步会替换当前进程的用户空间镜像为新程序的镜像。
(3)_start 入口点
控制权传递给程序的入口点 _start。这是由编译器提供的启动代码的一部分,负责设置初始的栈帧和其他必要的环境变量,并准备参数来调用 _libc_start_main。
(4)__libc_start_main/__libc_csu_init
__libc_start_main 是 glibc 内部函数,负责进一步初始化运行时环境,包括调用__libc_csu_init初始化全局构造函数和静态构造函数,即执行所有带有__attribute__((constructor))属性的函数以及C++中的静态对象构造函数。
__libc_start_main最终调用 main()。
(5)main() 函数执行
控制权交给用户定义的 main() 函数,在这个例子中,它调用了 puts 来打印 “Hello, World!\n”,然后返回 0 表示成功退出。
Xenomai3主要在上述的第(4)步,做了神奇操作!引用官方的Application setup :: Xenomai 3的一张图,增加一些自己的理解(蓝色背景),如下图。

第一部分:__libc_start_main/__libc_csu_init
__libc_start_main调用__libc_csu_init执行所有带有__attribute__((constructor))属性的函数。Xenomai3 利用GCC的 __attribute__((constructor)) 特性来定义了3种优先级的构造函数。凡是有这3个宏作为前缀的函数,总是会在__libc_csu_init中被执行。

- __setup_ctor
基于__setup_ctor,进一步封装了宏定义__setup_call。__setup_call总是调用__register_setup_call向一个setup_list链表注册struct setup_descriptor结构体。
xenomai-v3.2.1/include/boilerplate/setup.h:
#define __setup_ctor __attribute__ ((constructor(200)))
#define __setup_call(__name, __id) \
static __setup_ctor void __declare_ ## __name(void) \
{ \
__register_setup_call(&(__name), __id); \
}
#define core_setup_call(__name) __setup_call(__name, 0)
#define boilerplate_setup_call(__name) __setup_call(__name, 1)
#define copperplate_setup_call(__name) __setup_call(__name, 2)
#define interface_setup_call(__name) __setup_call(__name, 3)
#define post_setup_call(__name) __setup_call(__name, 4)
#define user_setup_call(__name) __setup_call(__name, 5)
其中,最关键就是core_setup_call,它仅在lib/cobalt/init.c中使用了一次,注册struct setup_descriptor cobalt_interface到setup_list链表,且优先级为0,代表最高!

- __early_ctor
xenomai-v3.2.1/include/boilerplate/setup.h:
#define __early_ctor __attribute__ ((constructor(210)))
在__setup_ctor和__bootstrap_ctor之间的,都放在这里。与本次分析无关,忽略。
- __bootstrap_ctor
xenomai-v3.2.1/include/boilerplate/setup.h:
#define __bootstrap_ctor __attribute__ ((constructor(220)))
xenomai-v3.2.1/lib/boilerplate/init/bootstrap.c
__bootstrap_ctor static void xenomai_bootstrap(void)
只有一个函数xenomai_bootstrap有__bootstrap_ctor前缀!原图中红色底框的“bootstrap”其实最终就是调用xenomai_bootstrap。
xenomai_bootstrap读取 /proc/self/cmdline 文件,解析命令行参数,并将这些参数传递给初始化函数 call_init(直接调用xenomai_init),处理xenomai特有的参数,将剩余的应用程序自身的参数保存到early_argc/early_argv中。
当使用xeno-config --posix --ldflags获取链接参数时,是默认打开Automatic bootstrap的,即链接bootstrap.o。可以使用xeno-config --posix --ldflags --no-auto-init来放弃Automatic bootstrap,但是必须手动调用xenomai_init()。一般来说还是老老实实的Automatic bootstrap吧。

xenomai_init在Xenomai3的源码中有两处定义,注意此时一定是定义在lib/boilerplate/setup.c中的xenomai_init。它做main_init_done的检查,确保不会重复初始化。如果没有初始化,则调用__xenomai_init继续。

__xenomai_init函数比较长,其中参数处理部分就先忽略了,来直接看对setup_list的遍历。找到一个已经注册的struct setup_descriptor,则调用setup->init函数。

在所有已经注册的,关注struct setup_descriptor cobalt_interface,它的init函数是cobalt_init,对应于官方流程图里面的黄色框内的setup.

cobalt_init
|->cobalt_init_1()
|->cobalt_init_2()
|->low_init() // 调用sc_coablt_bind系统调用创建cobalt_process
|->cobalt_print_init() //创建Linux普通线程cobalt_printf
|-> __RT(pthread_setschedparam(ptid, policy, &parm))
|->pthread_setschedparam_ex
pthread_setschedparam_ex的核心部分如下图,主要包括两个Xenomai系统调用!

其中,系统调用sc_cobalt_thread_setschedparam_ex在Xenomai内核中的实现如下。
COBALT_SYSCALL(thread_setschedparam_ex, conforming,
(unsigned long pth,
int policy,
const struct sched_param_ex __user *u_param,
__u32 __user *u_winoff,
int __user *u_promoted))
-> cobalt_thread_setschedparam_ex
-> thread_lookup_or_shadow
-> cobalt_thread_shadow
-> pthread_create //创建struct cobalt_thread,插入struct cobalt_process中thread_list链表!
-> cobalt_map_user //将struct cobalt_thread变成Linux struct task_struct的影子
-
创建struct cobalt_thread,插入struct cobalt_process中thread_list链表!
-
将struct cobalt_thread变成Linux struct task_struct的影子
从这里可以看出,Linux的进程用struct task_struct表示,同时struct task_struct也是调度的实体。而Xenomai对应创建了struct cobalt_process,再额外创建了struct cobalt_thread,并且struct cobalt_thread才是运行和调度的实体。
第二部分,__libc_start_main/Xenomai_main
__libc_start_main先调用Xenomai_main,再转到原先的main函数,秘密就在于链接阶段的参数!
-Wl,--wrap=main
-Wl,--dynamic-list=/root/ipipe-arm64/xenomai_install/usr/xenomai/lib/dynlist.ld
-
-Wl,–wrap=main : 将对 main 函数的所有引用重定向到名为 __wrap_main 的函数,并将 main 函数本身重命名或重新定义为 __real_main。__wrap_main函数就定义在bootstrap.o中,它的别名是xenomai_main!
-
-Wl,–dynamic-list:通过将main添加到 --dynamic-list 中,确保了即使使用了 -Wl,–wrap=main 选项来包装 main 函数,原始的 main 函数仍然可以被正确地识别和调用。

至此,已经完全回答了本文开头提出的问题!
Xenomai3 利用GCC的 __attribute__((constructor)) 特性来定义了3种优先级的构造函数,按优先级执行,完成了Xenomai相关服务的初始化,把Linux进程转换程序Xenomai进程。
Xenomai3利用GCC的–wrap特性,完成了从xenomai_main到__real_main的切换!
1011

被折叠的 条评论
为什么被折叠?



