【原创】xenomai内核解析--双核系统调用(二)--应用如何区分xenomai/linux系统调用或服务
版权声明:本文为本文为博主原创文章,转载请注明出处。如有错误,欢迎指正。
1. 引出问题
上一篇文章xenomai内核解析--双核系统调用(一)以X86处理器为例,分析了xenomai内核调用的流程,读了以后可能会觉得缺了点什么,你可能会有以下疑问:
- 系统中的两个内核都是POSIX接口实现系统调用,那么我写一个POSIX接口的应用程序,怎样知道它调用的内核,或者说怎样成为运行在cobalt内核的RT应用,而不是普通linux应用?
- 对于同一个POSIX接口,可能我的程序中,既需要xenomai内核提供服务(xenomai 系统调用),又需要调用linux内核提供服务(linux内核系统调用),或者说既有libcobalt,又有glibc库,他们是如何实现或区分的?
2. 编译链接
对于问题1,答案是:由编译时链接的库决定,如果普通的编译,则该应用编译后是一个普通linux运用。如果要编译为xenomai应用,则需要链接到xenomai库,那如何设置编译链接参数?编译安装xenomai库后,可通过执行/usr/bin/xeno-config来获取。
$ /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参数了:
$ /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表示链接,如下得到链接参数:
$ /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示例如下:
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)
3. libcobalt中的实现
下面来看问题2,既然我们已将一个接口链接到实时内核库libcobalt,当然由实时内核库libcobalt来区分该发起linux内核调用还是xenomai内核系统。与上一篇文章一样,以一个POSIX接口pthread_cretate()来解析libcobalt中的实现。
xenomai线程的创建流程比较复杂,需要先让linux创建普通线程,然后再由xenomai创建该线程的shadow 线程,即xenomai调度的实时线程,很符合我们上面的提出的问题2。 说到这先简答介绍一下xenomai实时线程的创建,详细的创建流程后面会写专门写一篇文章解析,敬请期待。
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接口。
4. 总结
- 在实时程序或实时库libcobalt中,通过
__STD()宏来表示使用linux接口。 - 对于一个未指明的接口,libcobalt会先尝试发起xenomai系统调用,不成功会接着尝试linux内核系统调用。
【原创】xenomai内核解析--双核系统调用(二)--应用如何区分xenomai/linux系统调用或服务的更多相关文章
- xenomai内核解析--双核系统调用(三)--如何为xenomai添加一个系统调用
版权声明:本文为本文为博主原创文章,转载请注明出处.如有错误,欢迎指正. @ 目录 一.添加系统调用 二.Cobalt库添加接口 三.应用使用 一.添加系统调用 下面给xenomai添加一个系统调用g ...
- 【原创】xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
版权声明:本文为本文为博主原创文章,转载请注明出处.如有问题,欢迎指正.博客地址:https://www.cnblogs.com/wsg1100/ 1.概述 上篇文章xenomai内核解析--实时IP ...
- 【xenomai内核解析】系列文章大纲
xenomai内核解析 本博客为本人学习linux实时操作系统框架xenomai的一些记录,主要剖析xenomai内核实现,以及与linux相关的知识.方便读者定位具体文章,现列出本博客大纲,后续会陆 ...
- xenomai内核解析之双核系统调用(一)
版权声明:本文为本文为博主原创文章,转载请注明出处.如有错误,欢迎指正.博客地址:https://www.cnblogs.com/wsg1100/ 目录 xenomai 内核系统调用 一.32位Lin ...
- 【原创】xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(二)--实时与非实时关联(bind流程)
版权声明:本文为本文为博主原创文章,转载请注明出处.如有问题,欢迎指正.博客地址:https://www.cnblogs.com/wsg1100/ 1.概述 上篇文章介绍了实时端socket创建和配置 ...
- xenomai内核解析---内核对象注册表—xnregistry(重要组件)
1. 概述 上篇文章xenomai内核解析--同步互斥机制(一)--优先级倒置讲到,对于所有内核对象: xnregistry:保存内核对象,提供内核对象存储和快速检索. xnsynch:资源抽象,提供 ...
- xenomai内核解析之信号signal(二)---xenomai信号处理机制
xenomai信号 上篇文章讲了linux的信号在内核的发送与处理流程,现在加入了cobalt核,Cobalt内核为xenomai线程提供了信号机制.下面一一解析xenomai内核的信号处理机制. 1 ...
- 【原创】xenomai内核解析--同步互斥机制(一)--优先级倒置
版权声明:本文为本文为博主原创文章,转载请注明出处.如有错误,欢迎指正.博客地址:https://www.cnblogs.com/wsg1100/ 目录 一.xenomai 资源管理简要 二.优先级倒 ...
- 【原创】xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(三)--实时与非实时数据交互
版权声明:本文为本文为博主原创文章,转载请注明出处.如有问题,欢迎指正.博客地址:https://www.cnblogs.com/wsg1100/ 目录 1.概述 1.概述 [原创]实时IPC概述 [ ...
随机推荐
- odoo10中的邮件提醒
odoo10中邮件提醒配置如下: 1.配置出向邮件服务器 打开开发者模式,设置-->技术-->email-->出向邮件服务器 设置如下: 如果配置成功,点击’测试连接‘,会出现如下弹 ...
- do...while 循环
语法 C 语言中 do...while 循环的语法: do { statement(s); }while( condition ); 请注意,条件表达式出现在循环的尾部,所以循环中的 statemen ...
- Minimizing maximizer(POJ 1769)
原题如下: Minimizing maximizer Time Limit: 5000MS Memory Limit: 30000K Total Submissions: 5104 Accep ...
- 分布式系统监视zabbix讲解五之web监控
Web 监控 概况 你可以使用 Zabbix 检查几个网站可用性方面. 如果要使用 Web 检测功能,必须在 编译Zabbix 的时候加入 cURL(libcurl) 的支持. 要使用 Web 监控, ...
- Netty中的一些注意事项--底层基础
转载自http://www.mamicode.com/info-detail-1215305.html 最近开发了一个纯异步的redis客户端,算是比较深入的使用了一把netty.在使用过程中一边优化 ...
- 【性能优化】面试官:Java中的对象都是在堆上分配的吗?
写在前面 从开始学习Java的时候,我们就接触了这样一种观点:Java中的对象是在堆上创建的,对象的引用是放在栈里的,那这个观点就真的是正确的吗?如果是正确的,那么,面试官为啥会问:"Jav ...
- Python爬虫练习:爬取800多所大学学校排名、星级等
前言 国内大学最新排名,北大反超,浙大仅第四,中科大跌至第八 时隔五年,"双一流"大学即将迎来首次大考,这也是继改变高校评断标准之后,第一次即将以官方对外发布,自然是引来了许多人的 ...
- 《Java从入门到失业》第四章:类和对象(4.6):类路径
4.6类路径 4.6.1什么是类路径 前面我们讨论过包,知道字节码文件最终都会被放到和包名相匹配的树状结构子目录中.例如上一节的例子: 其实类还有一种存放方式,就是可以归档到一个jar文件中,jar文 ...
- pytest封神之路第零步 快速入门
背景:本文是在系列第五篇发表后的补充篇章,第一篇介绍了tep,可能对不熟悉pytest的朋友不够友好,特意补充入门篇,帮大家快速了解如何动手写pytest.如果你是从这篇文章第一次阅读,那么请忽略以上 ...
- XSS平台简单使用
XSS常用语句及编码绕过 XSS常用的测试语句有: <script>alert(1)</script> <img src=x onerror=alert(1)> & ...