1. 程式人生 > >xenomai核心解析之雙核系統呼叫(一)

xenomai核心解析之雙核系統呼叫(一)

版權宣告:本文為本文為博主原創文章,轉載請註明出處。如有錯誤,歡迎指正。部落格地址:https://www.cnblogs.com/wsg1100/ [TOC] # xenomai 核心系統呼叫 解析系統呼叫是瞭解核心架構最有力的一把鑰匙,在這之前先搞懂xenomai與linux兩個核心共存後系統呼叫是如何實現的。 ![](https://wsg-blogs-pic.oss-cn-beijing.aliyuncs.com/xenomai/xenomai-arc.png) **為什麼需要系統呼叫** linux核心中設定了一組用於實現系統功能的子程式,稱為系統呼叫。系統呼叫和普通庫函式呼叫非常相似,只是系統呼叫由作業系統核心提供,運行於**核心態**,而普通的函式呼叫由函式庫或使用者自己提供,運行於**使用者態**。 一般的,程序是不能訪問核心的。它不能訪問核心所佔記憶體空間也不能呼叫核心函式。CPU硬體決定了這些(這就是為什麼它被稱作**“保護模式”**。 為了和使用者空間上執行的程序進行互動,核心提供了一組介面。透過該介面,應用程式可以訪問硬體裝置和其他作業系統資源。這組介面在應用程式和核心之間扮演了使者的角色,應用程式傳送各種請求,而核心負責滿足這些請求(或者讓應用程式暫時擱置)。實際上提供這組介面主要是為了保證系統穩定可靠,避免應用程式肆意妄行,惹出大麻煩。 系統呼叫在使用者空間程序和硬體裝置之間添加了一箇中間層。該層主要作用有三個: - 它為使用者空間提供了一種統一的硬體的抽象介面。比如當需要讀些檔案的時候,應用程式就可以不去管磁碟型別和介質,甚至不用去管檔案所在的檔案系統到底是哪種型別。 - 系統呼叫保證了系統的穩定和安全。作為硬體裝置和應用程式之間的中間人,核心可以基於許可權和其他一些規則對需要進行的訪問進行裁決。舉例來說,這樣可以避免應用程式不正確地使用硬體裝置,竊取其他程序的資源,或做出其他什麼危害系統的事情。 - 每個程序都執行在虛擬系統中,而在使用者空間和系統的其餘部分提供這樣一層公共介面,也是出於這種考慮。如果應用程式可以隨意訪問硬體而核心又對此一無所知的話,幾乎就沒法實現多工和虛擬記憶體,當然也不可能實現良好的穩定性和安全性。在Linux中,系統呼叫是使用者空間訪問核心的惟一手段;除異常和中斷外,它們是核心惟一的合法入口。 Linux加上實時系統核心xenomai後,實時任務常呼叫xenomai系統呼叫來完成實時的服務,如果實時任務需要用到linux的服務,還會呼叫linux的系統呼叫。 ### 一、32位Linux系統呼叫 linux應用程式除直接系統呼叫外還會由glibc觸發系統呼叫,glibc為了提高應用程式的效能,對一些系統呼叫進行了封裝。 32位系統系統呼叫使用軟中斷`int 0x80`指令實現,軟中斷屬於異常的一種,通過它陷入(trap)核心,trap在整理的文件`x86 Linux中斷系統`有說明。`tarp_init()`中設定IDT(Interrupt Descriptor Table 每個中斷處理程式的地址都儲存在一個特殊的位置)由關`int 0x80`的IDT如下: ```c static const __initconst struct idt_data def_idts[] = { ...... SYSG(IA32_SYSCALL_VECTOR, entry_INT80_32), ...... }; ``` 當生系統呼叫時,硬體根據向量號在 IDT 中找到對應的表項,即中斷描述符,進行特權級檢查,發現 DPL = CPL = 3 ,允許呼叫。然後硬體將切換到核心棧 (tss.ss0 : tss.esp0)。接著根據中斷描述符的 segment selector 在 GDT / LDT 中找到對應的段描述符,從段描述符拿到段的基址,載入到 cs 。將 offset 載入到 eip。最後硬體將 ss / sp / eflags / cs / ip / error code 依次壓到核心棧。於是開始執行`entry_INT80_32`函式,該函式在`entry_32.S`定義: ```asm ENTRY(entry_INT80_32) ASM_CLAC pushl %eax /* pt_regs->orig_ax */ SAVE_ALL pt_regs_ax=$-ENOSYS /* *儲存當前使用者態暫存器,儲存在pt_regs結構裡*/ /* * User mode is traced as though IRQs are on, and the interrupt gate * turned them off. */ TRACE_IRQS_OFF movl %esp, %eax call do_int80_syscall_32 .Lsyscall_32_done: ....... .Lirq_return: INTERRUPT_RETURN/*iret 指令將原來使用者態儲存的現場恢復回來,包含程式碼段、指令指標暫存器等。這時候使用者態 程序恢復執行。*/ ``` 在核心棧的最高地址端,存放的是結構 pt_regs,首先通過 push 和 SAVE_ALL 將當前使用者態的暫存器,儲存在棧中 pt_regs 結構裡面.儲存完畢後,關閉中斷,將當前棧指標儲存到 eax,即do_int80_syscall_32的引數1。 呼叫do_int80_syscall_32=>do_syscall_32_irqs_on。先看看沒有**ipipe**時Linux實現如下: ```c __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs) { struct thread_info *ti = pt_regs_to_thread_info(regs); unsigned int nr = (unsigned int)regs->orig_ax; ..... if (likely(nr < IA32_NR_syscalls)) { nr = array_index_nospec(nr, IA32_NR_syscalls); regs->ax = ia32_sys_call_table[nr]( /*根據系統呼叫號索引直接執行*/ (unsigned int)regs->bx, (unsigned int)regs->cx, (unsigned int)regs->dx, (unsigned int)regs->si, (unsigned int)regs->di, (unsigned int)regs->bp); } syscall_return_slowpath(regs); } ``` 在這裡,將系統呼叫號從pt_reges中eax 裡面取出來,然後根據系統呼叫號,在系統呼叫表中找到相應的函式進行呼叫,並將暫存器中儲存的引數取出來,作為函式引數。如果仔細比對,就能發現,這些引數所對應的暫存器,和 Linux 的註釋是一樣的。`ia32_sys_call_table`系統呼叫表生成後面解析(此圖來源於網路)。 ![](https://wsg-blogs-pic.oss-cn-beijing.aliyuncs.com/xenomai/syscall_32.PNG) 相關核心呼叫執行完後,一直返回到 do_syscall_32_irqs_on ,如果系統呼叫有返回值,會被儲存到 regs->ax 中。接著返回 entry_INT80_32 繼續執行,最後執行 INTERRUPT_RETURN 。 INTERRUPT_RETURN 在 `arch/x86/include/asm/irqflags.h` 中定義為 iret ,iret 指令將原來使用者態儲存的現場恢復回來,包含程式碼段、指令指標暫存器等。這時候使用者態程序恢復執行。 系統呼叫執行完畢。 ### 二、32位實時系統呼叫 Xenomai使用I-pipe 攔截常規Linux系統呼叫排程程式,並將系統呼叫定向到實現它們的系統。 實時系統呼叫,除了直接系統呼叫外,xenomai還實現了libcoblat實時庫,相當於glibc,通過libcoblat進行xenomai系統呼叫,以libcoblat庫函式sem_open為例,libcolat庫中C函式實現如下: ```c COBALT_IMPL(sem_t *, sem_open, (const char *name, int oflags, ...)) { ...... err = XENOMAI_SYSCALL5(sc_cobalt_sem_open, &rsem, name, oflags, mode, value); if (err == 0) { if (rsem != sem) free(sem); return &rsem->native_sem; } ....... return SEM_FAILED; } ``` libcolat庫呼叫系統呼叫使用巨集`XENOMAI_SYSCALL5`,**XENOAI_SYSCALL**巨集在`\include\asm\xenomai\syscall.h`中宣告,`XENOMAI_SYSCALL5`中的'5'代表'該系統呼叫有五個引數: ```c #define XENOMAI_DO_SYSCALL(nr, op, args...) \ ({ \ unsigned __resultvar; \ asm volatile ( \ LOADARGS_##nr \ "movl %1, %%eax\n\t" \ DOSYSCALL \ RESTOREARGS_##nr \ : "=a" (__resultvar) \ : "i" (__xn_syscode(op)) ASMFMT_##nr(args) \ : "memory", "cc"); \ (int) __resultvar; \ }) #define XENOMAI_SYSCALL0(op) XENOMAI_DO_SYSCALL(0,op) #define XENOMAI_SYSCALL1(op,a1) XENOMAI_DO_SYSCALL(1,op,a1) #define XENOMAI_SYSCALL2(op,a1,a2) XENOMAI_DO_SYSCALL(2,op,a1,a2) #define XENOMAI_SYSCALL3(op,a1,a2,a3) XENOMAI_DO_SYSCALL(3,op,a1,a2,a3) #define XENOMAI_SYSCALL4(op,a1,a2,a3,a4) XENOMAI_DO_SYSCALL(4,op,a1,a2,a3,a4) #define XENOMAI_SYSCALL5(op,a1,a2,a3,a4,a5) XENOMAI_DO_SYSCALL(5,op,a1,a2,a3,a4,a5) ``` 每個巨集中,內嵌另一個巨集DOSYSCALL,即實現系統呼叫的int指令:`int $0x80`。 ```c #define DOSYSCALL "int $0x80\n\t" ``` 系統呼叫過程硬體處理及中斷入口上節一致,從`do_syscall_32_irqs_on`開始不同,有ipipe後變成下面這樣子: ```c static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs) { struct thread_info *ti = current_thread_info(); unsigned int nr = (unsigned int)regs->orig_ax;/*取出系統呼叫號*/ int ret; ret = pipeline_syscall(ti, nr, regs);/*pipeline 攔截系統呼叫*/ ...... done: syscall_return_slowpath(regs); } ``` 套路和ipipe接管中斷類似,在關鍵路徑上攔截系統呼叫,然後呼叫`ipipe_handle_syscall(ti, nr, regs)`讓ipipe來接管處理: ```c int ipipe_handle_syscall(struct thread_info *ti, unsigned long nr, struct pt_regs *regs) { unsigned long local_flags = READ_ONCE(ti->ipipe_flags); int ret; if (nr >= NR_syscalls && (local_flags & _TIP_HEAD)) {/*執行在head域且者系統呼叫號超過linux*/ ipipe_fastcall_hook(regs); /*快速系統呼叫路徑*/ local_flags = READ_ONCE(ti->ipipe_flags); if (local_flags & _TIP_HEAD) { if (local_flags & _TIP_MAYDAY) __ipipe_call_mayday(regs); return 1; /* don't pass down, no tail work. */ } else { sync_root_irqs(); return -1; /* don't pass down, do tail work. */ } } if ((local_flags & _TIP_NOTIFY) || nr >= NR_syscalls) { ret =__ipipe_notify_syscall(regs); local_flags = READ_ONCE(ti->ipipe_flags); if (local_flags & _TIP_HEAD) return 1; /* don't pass down, no tail work. */ if (ret) return -1; /* don't pass down, do tail work. */ } return 0; /* pass syscall down to the host. */ } ``` 這個函式的處理邏輯是這樣,怎樣區分xenomai系統呼叫和linux系統呼叫?每個CPU架構不同linux系統呼叫總數不同,在x86系統中有300多個,用變數`NR_syscalls`表示,系統呼叫號與系統呼叫一一對應。首先獲取到的系統呼叫號`nr >= NR_syscalls`,不用多想,那這個系統呼叫是xenomai核心的系統呼叫。 另外還有個問題,如果是Linux非實時任務觸發的xenomai系統呼叫,或者xenomai 實時任務要呼叫linux的服務,這些交叉服務涉及實時任務與非實時任務在兩個核心之間執行,優先順序怎麼處理等問題。這些涉及`cobalt_sysmodes[]`. 首先看怎麼區分一個任務是realtime還是no_realtime。在`task_struct`結構的頭有一個成員結構體`thread_info`,儲存著當前執行緒的資訊,**ipipe**在結構體`thread_info`中增加了兩個成員變數`ipipe_flags`和`ipipe_data`,`ipipe_flags`用來來標示一個執行緒是實時還是非實時,**_TIP_HEAD**置位表示已經是實時上下文。對於需要切換到xenomai上下文的系統呼叫**_TIP_NOTIFY**置位。 ```c struct thread_info { unsigned long flags; /* low level flags */ u32 status; /* thread synchronous flags */ #ifdef CONFIG_IPIPE unsigned long ipipe_flags; struct ipipe_threadinfo ipipe_data; #endif }; ``` `ipipe_handle_syscall`處理邏輯: 1.對於已經在實時上下文的實時任務發起xenomai的系統呼叫,使用快速呼叫路徑函式`ipipe_fastcall_hook(regs)`; 2.需要切換到實時上下文或者非實時呼叫實時的,使用慢速呼叫路徑: > __ipipe_notify_syscall(regs) > ->ipipe_syscall_hook(caller_domain, regs) 快速呼叫`ipipe_fastcall_hook(regs)`內直接`handle_head_syscall`執行程式碼如下: ```c static int handle_head_syscall(struct ipipe_domain *ipd, struct pt_regs *regs) { .... code = __xn_syscall(regs); nr = code & (__NR_COBALT_SYSCALLS - 1); ...... handler = cobalt_syscalls[code]; sysflags = cobalt_sysmodes[nr]; ........ ret = handler(__xn_reg_arglist(regs)); ....... __xn_status_return(regs, ret); ....... } ``` 這個函式很複雜,涉及xenomai與linux之間很多聯絡,程式碼是簡化後的,先取出系統呼叫號,然後從`cobalt_syscalls`取出系統呼叫入口handler,然後執行`handler(__xn_reg_arglist(regs))`執行完成後將執行結果放到暫存器`ax`,後面的文章會詳細分析ipipe如何處理系統呼叫。 ### 三、 64位系統呼叫 我們再來看 64 位的情況,系統呼叫,不是用中斷了,而是改用 syscall 指令。並且傳遞引數的暫存器也變了。![](https://wsg-blogs-pic.oss-cn-beijing.aliyuncs.com/xenomai/syscall-g.png) ``` #define DO_SYSCALL(name, nr, args...) \ ({ \ unsigned long __resultvar; \ LOAD_ARGS_##nr(args) \ LOAD_REGS_##nr \ asm volatile ( \ "syscall\n\t" \ : "=a" (__resultvar) \ : "0" (name) ASM_ARGS_##nr \ : "memory", "cc", "r11", "cx"); \ (int) __resultvar; \ }) #define XENOMAI_DO_SYSCALL(nr, op, args...) \ DO_SYSCALL(__xn_syscode(op), nr, args) #define XENOMAI_SYSBIND(breq) \ XENOMAI_DO_SYSCALL(1, sc_cobalt_bind, breq) ``` 這裡將系統呼叫號使用`__xn_syscode(op)`處理了一下,把最高位置1,表示Cobalt系統呼叫,然後使用syscall 指令。 ```c #define __COBALT_SYSCALL_BIT 0x10000000 #define __xn_syscode(__nr) (__COBALT_SYSCALL_BIT | (__nr)) ``` syscall 指令還使用了一種特殊的暫存器,我們叫特殊模組暫存器(Model Specific Registers,簡稱 MSR)。這種暫存器是 CPU 為了完成某些特殊控制功能為目的的暫存器,其中就有系統呼叫。在系統初始化的時候,trap_init 除了初始化上面的中斷模式,這裡面還會呼叫 cpu_init->syscall_init。這裡面有這樣的程式碼: ```c wrmsrl(MSR_LSTAR, (unsigned long)entry_SYSCALL_64); ``` rdmsr 和 wrmsr 是用來讀寫特殊模組暫存器的。MSR_LSTAR 就是這樣一個特殊的暫存器, 當 syscall 指令呼叫的時候,會從這個暫存器裡面拿出函式地址來呼叫,也就是調entry_SYSCALL_64。 該函式在'entry_64.S'定義: ```asm ENTRY(entry_SYSCALL_64) UNWIND_HINT_EMPTY ...... swapgs /* * This path is only taken when PAGE_TABLE_ISOLATION is disabled so it * is not required to switch CR3. */ movq %rsp, PER_CPU_VAR(rsp_scratch) movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp /* Construct struct pt_regs on stack */ pushq $__USER_DS /* pt_regs->ss */ pushq PER_CPU_VAR(rsp_scratch) /* pt_regs->sp */ pushq %r11 /* pt_regs->flags */ pushq $__USER_CS /* pt_regs->cs */ pushq %rcx /* pt_regs->ip *//*儲存使用者太指令指標暫存器*/ GLOBAL(entry_SYSCALL_64_after_hwframe) pushq %rax /* pt_regs->orig_ax */ PUSH_AND_CLEAR_REGS rax=$-ENOSYS TRACE_IRQS_OFF /* IRQs are off. */ movq %rsp, %rdi call do_syscall_64 /* returns with IRQs disabled */ TRACE_IRQS_IRETQ /* we're about to change IF */ /* * Try to use SYSRET instead of IRET if we're returning to * a completely clean 64-bit userspace context. If we're not, * go to the slow exit path. */ movq RCX(%rsp), %rcx movq RIP(%rsp), %r11 cmpq %rcx, %r11 /* SYSRET requires RCX == RIP */ jne swapgs_restore_regs_and_return_to_usermode ....... testq $(X86_EFLAGS_RF|X86_EFLAGS_TF), %r11 jnz swapgs_restore_regs_and_return_to_usermode /* nothing to check for RSP */ cmpq $__USER_DS, SS(%rsp) /* SS must match SYSRET */ jne swapgs_restore_regs_and_return_to_usermode /* * We win! This label is here just for ease of understanding * perf profiles. Nothing jumps here. */ syscall_return_via_sysret: /* rcx and r11 are already restored (see code above) */ UNWIND_HINT_EMPTY POP_REGS pop_rdi=0 skip_r11rcx=1 /* * Now all regs are restored except RSP and RDI. * Save old stack pointer and switch to trampoline stack. */ movq %rsp, %rdi movq PER_CPU_VAR(cpu_tss_rw + TSS_sp0), %rsp pushq RSP-RDI(%rdi) /* RSP */ pushq (%rdi) /* RDI */ /* * We are on the trampoline stack. All regs except RDI are live. * We can do future final exit work right here. */ SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi popq %rdi popq %rsp USERGS_SYSRET64 END(entry_SYSCALL_64) ``` 這裡先儲存了很多暫存器到 pt_regs 結構裡面,例如使用者態的程式碼段、資料段、儲存引數的暫存器. ![](https://wsg-blogs-pic.oss-cn-beijing.aliyuncs.com/xenomai/syscall-64-reg.png) 然後呼叫` entry_SYSCALL64_slow_pat->do_syscall_64`。 ```C __visible void do_syscall_64(struct pt_regs *regs) { struct thread_info *ti = current_thread_info(); unsigned long nr = regs->orig_ax; /*取出系統呼叫號*/ int ret; enter_from_user_mode(); enable_local_irqs(); ret = ipipe_handle_syscall(ti, nr & __SYSCALL_MASK, regs); if (ret > 0) { disable_local_irqs(); return; } if (ret < 0) goto done; ...... if (likely((nr & __SYSCALL_MASK) < NR_syscalls)) { nr = array_index_nospec(nr & __SYSCALL_MASK, NR_syscalls); regs->ax = sys_call_table[nr]( regs->di, regs->si, regs->dx, regs->r10, regs->r8, regs->r9); } done: syscall_return_slowpath(regs); } ``` 與32位一樣,ipipe攔截了系統呼叫,後面的處理流程類似所以,無論是 32 位,還是 64 位,都會到linux系統呼叫表` sys_call_table`和xenomai系統呼叫表`cobalt_syscalls[] `這裡來。 ### 五、 實時系統呼叫表cobalt_syscalls xenomai每個系統的系統系統呼叫號在`\cobalt\uapi\syscall.h`中: ```c #define sc_cobalt_bind 0 #define sc_cobalt_thread_create 1 #define sc_cobalt_thread_getpid 2 ...... #define sc_cobalt_extend 96 ``` `bind()`函式在核心程式碼中對應的宣告和實現為: ```c /*宣告*/ #define COBALT_SYSCALL_DECL(__name, __args) \ long CoBaLt_ ## __name __args static COBALT_SYSCALL_DECL(bind, lostage, (struct cobalt_bindreq __user *u_breq)); /*實現*/ #define COBALT_SYSCALL(__name, __mode, __args) \ long CoBaLt_ ## __name __args static COBALT_SYSCALL(bind, lostage, (struct cobalt_bindreq __user *u_breq)){......} ``` 其中`__name`表示系統呼叫名對應bind、`__mode`表示該系統呼叫模式對應lostage。**`COBALT_SYSCALL`**展開定義的bind函式後如下: ```c long CoBaLt_bind(struct cobalt_bindreq __user *u_breq){......} ``` 怎麼將`CoBaLt_bind`與系統呼叫號`sc_cobalt_bind`聯絡起來後放入`cobalt_syscalls[]`的呢? 在編譯過程中Makefile使用指令碼`gen-syscall-entries.sh`處理各個`.c`檔案中的**COBALT_SYSCALL**巨集,生成一個頭檔案`syscall_entries.h`,裡面是對每個**COBALT_SYSCALL**巨集處理後後的項,以上面`COBALT_SYSCALL(bind,...)`為例`syscall_entries.h`中會生成如下兩項,第一項為系統呼叫入口,第二項為系統呼叫的模式: ```c #define __COBALT_CALL_ENTRIES __COBALT_CALL_ENTRY(bind) #define __COBALT_CALL_MODES __COBALT_MODE(lostage) ``` 實時系統呼叫表**`cobalt_syscalls[]`**定義在檔案`kernel\cobalt\posix\syscall.c`中: ```c #define __syshand__(__name) ((cobalt_syshand)(CoBaLt_ ## __name)) #define __COBALT_NI __syshand__(ni) #define __COBALT_CALL_NI \ [0 ... __NR_COBALT_SYSCALLS-1] = __COBALT_NI, \ __COBALT_CALL32_INITHAND(__COBALT_NI) #define __COBALT_CALL_NFLAGS \ [0 ... __NR_COBALT_SYSCALLS-1] = 0, \ __COBALT_CALL32_INITMODE(0) #define __COBALT_CALL_ENTRY(__name) \ [sc_cobalt_ ## __name] = __syshand__(__name), \ __COBALT_CALL32_ENTRY(__name, __syshand__(__name)) #define __COBALT_MODE(__name, __mode) \ [sc_cobalt_ ## __name] = __xn_exec_##__mode, #include "syscall_entries.h" /*該標頭檔案由指令碼生成*/ static const cobalt_syshand cobalt_syscalls[] = { __COBALT_CALL_NI __COBALT_CALL_ENTRIES }; static const int cobalt_sysmodes[] = { __COBALT_CALL_NFLAGS __COBALT_CALL_MODES }; ``` **__COBALT_CALL_NI**巨集表示陣列空間大小為\_\_NR_COBALT_SYSCALLS(128),每一項由__COBALT_CALL_ENTRIES定義,即指令碼標頭檔案`syscall_entries.h`中生成的每一項來填充: ```c #define __COBALT_CALL_ENTRY(__name) \ [sc_cobalt_ ## __name] = __syshand__(__name), \ __COBALT_CALL32_ENTRY(__name, __syshand__(__name)) ``` ```__COBALT_CALL32_ENTRY```是定義相容的系統呼叫,巨集展開如下,相當於在陣列的多個位置定義包含了同一項`CoBaLt_bind`: ```c #define __COBALT_CALL32_ENTRY(__name, __handler) \ __COBALT_CALL32x_ENTRY(__name, __handler) \ __COBALT_CALL32emu_ENTRY(__name, __handler) #define __COBALT_CALL32emu_ENTRY(__name, __handler) \ [sc_cobalt_ ## __name + 256] = __handler, #define __COBALT_CALL32x_ENTRY(__name, __handler) \ [sc_cobalt_ ## __name + 128] = __handler, ``` 最後bind系統呼叫在cobalt_syscalls[]中如下 ```c static const cobalt_syshand cobalt_syscalls[] = { [sc_cobalt_bind] = CoBaLt_bind, [sc_cobalt_bind + 128] = CoBaLt_bind, /*x32 support */ [sc_cobalt_bind + 256] = CoBaLt_bind, /*ia32 emulation support*/ ..... }; ``` 相應的陣列`cobalt_sysmodes[]`中的內容如下: ```c static const int cobalt_sysmodes[] = { [sc_cobalt_bind] = __xn_exec_bind, [sc_cobalt_bind + 256] = __xn_exec_lostage, /*x32 support */ [sc_cobalt_bind + 128] = __xn_exec_lostage, /*ia32 emulation support*/ ...... }; ``` ### 六、實時系統呼叫許可權控制cobalt_sysmodes 上面說到,ipipe管理應用的系統呼叫時需要分清該系統呼叫是否合法,是否需要域切換等等。`cobalt_sysmodes[]`就是每個系統呼叫對應的模式,控制著每個系統呼叫的呼叫路徑。系統呼叫號為下標,值為具體模式。每個系統呼叫的sysmode如何生成見上一節,還是以實時應用的`bind`系統呼叫為例: ```C static const int cobalt_sysmodes[] = { [sc_cobalt_bind] = __xn_exec_bind, [sc_cobalt_bind + 256] = __xn_exec_lostage, /*x32 support */ [sc_cobalt_bind + 128] = __xn_exec_lostage, /*ia32 emulation support*/ ...... }; ``` xenomai中所有的系統呼叫模式定義如下: ```C /*xenomai\posix\syscall.c*/ #define __xn_exec_lostage 0x1 /*該系統呼叫必須執行在linux域*/ #define __xn_exec_histage 0x2 /*該系統呼叫必須執行在Xenomai域*/ #define __xn_exec_shadow 0x4 /*影子系統呼叫:必須對映呼叫方*/ #define __xn_exec_switchback 0x8 /*切換回切換; 呼叫者必須返回其原始模式*/ #define __xn_exec_current 0x10 /*在當前域中執行。*/ #define __xn_exec_conforming 0x20 /*在相容域(Xenomai或Linux)中執行*/ #define __xn_exec_adaptive 0x40 /* 在-ENOSYS上嘗試在相反的域中重新啟動系統呼叫 */ #define __xn_exec_norestart 0x80 /*收到訊號後不要重新啟動syscall*/ /*Shorthand初始化系統呼叫的簡寫*/ #define __xn_exec_init __xn_exec_lostage /*Xenomai空間中shadow系統呼叫的簡寫*/ #define __xn_exec_primary (__xn_exec_shadow|__xn_exec_histage) /*Linux空間中shadow系統呼叫的簡寫*/ #define __xn_exec_secondary (__xn_exec_shadow|__xn_exec_lostage) /*Linux空間中syscall的簡寫,如果有shadow則切換回去*/ #define __xn_exec_downup (__xn_exec_lostage|__xn_exec_switchback) /* 不可重啟主系統呼叫的簡寫 */ #define __xn_exec_nonrestartable (__xn_exec_primary|__xn_exec_norestart) /*域探測系統呼叫以一致模式啟動。*/ #define __xn_exec_probing (__xn_exec_conforming|__xn_exec_adaptive) /*將模式選擇移交給syscall。*/ #define __xn_exec_handover (__xn_exec_current|__xn_exec_adaptive) ``` 使用一個無符號32 位數的每一位來表示一種模式,各模式註釋已經很清楚,不在解釋,後面文章解析ipipe是如何執行這些mode的。 使用一個無符號32 位數的每一位來表示一種模式,各模式註釋已經很清楚,不在解釋,後面文章解析ipipe是如何根據mode來處理的。 ### 參考 [英特爾® 64 位和 IA-32 架構軟體開發人員手冊第 3 卷 :系統程式設計指南](https://www.intel.cn/content/www/cn/zh/architecture-and-technology/64-ia-32-architectures-software-developer-system-programming-manual-325384.html) [極客時間專欄-趣談Linux作業系統](https://time.geekbang.org/column/article/98855) 《linux核心原始碼情景