版权声明:本文为本文为博主原创文章,转载请注明出处。若有错误,欢迎指正。html
上一篇文章xenomai内核解析--双核系统调用(一)以X86处理器为例,分析了xenomai内核调用的流程,读了之后可能会以为缺了点什么,你可能会有如下疑问:linux
对于问题1,答案是:由编译时连接的库决定,若是普通的编译,则该应用编译后是一个普通linux运用。若是要编译为xenomai应用,则须要连接到xenomai库,那如何设置编译连接参数?编译安装xenomai库后,可经过执行/usr/bin/xeno-config
来获取。shell
$ /usr/bin/xeno-config --help
xeno-config --verbose
--core=cobalt
--version="3.1"
--cc="gcc"
--ccld="/usr/bin/wrap-link.sh gcc"
--arch="x86"
--prefix="/usr"
--library-dir="/usr/lib"
Usage xeno-config OPTIONS
Options :
--help
--v,--verbose
--version
--cc
--ccld
--arch
--prefix
--[skin=]posix|vxworks|psos|alchemy|rtdm|smokey|cobalt
--auto-init|auto-init-solib|no-auto-init
--mode-check|no-mode-check
--cflags
--ldflags
--lib*-dir|libdir|user-libdir
--core
--info
--compat
复制代码
例如编译一个POSIX接口的实时应用,参数--cflags
表示编译,指定接口(skin)--posix
,就能获得编译该程序的gcc参数了:bootstrap
$ /usr/bin/xeno-config --posix --cflags
-I/usr/include/xenomai/cobalt -I/usr/include/xenomai -D_GNU_SOURCE -D_REENTRANT -fasynchronous-unwind-tables -D__COBALT__ -D__COBALT_WRAP__
复制代码
再看连接,--ldflags
表示连接,以下获得连接参数:bash
$ /usr/bin/xeno-config --ldflags --posix
-Wl,--no-as-needed -Wl,@/usr/lib/cobalt.wrappers -Wl,@/usr/lib/modechk.wrappers /usr/lib/xenomai/bootstrap.o -Wl,--wrap=main -Wl,--dynamic-list=/usr/lib/dynlist.ld -L/usr/lib -lcobalt -lmodechk -lpthread -lrt
复制代码
其余更多参数可经过xenomai Manual Page了解。网络
这样就将POSIX接口源码编译成一个xenomai可执行程序了。一般咱们会将获取编译参数的操做直接放到Makefile里,编译时直接执行获取使用,这里给一个简单的Makefile示例以下:app
XENO_CONFIG := /usr/xenomai/bin/xeno-config
PROJPATH = .
CFLAGS := $(shell $(XENO_CONFIG) --posix --alchemy --cflags)
LDFLAGS := $(shell $(XENO_CONFIG) --posix --alchemy --ldflags)
INCFLAGS= -I$(PROJPATH)/include/
EXECUTABLE := rt-task
src = $(wildcard ./*.c)
obj = $(patsubst %.c, %.o, $(src))
all: $(EXECUTABLE)
$(EXECUTABLE): $(obj)
$(CC) -g -o $@ $^ $(INCFLAGS) $(CFLAGS) $(LDFLAGS)
%.o:%.c
$(CC) -g -o $@ -c $< $(INCFLAGS) $(CFLAGS) $(LDFLAGS)
.PHONY: clean
clean:
rm -f $(EXECUTABLE) $(obj)
复制代码
下面来看问题2,既然咱们已将一个接口连接到实时内核库libcobalt,固然由实时内核库libcobalt来区分该发起linux内核调用仍是xenomai内核系统。与上一篇文章同样,以一个POSIX接口pthread_cretate()
来解析libcobalt中的实现。socket
xenomai线程的建立流程比较复杂,须要先让linux建立普通线程,而后再由xenomai建立该线程的shadow 线程,即xenomai调度的实时线程,很符合咱们上面的提出的问题2。 说到这先简答介绍一下xenomai实时线程的建立,详细的建立流程后面会写专门写一篇文章解析,敬请期待。async
pthread_cretate()
不是一个系统调用,由NPTL(Native POSIX Threads Library)实现(NPTL是Linux 线程实现的现代版,由UlrichDrepper 和Ingo Molnar 开发,以取代LinuxThreads),NPTL负责一个用户线程的用户空间栈建立、内存分配、初始化等工做,与linux内核配合完成线程的建立。每一线程映射一个单独的内核调度实体(KSE,Kernel Scheduling Entity)。内核分别对每一个线程作调度处理。线程同步操做经过内核系统调用实现。函数
xenomai coblat做为实时任务的调度器,每一个实时线程须要对应到 coblat调度实体,若是要建立实时线程就须要像linux那样NPTL与linux 内核深度结合,那么coblat与libcoblat实现将会变得很复杂。在这里,xenomai使用了一种方式,由NPTL方式去完成实时线程实体的建立(linux部分),在普通线程的基础上附加一些属性,对应到xenomai cobalt内核实体时能被实时内核cobalt调度。
因此libcoblat库中的实时线程建立函数pthread_cretate
最后仍是须要使用 glibc的pthread_cretate
函数,xenomai只是去扩展glibc pthread_cretate
建立的线程,使这个线程能够在实时内核cobalt调度。
pthread_cretate()
在libcobalt中pthread.h文件中定义以下:
COBALT_DECL(int, pthread_create(pthread_t *ptid_r,
const pthread_attr_t *attr,
void *(*start) (void *),
void *arg));
复制代码
COBALT_DECL
宏在wrappers.h
中以下,展开上面宏,会为pthread_create()
生成三个类型函数:
#define __WRAP(call) __wrap_ ## call
#define __STD(call) __real_ ## call
#define __COBALT(call) __cobalt_ ## call
#define __RT(call) __COBALT(call)
#define COBALT_DECL(T, P) \ __typeof__(T) __RT(P); \ __typeof__(T) __STD(P); \ __typeof__(T) __WRAP(P)
int __cobalt_pthread_create(pthread_t *ptid_r,
const pthread_attr_t *attr,
void *(*start) (void *),
void *arg);
int __wrap_pthread_create(pthread_t *ptid_r,
const pthread_attr_t *attr,
void *(*start) (void *),
void *arg);
int __real_pthread_create(pthread_t *ptid_r,
const pthread_attr_t *attr,
void *(*start) (void *),
void *arg);
复制代码
声明pthread_create()
函数的这三个宏意思为:
__RT(P):__cobalt_pthread_create
表示Cobalt实现的POSIX函数
__STD(P):__real_pthread_create
表示原始的POSIX函数(Linux glibc实现),cobalt库内部经过它来表示调用原始的POSIX函数(glibc NPTL).
__WRAP(P):__wrap_pthread_create
是__cobalt_pthread_create
的弱别名,若是编译器编译时知道有该函数其它的实现,该函数就会被覆盖。
主要关注前面两个,对于最后一个宏,若是外部库想覆盖已有的函数,应提供其本身的__wrap_pthread_create()
实现,来覆盖Cobalt实现的pthread_create()
版本。 原始的Cobalt实现仍能够引用为__COBALT(pthread_create)
。由宏COBALT_IMPL来定义:
#define COBALT_IMPL(T, I, A) \ __typeof__(T) __wrap_ ## I A __attribute__((alias("__cobalt_" __stringify(I)), weak)); \ __typeof__(T) __cobalt_ ## I A
复制代码
最后cobalt库函数pthread_create
实现主体为(xenomai3.x.x\lib\cobalt\thread.c):
COBALT_IMPL(int, pthread_create, (pthread_t *ptid_r,
const pthread_attr_t *attr,
void *(*start) (void *), void *arg))
{
pthread_attr_ex_t attr_ex;
......
return pthread_create_ex(ptid_r, &attr_ex, start, arg);
}
复制代码
COBALT_IMPL定义了__cobalt_pthread_create
函数及该函数的一个弱别名__wrap_pthread_create
,调用这两个函数执行的是同一个函数体。
对于 NPTL函数pthread_create
,在Cobalt库里使用__STD()
修饰,展开后即__real_pthread_create()
,其实只是NPTL pthread_create()
的封装,__real_pthread_create()
会直接调用 NPTL pthread_create
,在lib\cobalt\wrappers.c实现以下:
/* pthread */
__weak
int __real_pthread_create(pthread_t *ptid_r,
const pthread_attr_t * attr,
void *(*start) (void *), void *arg)
{
return pthread_create(ptid_r, attr, start, arg);
}
复制代码
它调用的就是glibc中的pthread_create
函数.一样咱们接着__cobalt_pthread_create()
看哪里调用的.
int pthread_create_ex(pthread_t *ptid_r,
const pthread_attr_ex_t *attr_ex,
void *(*start) (void *), void *arg)
{
......
__STD(sem_init(&iargs.sync, 0, 0));
ret = __STD(pthread_create(&lptid, &attr, cobalt_thread_trampoline, &iargs));/*__STD 调用标准库的函数*/
if (ret) {
__STD(sem_destroy(&iargs.sync));
return ret;
}
__STD(clock_gettime(CLOCK_REALTIME, &timeout));
.....
}
复制代码
下面再看另外一个例子,实时任务在代码中使用了linux的网络套接字(xenomai任务也是一个linux任务,也可使用linux来提供服务,只不过会影响实时性),有如下代码,:
....
int sockfd,ret;
struct sockaddr_in addr;
sockfd = socket(PF_INET, SOCK_STREAM, 0);
.....
bind(sockfd, (struct sockaddr *)&my_addr,sizeof(struct sockaddr_in));
....
复制代码
该代码编译时连接到了libcobalt,socket()函数即libcobalt中的__cobalt_socket()
,其定义在xenomai-3.x.x\lib\cobalt\rtdm.c
,以下:
COBALT_IMPL(int, socket, (int protocol_family, int socket_type, int protocol))
{
int s;
s = XENOMAI_SYSCALL3(sc_cobalt_socket, protocol_family,
socket_type, protocol);
if (s < 0) {
s = __STD(socket(protocol_family, socket_type, protocol));
}
return s;
}
复制代码
能够看到,libcobalt中的函数会先尝试调用实时内核cobalt的系统调用, 当cobalt系统调用不成功的时候才继续尝试经过__STD()
宏来调用linux系统调用(cobalt内核根据socket协议类型参数PF_INET
,SOCK_STREAM
判断),这样就有效的分清了是linux系统调用仍是xenomai系统调用。
通常状况下,能够直接在代码中使用__STD()
宏指明咱们调用的linux内核的服务,修改以下:
....
int sockfd,ret;
struct sockaddr_in addr;
sockfd = __STD(socket(PF_INET, SOCK_STREAM, 0));
.....
__STD(bind(sockfd, (struct sockaddr *)&my_addr,sizeof(struct sockaddr_in)));
....
复制代码
如今一切都明了了,一个函数编译时经过参数连接到xenomai库后,经过__STD()
宏来表示使用linux接口。
__STD()
宏来表示使用linux接口。