6.1_Xenomai进程的创建流程

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中被执行。

在这里插入图片描述

  1. __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,代表最高!

在这里插入图片描述

  1. __early_ctor
xenomai-v3.2.1/include/boilerplate/setup.h:

#define __early_ctor        __attribute__ ((constructor(210)))

在__setup_ctor和__bootstrap_ctor之间的,都放在这里。与本次分析无关,忽略。

  1. __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的切换!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值