一、如何區(qū)分xenomai、linux系統(tǒng)調(diào)用/服務(wù)
1. 引出問題
上一篇文章xenomai內(nèi)核解析--雙核系統(tǒng)調(diào)用(一)以X86處理器為例,分析了xenomai內(nèi)核系統(tǒng)調(diào)用的流程,讀了以后可能會覺得缺了點什么,你可能會有以下疑問:
-
系統(tǒng)中的兩個內(nèi)核都是POSIX接口實現(xiàn)系統(tǒng)調(diào)用,那么我們用POSIX接口寫了一個應(yīng)用程序,怎樣知道它調(diào)用的內(nèi)核,或者如何區(qū)分這個應(yīng)用是cobalt內(nèi)核的應(yīng)用,而不是普通linux應(yīng)用?
-
對于同一個POSIX接口應(yīng)用程序,可能既需要xenomai內(nèi)核提供服務(wù)(xenomai 系統(tǒng)調(diào)用),又需要調(diào)用linux內(nèi)核提供服務(wù)(linux內(nèi)核系統(tǒng)調(diào)用),或者既有l(wèi)ibcobalt,又有g(shù)libc庫,他們是如何實現(xiàn)和區(qū)分的?

2. 編譯鏈接
對于問題1,答案是:由編譯的鏈接過程決定,鏈接的庫不同當(dāng)然執(zhí)行的也就不同,如果普通編譯,則該應(yīng)用編譯后是一個普通linux運用。如果要編譯為xenomai應(yīng)用,則需要鏈接到xenomai庫。但是我們應(yīng)用程序調(diào)用的代碼函數(shù)symbol完全一樣,我們會有疑惑xenomai是如何貍貓換太子的?首先鏈接是通過符號表(symbol)來鏈接的,當(dāng)然也就要從代碼符號(symbol)入手,首先來看一個常用的編譯xenomai 應(yīng)用的makefile:
XENO_CONFIG := /usr/xenomai/bin/xeno-configPROJPATH = .CFLAGS := $(shell $(XENO_CONFIG) --posix --alchemy --cflags)LDFLAGS := $(shell $(XENO_CONFIG) --posix --alchemy --ldflags)INCFLAGS= -I$(PROJPATH)/include/EXECUTABLE := rt-appsrc = $(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)clean:rm -f $(EXECUTABLE) $(obj)
其中最重要的就是編譯時需要 xeno-config來生成gcc參數(shù)。xeno-config在我們編譯安裝xenomai庫后,默認(rèn)放在 /usr/bin/xeno-config。
$ /usr/bin/xeno-config --helpxeno-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 OPTIONSOptions :--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接口的實時應(yīng)用,參數(shù) --cflags表示編譯,指定接口(skin) --posix,就能得到編譯該程序的gcc參數(shù)了,看著沒什么特別的:
$ /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表示鏈接,如下得到鏈接參數(shù):
$ /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
這一看就多出不少東西,重點就在這 cobalt.wrappers、 modechk.wrappers,兩個文件的內(nèi)容如下:
...--wrap open--wrap open64--wrap socket--wrap close--wrap ioctl--wrap read....--wrap recv--wrap send--wrap getsockopt--wrap setsockop...
里面是一些posix系統(tǒng)調(diào)用,前面的 --wrap是什么作用?這是執(zhí)行鏈接過程的程序 ld的一個參數(shù),通過 man ld可以找到該參數(shù)的說明:
--wrap symbolUse a wrapper function for symbol. Any undefined reference to symbol will be resolved to "__wrap_symbol". Any undefined reference to "__real_symbol" will be resolved to symbol.This can be used to provide a wrapper for a system function. The wrapper function should be called "__wrap_symbol". If it wishes to call the system function, it should call "__real_symbol".Here is a trivial example:void *__wrap_malloc (size_t c){printf ("malloc called with %zu ", c);return __real_malloc (c);}If you link other code with this file using --wrap malloc, then all calls to "malloc" will call the function "__wrap_malloc" instead. The call to "__real_malloc" in "__wrap_malloc" will call the real "malloc"function.You may wish to provide a "__real_malloc" function as well, so that links without the --wrap option will succeed. If you do this, you should not put the definition of "__real_malloc" in the same file as"__wrap_malloc"; if you do, the assembler may resolve the call before the linker has a chance to wrap it to "malloc".
簡單來說就是:任何 對 symbol未定義 的 引用 (undefined reference) 將 解析為 __wrap_symbol. 任何 對 __real_symbol未定義 的 引用 將 解析為 symbol。意思就是我們代碼里使用到且是參數(shù) --wrap指定的符號 symbol(即文件里的內(nèi)容),鏈接的時候就認(rèn)為它是 __wrap_symbol,比如我們代碼用到了 open()在鏈接的時候是與庫中的 __wrap_open()鏈接的, __wrap_open就是在xenomai 實時庫libcobalt中實現(xiàn),現(xiàn)在我們明白我們的posix函數(shù)如何和xenomai對接上了。
對于
xeno-config的其他更多參數(shù)可通過xenomai Manual Page了解。
這樣就將POSIX接口源碼編譯成一個xenomai可執(zhí)行程序了。
我們還有另一個問題,既然鏈接到了libcobalt,但我們是要Linux來提供服務(wù),又是怎么讓Linux來提供服務(wù)的呢?看libcobalt具體實現(xiàn)可以知道答案。
3. libcobalt中的實現(xiàn)
下面來看問題2,既然我們已將一個接口鏈接到實時內(nèi)核庫libcobalt,當(dāng)然由實時內(nèi)核庫libcobalt來區(qū)分該發(fā)起linux內(nèi)核調(diào)用還是xenomai內(nèi)核系統(tǒng)。與上一篇文章一樣,以一個POSIX接口 pthread_cretate()來解析libcobalt中的實現(xiàn)。
xenomai線程的創(chuàng)建流程比較復(fù)雜,需要先讓linux創(chuàng)建普通線程,然后再由xenomai創(chuàng)建該線程的shadow 線程,即xenomai調(diào)度的實時線程,很符合我們上面的提出的問題2。說到這先簡答介紹一下xenomai實時線程的創(chuàng)建,詳細(xì)的創(chuàng)建流程后面會寫專門寫一篇文章解析,敬請期待。
pthread_cretate()不是一個系統(tǒng)調(diào)用,由NPTL(Native POSIX Threads Library)實現(xiàn)(NPTL是Linux 線程實現(xiàn)的現(xiàn)代版,由UlrichDrepper 和Ingo Molnar 開發(fā),以取代LinuxThreads),NPTL負(fù)責(zé)一個用戶線程的用戶空間棧創(chuàng)建、內(nèi)存分配、初始化等工作,與linux內(nèi)核配合完成線程的創(chuàng)建。每一線程映射一個單獨的內(nèi)核調(diào)度實體(KSE,Kernel Scheduling Entity)。內(nèi)核分別對每個線程做調(diào)度處理。線程同步操作通過內(nèi)核系統(tǒng)調(diào)用實現(xiàn)。
xenomai coblat作為實時任務(wù)的調(diào)度器,每個實時線程需要對應(yīng)到 coblat調(diào)度實體,如果要創(chuàng)建實時線程就需要像linux那樣NPTL與linux 內(nèi)核深度結(jié)合,那么coblat與libcoblat實現(xiàn)將會變得很復(fù)雜。在這里,xenomai使用了一種方式,由NPTL方式去完成實時線程實體的創(chuàng)建(linux部分),在普通線程的基礎(chǔ)上附加一些屬性,對應(yīng)到xenomai cobalt內(nèi)核實體時能被實時內(nèi)核cobalt調(diào)度。
所以libcoblat庫中的實時線程創(chuàng)建函數(shù) pthread_cretate最后還是需要使用 glibc的 pthread_cretate函數(shù),xenomai只是去擴(kuò)展glibc pthread_cretate創(chuàng)建的線程,使這個線程可以在實時內(nèi)核cobalt調(diào)度。
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()生成三個類型函數(shù):
__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()函數(shù)的這三個宏意思為:
__RT(P):__cobalt_pthread_create明確表示Cobalt實現(xiàn)的POSIX函數(shù)
__COBALT(P):與__RT()等效。
__STD(P):__real_pthread_create表示這是原始的POSIX函數(shù)(Linux glibc實現(xiàn)),cobalt庫內(nèi)部通過它來表示調(diào)用原始的POSIX函數(shù)(glibc NPTL).
__WRAP(P):__wrap_pthread_create是__cobalt_pthread_create的弱別名,如果編譯器編譯時知道有該函數(shù)其它的實現(xiàn),該函數(shù)就會被覆蓋。
主要關(guān)注前面兩個,對于最后一個宏,如果外部庫想覆蓋已有的函數(shù),應(yīng)提供其自己的 __wrap_pthread_create()實現(xiàn),來覆蓋Cobalt實現(xiàn)的 pthread_create()版本。原始的Cobalt實現(xiàn)仍可以引用為 __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庫函數(shù) pthread_create實現(xiàn)主體為
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函數(shù)及該函數(shù)的一個弱別名 __wrap_pthread_create,調(diào)用這兩個函數(shù)執(zhí)行的是同一個函數(shù)體。
對于 NPTL函數(shù) pthread_create,在Cobalt庫里使用 __STD()修飾,展開后即 __real_pthread_create(),其實只是NPTL pthread_create()的封裝, __real_pthread_create()會直接調(diào)用 NPTL pthread_create,在libcobaltwrappers.c實現(xiàn)如下:
/* pthread */__weakint __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);}
它調(diào)用的就是glibc中的 pthread_create函數(shù).同樣我們接著 __cobalt_pthread_create()看哪里調(diào)用的.
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 調(diào)用標(biāo)準(zhǔn)庫的函數(shù)*/if (ret) {__STD(sem_destroy(&iargs.sync));return ret;}__STD(clock_gettime(CLOCK_REALTIME, &timeout));.....}
下面再看另一個例子,實時任務(wù)在代碼中使用了linux的網(wǎng)絡(luò)套接字(xenomai任務(wù)也是一個linux任務(wù),也可以使用linux來提供服務(wù),只不過會影響實時性),有以下代碼,:
....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()函數(shù)即libcobalt中的 __cobalt_socket(),其定義在xenomai- 3.x.xlibcobalt tdm.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中的函數(shù)會先嘗試調(diào)用實時內(nèi)核cobalt的系統(tǒng)調(diào)用, 當(dāng)cobalt系統(tǒng)調(diào)用不成功的時候才繼續(xù)嘗試通過 __STD()宏來調(diào)用linux系統(tǒng)調(diào)用(cobalt內(nèi)核根據(jù)socket協(xié)議類型參數(shù) PF_INET, SOCK_STREAM判斷),這樣就有效的分清了是linux系統(tǒng)調(diào)用還是xenomai系統(tǒng)調(diào)用,這也是所有l(wèi)ibcobalt實現(xiàn)的posix都鏈接到libobalt庫的原因。
一般情況下,可以直接在代碼中使用 __STD()宏指明我們調(diào)用的linux內(nèi)核的服務(wù),修改如下:
....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)));....
現(xiàn)在一切都明了了,一個函數(shù)編譯時通過參數(shù)鏈接到xenomai庫后,通過 __STD()宏來表示使用linux接口。
4. 總結(jié)
-
在實時程序或?qū)崟r庫libcobalt中,通過
__STD()宏來表示使用linux接口。 -
對于一個未指明的接口,libcobalt會先嘗試發(fā)起xenomai系統(tǒng)調(diào)用,不成功會接著嘗試linux內(nèi)核系統(tǒng)調(diào)用或者調(diào)用glibc函數(shù)。
-
如果我們向libcobalt庫中新添加一個libcobalt庫中沒有的自定義POSIX函數(shù)/系統(tǒng)調(diào)用時,一定要在內(nèi)部先嘗試發(fā)起xenomai系統(tǒng)調(diào)用,不成功時接著嘗試linux內(nèi)核系統(tǒng)調(diào)用,此外還必須將該接口添加到文件
xenomailibcobaltcobalt.wrappers中,這樣才能正確鏈接,否則編譯后的應(yīng)用還是原來的。
二、 如何為xenomai添加一個系統(tǒng)調(diào)用
1. 添加系統(tǒng)調(diào)用
有的時候我們需要給系統(tǒng)添加一個特殊功能的系統(tǒng)調(diào)用,比如我們在對xenomai做benchmark測試的時候,需要測試每個系統(tǒng)服務(wù)操作系統(tǒng)需要消耗多長時間,比如測量獲取信號量操作系統(tǒng)的耗時。我們知道中斷優(yōu)先級最高,會強占前臺的操作系統(tǒng)和應(yīng)用,為了更準(zhǔn)確的測量獲取信號量時操作系統(tǒng)的耗時,這里需要將這種情況下的結(jié)果丟棄,我們就需要一個獲取xenomai中斷次數(shù)的系統(tǒng)調(diào)用。
假設(shè)該系統(tǒng)沒有任何實時驅(qū)動運行,且設(shè)置了xenomai.supportedcpus和linux irqaffinity,supportedcpus啟用tickless,下面給xenomai添加一個系統(tǒng)調(diào)用 get_timer_hits(),用于獲取應(yīng)用程序運行CPU的定時器中斷產(chǎn)生的次數(shù)(類似于VxWorks里的tickGet(),VxWorks是采用周期tick的方式來驅(qū)動系統(tǒng)運作,tickGet()獲取的也就是tick定時器中斷的次數(shù))。以該系統(tǒng)調(diào)用來舉例如何為xenomai添加一個實時系統(tǒng)調(diào)用。
在前兩篇文中說到,xenomai每個系統(tǒng)的系統(tǒng)系統(tǒng)調(diào)用號在 cobaltuapisyscall.h中:
......
在此添加 sc_cobalt_get_timer_hits的系統(tǒng),為了避免與xenomai系統(tǒng)調(diào)用沖突(xenomai官方添加的系統(tǒng)調(diào)用號從小到大),那我們就從最后一個系統(tǒng)調(diào)用添加,即127號系統(tǒng)調(diào)用,如下。
....../*Powerof2*/
先確定一下我們這個函數(shù)的API形式,由于是一個非標(biāo)準(zhǔn)的形式,這里表示如下:
int get_timer_hits(unsigned long *u_tick);
參數(shù)為保存hits的變量地址;
返回值:成功0;出錯 <0;
系統(tǒng)調(diào)用的頭文件,然后添加一個系統(tǒng)調(diào)用的聲明,覺得它和clock相關(guān),那就放在 kernelxenomaiposixclock.h中吧。
COBALT_SYSCALL_DECL(get_timer_hits,(unsignedlong__user*u_tick));
然后是該函數(shù)的內(nèi)核實現(xiàn),放在 /kernelxenomaiposixclock.c,如下:
COBALT_SYSCALL(get_timer_hits,primary,(unsignedlong__user*u_tick)){structxnthread*thread;unsignedlongtick;intcpu;intret=0;unsignedintirq;thread=xnthread_current();if(thread==NULL)return-EPERM;/*得到當(dāng)前任務(wù)CPU號*/cpu=xnsched_cpu(thread->sched);irq=per_cpu(ipipe_percpu.hrtimer_irq,cpu);/*讀取該CPU中斷計數(shù)*/tick=__ipipe_cpudata_irq_hits(&xnsched_realtime_domain,cpu,irq);if(cobalt_copy_to_user(u_tick,&tick,sizeof(tick)))return-EFAULT;returnret;}
需要注意的是該系統(tǒng)調(diào)用的權(quán)限,這里使用 primary,表示只有cobalt上下文(實時線程)才能調(diào)用。
修改完成后重新編譯內(nèi)核并安裝。
2.Cobalt庫添加接口
在前兩篇文中說到,xenomai系統(tǒng)調(diào)用由libcobalt發(fā)起,所以修改應(yīng)用庫來添加該函數(shù)接口,添加聲明 includecobalt ime.h:
COBALT_DECL(int,get_timer_hits(unsignedlongtick));
xenomai3.x.xlibcobaltclock.c添加該接口定義:
COBALT_IMPL(int,get_timer_hits,(unsignedlong*tick)){intret;ret=-XENOMAI_SYSCALL1(sc_cobalt_get_tick,tick);returnret;}
因為該系統(tǒng)調(diào)用和posix接口沒有符號重名,不需要修改wrappers文件,完成上述步驟后重新編譯并安裝xenomai庫。
3. 應(yīng)用使用
由于我們添加 get_timer_hits()系統(tǒng)調(diào)用時,指定了系統(tǒng)調(diào)用的權(quán)限為primary,這里創(chuàng)建一個實時任務(wù),使用宏 __RT()指定鏈接到libcobalt庫。
void test(void *cookie){unsigned long tick;int ret;ret = __RT(get_timer_hits(&tick));if (ret){fprintf(stderr,"%s: failed to get_tick,%s ",__func__,strerror(-ret));return ret;}fprintf(stdout,"timer_hits:%ld ",tick);/*....*/return 0;}int main(int argc, char *const *argv){struct sigaction sa __attribute__((unused));int sig, cpu = 0;char sem_name[16];sigset_t mask;RT_TASK task;int ret;sigemptyset(&mask);sigaddset(&mask, SIGINT);sigaddset(&mask, SIGTERM);sigaddset(&mask, SIGHUP);sigaddset(&mask, SIGALRM);pthread_sigmask(SIG_BLOCK, &mask, NULL);setlinebuf(stdout);ret = rt_task_spawn(&task, "test_task", 0, PRIO,T_JOINABLE, test, NULL);if (ret){fprintf(stderr,"%s: failed to create task,%s ",__func__,strerror(-ret));return ret;}__STD(sigwait(&mask, &sig));rt_task_join(&task);rt_task_delete(&task);return 0;}
編譯Makefile:
XENO_CONFIG := /usr/xenomai/bin/xeno-configPROJPATH = .CFLAGS := $(shell $(XENO_CONFIG) --posix --alchemy --cflags)LDFLAGS := $(shell $(XENO_CONFIG) --posix --alchemy --ldflags)INCFLAGS= -I$(PROJPATH)/include/EXECUTABLE := get-timer-hitssrc = $(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)clean:rm -f $(EXECUTABLE) $(obj)
運行結(jié)果:
$./get-timer-hitstimer_hits:3
可以看到,雖然系統(tǒng)已經(jīng)啟動十幾分鐘了,但一直沒有運行xenomai應(yīng)用,xenomai tick相關(guān)中斷才產(chǎn)生了3次,這就是tickless,后面會出xenomai調(diào)度及時間子系統(tǒng)和xenomai benchmark相關(guān)文章,敬請關(guān)注。
審核編輯 :李倩-
Linux
+關(guān)注
關(guān)注
88文章
11641瀏覽量
218191 -
Xenomai
+關(guān)注
關(guān)注
0文章
13瀏覽量
8213
原文標(biāo)題:xenomai內(nèi)核解析--雙核系統(tǒng)調(diào)用(二)--應(yīng)用如何區(qū)分xenomai/linux系統(tǒng)調(diào)用或服務(wù)
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
【技術(shù)分享】RK3588如何搭建xenomai3+ethercat
技術(shù)分享 | RK3588增加Xenomai3實時補丁
服務(wù)器數(shù)據(jù)恢復(fù)—意外斷電!Linux服務(wù)器數(shù)據(jù)恢復(fù)大揭秘
深入了解系統(tǒng)調(diào)用API:探索操作系統(tǒng)底層的關(guān)鍵接口
瑞芯微RK35XX系列Linux實時性詳細(xì)測試對比( PREEMPT_RT和Xenomai )
RK3506開發(fā)板Xenomai內(nèi)核RT-Linux實時性系統(tǒng)適配教程與性能實測,實測僅7μs穩(wěn)定延時
SSH 遠(yuǎn)程連接內(nèi)網(wǎng) Linux 服務(wù)器
華納云服務(wù)器Linux系統(tǒng)電源管理與節(jié)能優(yōu)化配置方法
一文讀懂:嵌入式Linux實時性進(jìn)階
詳解Linux系統(tǒng)中的服務(wù)管理

如何區(qū)分xenomai、linux系統(tǒng)調(diào)用/服務(wù)
評論