雙核系統(tǒng)調(diào)用(ipipe)
解析系統(tǒng)調(diào)用是了解內(nèi)核架構(gòu)最有力的一把鑰匙。
在Linux內(nèi)核基礎(chǔ)上加入xenomai實(shí)時(shí)系統(tǒng)內(nèi)核后,在內(nèi)核空間兩個(gè)內(nèi)核共存,實(shí)時(shí)任務(wù)需要xenomai內(nèi)核來(lái)完成實(shí)時(shí)的服務(wù),如果實(shí)時(shí)任務(wù)需要用到linux的服務(wù),還可以調(diào)用linux內(nèi)核的系統(tǒng)調(diào)用,你可能會(huì)好奇xenomai與linux兩個(gè)內(nèi)核共存后系統(tǒng)調(diào)用是如何實(shí)現(xiàn)的?
為什么需要系統(tǒng)調(diào)用?現(xiàn)代操作系統(tǒng)中,處理器的運(yùn)行模式一般分為兩個(gè)空間:內(nèi)核空間和用戶空間,大部分應(yīng)用程序運(yùn)行在用戶空間,而操作系統(tǒng)內(nèi)核和設(shè)備驅(qū)動(dòng)程序運(yùn)行在內(nèi)核空間,如果應(yīng)用程序需要訪問(wèn)硬件資源或者需要內(nèi)核提供服務(wù),該怎么辦?
為了向用戶空間上運(yùn)行的應(yīng)用程序提供服務(wù),內(nèi)核提供了一組接口。透過(guò)該接口,應(yīng)用程序可以訪問(wèn)硬件設(shè)備和其他操作系統(tǒng)資源。這組接口在應(yīng)用程序和內(nèi)核之間扮演了使者的角色,應(yīng)用程序發(fā)送各種請(qǐng)求,而內(nèi)核負(fù)責(zé)滿足這些請(qǐng)求,這些接口就是系統(tǒng)調(diào)用,它是用戶空間和內(nèi)核空間一個(gè)中間層。
系統(tǒng)調(diào)用層主要作用有三個(gè):
-
它為用戶空間提供了一種統(tǒng)一的硬件的抽象接口。比如當(dāng)需要讀些文件的時(shí)候,應(yīng)用程序就可以不去管磁盤(pán)類型和介質(zhì),甚至不用去管文件所在的文件系統(tǒng)到底是哪種類型。
-
系統(tǒng)調(diào)用保證了系統(tǒng)的穩(wěn)定和安全。應(yīng)用程序要訪問(wèn)內(nèi)核就必須通過(guò)系統(tǒng)調(diào)用層,內(nèi)核可以在系統(tǒng)調(diào)用層對(duì)應(yīng)用程序的訪問(wèn)權(quán)限、用戶類型和其他一些規(guī)則進(jìn)行過(guò)濾,這避免了應(yīng)用不正確地訪問(wèn)內(nèi)核,保證了系統(tǒng)和各個(gè)應(yīng)用程序的安全性。
-
可移植性。可以讓?xiě)?yīng)用程序在不修改源代碼的情況下,在不同的操作系統(tǒng)或擁有不同硬件架構(gòu)的系統(tǒng)中重新編譯運(yùn)行。
回到本文開(kāi)頭的問(wèn)題,該問(wèn)題細(xì)分為如下兩個(gè)問(wèn)題:
-
雙核共存時(shí),如何區(qū)分應(yīng)用發(fā)起的系統(tǒng)調(diào)用是xenomai內(nèi)核調(diào)用還是linux內(nèi)核調(diào)用?
-
一個(gè)xenomai實(shí)時(shí)任務(wù)既可以調(diào)用xenomai內(nèi)核服務(wù),也可以調(diào)用linux內(nèi)核服務(wù),這是如何做到的?
本文通過(guò)分析源代碼為你解答問(wèn)題1,對(duì)于問(wèn)題2,涉及雙核間的調(diào)度,本文暫不涉及,后面的文章揭曉答案。
一、32位Linux系統(tǒng)調(diào)用
我們先來(lái)看沒(méi)有ipipe和xenomai內(nèi)核時(shí)的linux系統(tǒng)調(diào)用流程是怎樣的。linux操作系統(tǒng)的API通常以C標(biāo)準(zhǔn)庫(kù)的方式提供,比如linux中的libc庫(kù)。C標(biāo)準(zhǔn)庫(kù)中提供了POSIX的絕大部分API實(shí)現(xiàn),glibc為了提高應(yīng)用程序的性能,還對(duì)一些系統(tǒng)調(diào)用進(jìn)行了封裝。此外,由于32位系統(tǒng)系統(tǒng)調(diào)用使用軟中斷 int0x80
指令實(shí)現(xiàn),應(yīng)用程序也可以通過(guò)匯編直接進(jìn)行系統(tǒng)調(diào)用。軟中斷屬于異常的一種,通過(guò)執(zhí)行該指令陷入(trap)內(nèi)核,trap在整理的文檔 x86Linux中斷系統(tǒng)
有說(shuō)明。內(nèi)核初始化過(guò)程中,通過(guò)函數(shù) tarp_init()
設(shè)置IDT(Interrupt Descriptor Table 記錄每個(gè)中斷異常處理程序的地址的一張表),有關(guān) int0x80
的IDT表項(xiàng)如下:
static const __initconst struct idt_data def_idts[] = {
......
SYSG(IA32_SYSCALL_VECTOR, entry_INT80_32),
......
};
當(dāng)產(chǎn)生系統(tǒng)調(diào)用時(shí),硬件根據(jù)向量號(hào)在 IDT 中找到對(duì)應(yīng)的表項(xiàng),即中斷描述符,進(jìn)行特權(quán)級(jí)檢查,發(fā)現(xiàn) DPL = CPL = 3 ,允許調(diào)用。然后硬件將切換到內(nèi)核棧 (tss.ss0 : tss.esp0)。接著根據(jù)中斷描述符的 segment selector 在 GDT / LDT 中找到對(duì)應(yīng)的段描述符,從段描述符拿到段的基址,加載到 cs 。將 offset 加載到 eip。最后硬件將 ss / sp / eflags / cs / ip / error code 依次壓到內(nèi)核棧。于是開(kāi)始執(zhí)行 entry_INT80_32
函數(shù),該函數(shù)在 entry_32.S
定義:
ENTRY(entry_INT80_32)
ASM_CLAC
pushl %eax /* pt_regs->orig_ax */
SAVE_ALL pt_regs_ax=$-ENOSYS /* *存儲(chǔ)當(dāng)前用戶態(tài)寄存器,保存在pt_regs結(jié)構(gòu)里*/
/*
* 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 指令將原來(lái)用戶態(tài)保存的現(xiàn)場(chǎng)恢復(fù)回來(lái),包含代碼段、指令指針寄存器等。這時(shí)候用戶態(tài)
進(jìn)程恢復(fù)執(zhí)行。*/
在內(nèi)核棧的最高地址端,存放的是結(jié)構(gòu) ptregs,首先通過(guò) push 和 SAVEALL 將當(dāng)前用戶態(tài)的寄存器,保存在棧中 ptregs 結(jié)構(gòu)里面.保存完畢后,關(guān)閉中斷,將當(dāng)前棧指針保存到 eax,即doint80syscall32的參數(shù)1。調(diào)用doint80syscall32=>dosyscall32irqs_on。先看看沒(méi)有ipipe時(shí)Linux實(shí)現(xiàn)如下:
__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]( /*根據(jù)系統(tǒng)調(diào)用號(hào)索引直接執(zhí)行*/
(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);
}
在這里,將系統(tǒng)調(diào)用號(hào)從pt_reges中eax 里面取出來(lái),然后根據(jù)系統(tǒng)調(diào)用號(hào),在系統(tǒng)調(diào)用表中找到相應(yīng)的函數(shù)進(jìn)行調(diào)用,并將寄存器中保存的參數(shù)取出來(lái),作為函數(shù)參數(shù)。如果仔細(xì)比對(duì),就能發(fā)現(xiàn),這些參數(shù)所對(duì)應(yīng)的寄存器,和 Linux 的注釋是一樣的。ia32_sys_call_table
系統(tǒng)調(diào)用表生成后面解析(此圖來(lái)源于網(wǎng)絡(luò))。
相關(guān)內(nèi)核調(diào)用執(zhí)行完后,一直返回到 dosyscall32irqson ,如果系統(tǒng)調(diào)用有返回值,會(huì)被保存到 regs->ax 中。接著返回 entryINT8032 繼續(xù)執(zhí)行,最后執(zhí)行 INTERRUPTRETURN 。INTERRUPTRETURN 在 arch/x86/include/asm/irqflags.h
中定義為 iret ,iret 指令將原來(lái)用戶態(tài)保存的現(xiàn)場(chǎng)恢復(fù)回來(lái),包含代碼段、指令指針寄存器等。這時(shí)候用戶態(tài)進(jìn)程恢復(fù)執(zhí)行。
系統(tǒng)調(diào)用執(zhí)行完畢。
二、32位實(shí)時(shí)系統(tǒng)調(diào)用
xenomai+linux雙內(nèi)核架構(gòu)下,通過(guò)I-pipe 攔截系統(tǒng)調(diào)用,并將系統(tǒng)調(diào)用定向到實(shí)現(xiàn)它們的系統(tǒng)。
實(shí)時(shí)系統(tǒng)調(diào)用,除了直接通過(guò)匯編系統(tǒng)調(diào)用外,xenomai還實(shí)現(xiàn)了libcoblat實(shí)時(shí)庫(kù),相當(dāng)于glibc,通過(guò)libcoblat進(jìn)行xenomai系統(tǒng)調(diào)用,以libcoblat庫(kù)函數(shù)sem_open為例,libcolat庫(kù)中C函數(shù)實(shí)現(xiàn)如下:
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庫(kù)調(diào)用系統(tǒng)調(diào)用使用宏 XENOMAI_SYSCALL5
,XENOAI_SYSCALL宏在 includeasmxenomaisyscall.h
中聲明, XENOMAI_SYSCALL5
中的'5'代表'該系統(tǒng)調(diào)用有五個(gè)參數(shù):
({
unsigned __resultvar;
asm volatile (
LOADARGS_##nr
DOSYSCALL
RESTOREARGS_##nr
: (__resultvar)
: (__xn_syscode(op)) ASMFMT_##nr(args)
: , );
(int) __resultvar;
})
每個(gè)宏中,內(nèi)嵌另一個(gè)宏DOSYSCALL,即實(shí)現(xiàn)系統(tǒng)調(diào)用的int指令:int$0x80
。
"int$0x80
" defineDOSYSCALL
系統(tǒng)調(diào)用過(guò)程硬件處理及中斷入口上節(jié)一致,從 do_syscall_32_irqs_on
開(kāi)始不同,有ipipe后變成下面這樣子:
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;/*取出系統(tǒng)調(diào)用號(hào)*/
int ret;
ret = pipeline_syscall(ti, nr, regs);/*pipeline 攔截系統(tǒng)調(diào)用*/
......
done:
syscall_return_slowpath(regs);
}
套路和ipipe接管中斷類似,在關(guān)鍵路徑上攔截系統(tǒng)調(diào)用,然后調(diào)用 ipipe_handle_syscall(ti,nr,regs)
讓ipipe來(lái)接管處理:
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)) {/*運(yùn)行在head域且者系統(tǒng)調(diào)用號(hào)超過(guò)linux*/
ipipe_fastcall_hook(regs); /*快速系統(tǒng)調(diào)用路徑*/
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. */
}
這個(gè)函數(shù)的處理邏輯是這樣,怎樣區(qū)分xenomai系統(tǒng)調(diào)用和linux系統(tǒng)調(diào)用?每個(gè)CPU架構(gòu)不同linux系統(tǒng)調(diào)用總數(shù)不同,在x86系統(tǒng)中有300多個(gè),用變量 NR_syscalls
表示,系統(tǒng)調(diào)用號(hào)與系統(tǒng)調(diào)用一一對(duì)應(yīng)。首先獲取到的系統(tǒng)調(diào)用號(hào) nr>=NR_syscalls
,不用多想,那這個(gè)系統(tǒng)調(diào)用是xenomai內(nèi)核的系統(tǒng)調(diào)用。另外還有個(gè)問(wèn)題,如果是Linux非實(shí)時(shí)任務(wù)觸發(fā)的xenomai系統(tǒng)調(diào)用,或者xenomai 實(shí)時(shí)任務(wù)要調(diào)用linux的服務(wù),這些交叉服務(wù)涉及實(shí)時(shí)任務(wù)與非實(shí)時(shí)任務(wù)在兩個(gè)內(nèi)核之間運(yùn)行,優(yōu)先級(jí)怎么處理等問(wèn)題。這些涉及 cobalt_sysmodes[]
.
首先看怎么區(qū)分一個(gè)任務(wù)是realtime還是norealtime。在 task_struct
結(jié)構(gòu)的頭有一個(gè)成員結(jié)構(gòu)體 thread_info
,存儲(chǔ)著當(dāng)前線程的信息,ipipe在結(jié)構(gòu)體 thread_info
中增加了兩個(gè)成員變量 ipipe_flags
和 ipipe_data
, ipipe_flags
用來(lái)來(lái)標(biāo)示一個(gè)線程是實(shí)時(shí)還是非實(shí)時(shí),TIPHEAD置位表示已經(jīng)是實(shí)時(shí)上下文。對(duì)于需要切換到xenomai上下文的系統(tǒng)調(diào)用TIP_NOTIFY置位。
struct thread_info {
unsigned long flags; /* low level flags */
u32 status; /* thread synchronous flags */
unsigned long ipipe_flags;
struct ipipe_threadinfo ipipe_data;
};
ipipe_handle_syscall
處理邏輯:1.對(duì)于已經(jīng)在實(shí)時(shí)上下文的實(shí)時(shí)任務(wù)發(fā)起xenomai的系統(tǒng)調(diào)用,使用快速調(diào)用路徑函數(shù) ipipe_fastcall_hook(regs)
;2.需要切換到實(shí)時(shí)上下文或者非實(shí)時(shí)調(diào)用實(shí)時(shí)的,使用慢速調(diào)用路徑:
_ipipenotifysyscall(regs)->ipipesyscallhook(callerdomain, regs)
快速調(diào)用 ipipe_fastcall_hook(regs)
內(nèi)直接 handle_head_syscall
執(zhí)行代碼如下:
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));
.......
ret);
.......
}
這個(gè)函數(shù)很復(fù)雜,涉及xenomai與linux之間很多聯(lián)系,代碼是簡(jiǎn)化后的,先取出系統(tǒng)調(diào)用號(hào),然后從 cobalt_syscalls
取出系統(tǒng)調(diào)用入口handler,然后執(zhí)行 handler(__xn_reg_arglist(regs))
執(zhí)行完成后將執(zhí)行結(jié)果放到寄存器 ax
,后面的文章會(huì)詳細(xì)分析ipipe如何處理系統(tǒng)調(diào)用。
三、 64位系統(tǒng)調(diào)用
我們?cè)賮?lái)看 64 位的情況,系統(tǒng)調(diào)用,不是用中斷了,而是改用 syscall 指令。并且傳遞參數(shù)的寄存器也變了。
({
unsigned long __resultvar;
LOAD_ARGS_
LOAD_REGS_
asm volatile (
"syscall "
: "=a" (__resultvar)
: "0" (name) ASM_ARGS_
: "memory", "cc", "r11", "cx");
(int) __resultvar;
})
DO_SYSCALL(__xn_syscode(op), nr, args)
XENOMAI_DO_SYSCALL(1,sc_cobalt_bind,breq)
這里將系統(tǒng)調(diào)用號(hào)使用 __xn_syscode(op)
處理了一下,把最高位置1,表示Cobalt系統(tǒng)調(diào)用,然后使用syscall 指令。
syscall 指令還使用了一種特殊的寄存器,我們叫特殊模塊寄存器(Model Specific Registers,簡(jiǎn)稱 MSR)。這種寄存器是 CPU 為了完成某些特殊控制功能為目的的寄存器,其中就有系統(tǒng)調(diào)用。在系統(tǒng)初始化的時(shí)候,trapinit 除了初始化上面的中斷模式,這里面還會(huì)調(diào)用 cpuinit->syscall_init。這里面有這樣的代碼:
wrmsrl(MSR_LSTAR,(unsignedlong)entry_SYSCALL_64);
rdmsr 和 wrmsr 是用來(lái)讀寫(xiě)特殊模塊寄存器的。MSRLSTAR 就是這樣一個(gè)特殊的寄存器, 當(dāng) syscall 指令調(diào)用的時(shí)候,會(huì)從這個(gè)寄存器里面拿出函數(shù)地址來(lái)調(diào)用,也就是調(diào)entrySYSCALL64。該函數(shù)在'entry64.S'定義:
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 結(jié)構(gòu)里面,例如用戶態(tài)的代碼段、數(shù)據(jù)段、保存參數(shù)的寄存器.
然后調(diào)用 entry_SYSCALL64_slow_pat->do_syscall_64
。
__visible void do_syscall_64(struct pt_regs *regs)
{
struct thread_info *ti = current_thread_info();
unsigned long nr = regs->orig_ax; /*取出系統(tǒng)調(diào)用號(hào)*/
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攔截了系統(tǒng)調(diào)用,后面的處理流程類似所以,無(wú)論是 32 位,還是 64 位,都會(huì)到linux系統(tǒng)調(diào)用表 sys_call_table
和xenomai系統(tǒng)調(diào)用表 cobalt_syscalls[]
這里來(lái)。
四、 實(shí)時(shí)系統(tǒng)調(diào)用表cobalt_syscalls
xenomai每個(gè)系統(tǒng)的系統(tǒng)系統(tǒng)調(diào)用號(hào)在 cobaltuapisyscall.h
中:
......
bind()
函數(shù)在內(nèi)核代碼中對(duì)應(yīng)的聲明和實(shí)現(xiàn)為:
/*聲明*/
long CoBaLt_
static COBALT_SYSCALL_DECL(bind, lostage,
(struct cobalt_bindreq __user *u_breq));
/*實(shí)現(xiàn)*/
#define COBALT_SYSCALL(__name, __mode, __args)
long CoBaLt_ ## __name __args
static COBALT_SYSCALL(bind, lostage,
(structcobalt_bindreq__user*u_breq)){......}
其中 __name
表示系統(tǒng)調(diào)用名對(duì)應(yīng)bind、 __mode
表示該系統(tǒng)調(diào)用模式對(duì)應(yīng)lostage。 COBALT_SYSCALL
展開(kāi)定義的bind函數(shù)后如下:
longCoBaLt_bind(structcobalt_bindreq__user*u_breq){......}
怎么將 CoBaLt_bind
與系統(tǒng)調(diào)用號(hào) sc_cobalt_bind
聯(lián)系起來(lái)后放入 cobalt_syscalls[]
的呢?在編譯過(guò)程中Makefile使用腳本 gen-syscall-entries.sh
處理各個(gè) .c
文件中的COBALTSYSCALL宏,生成一個(gè)頭文件 syscall_entries.h
,里面是對(duì)每個(gè)COBALTSYSCALL宏處理后后的項(xiàng),以上面 COBALT_SYSCALL(bind,...)
為例 syscall_entries.h
中會(huì)生成如下兩項(xiàng),第一項(xiàng)為系統(tǒng)調(diào)用入口,第二項(xiàng)為系統(tǒng)調(diào)用的模式:
實(shí)時(shí)系統(tǒng)調(diào)用表 cobalt_syscalls[]
定義在文件 kernelcobaltposixsyscall.c
中:
[ ] = __COBALT_NI,
__COBALT_CALL32_INITHAND(__COBALT_NI)
[0, ] =
__COBALT_CALL32_INITMODE(0)
[ ] = __syshand__(__name),
__COBALT_CALL32_ENTRY(__name, __syshand__(__name))
[ ] = __xn_exec_
static const cobalt_syshand cobalt_syscalls[] = {
__COBALT_CALL_NI
__COBALT_CALL_ENTRIES
};
static const int cobalt_sysmodes[] = {
__COBALT_CALL_NFLAGS
__COBALT_CALL_MODES
};
_COBALTCALLNI宏表示數(shù)組空間大小為_(kāi)_NRCOBALTSYSCALLS(128),每一項(xiàng)由COBALTCALL_ENTRIES定義,即腳本頭文件 syscall_entries.h
中生成的每一項(xiàng)來(lái)填充:
#define __COBALT_CALL_ENTRY(__name)
[sc_cobalt_ ## __name] = __syshand__(__name),
__COBALT_CALL32_ENTRY(__name,__syshand__(__name))
__COBALT_CALL32_ENTRY
是定義兼容的系統(tǒng)調(diào)用,宏展開(kāi)如下,相當(dāng)于在數(shù)組的多個(gè)位置定義包含了同一項(xiàng)CoBaLt_bind
:
#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系統(tǒng)調(diào)用在cobalt_syscalls[]中如下
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*/
.....
};
相應(yīng)的數(shù)組cobalt_sysmodes[]
中的內(nèi)容如下:
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*/
......
};
五、實(shí)時(shí)系統(tǒng)調(diào)用權(quán)限控制cobalt_sysmodes
上面說(shuō)到,ipipe管理應(yīng)用的系統(tǒng)調(diào)用時(shí)需要分清該系統(tǒng)調(diào)用是否合法,是否需要域切換等等。cobalt_sysmodes[]
就是每個(gè)系統(tǒng)調(diào)用對(duì)應(yīng)的模式,控制著每個(gè)系統(tǒng)調(diào)用的調(diào)用路徑。系統(tǒng)調(diào)用號(hào)為下標(biāo),值為具體模式。每個(gè)系統(tǒng)調(diào)用的sysmode如何生成見(jiàn)上一節(jié),還是以實(shí)時(shí)應(yīng)用的bind
系統(tǒng)調(diào)用為例:
static const int cobalt_sysmodes[] = {
[ ] = __xn_exec_bind,
[/*x32 support */ ] = __xn_exec_lostage,
[/*ia32 emulation support*/ ] = __xn_exec_lostage,
......
};
xenomai中所有的系統(tǒng)調(diào)用模式定義如下:
/*xenomaiposixsyscall.c*/
#define __xn_exec_lostage 0x1 /*必須在linux域運(yùn)行該系統(tǒng)調(diào)用*/
#define __xn_exec_histage 0x2 /*必須在Xenomai域運(yùn)行該系統(tǒng)調(diào)用*/
#define __xn_exec_shadow 0x4 /*影子系統(tǒng)調(diào)用:必須映射調(diào)用方*/
#define __xn_exec_switchback 0x8 /*切換回切換;調(diào)用者必須返回其原始模式*/
#define __xn_exec_current 0x10 /*在不管域直接執(zhí)行。*/
#define __xn_exec_conforming 0x20 /*在兼容域(Xenomai或Linux)中執(zhí)行*/
#define __xn_exec_adaptive 0x40 /* 先直接執(zhí)行如果返回-ENOSYS,則嘗試在相反的域中重新執(zhí)行系統(tǒng)調(diào)用 */
#define __xn_exec_norestart 0x80 /*收到信號(hào)后不要重新啟動(dòng)syscall*/
/*Shorthand初始化系統(tǒng)調(diào)用的簡(jiǎn)寫(xiě)*/
#define __xn_exec_init __xn_exec_lostage
/*Xenomai空間中shadow系統(tǒng)調(diào)用的簡(jiǎn)寫(xiě)*/
#define __xn_exec_primary (__xn_exec_shadow|__xn_exec_histage)
/*Linux空間中shadow系統(tǒng)調(diào)用的簡(jiǎn)寫(xiě)*/
#define __xn_exec_secondary (__xn_exec_shadow|__xn_exec_lostage)
/*Linux空間中syscall的簡(jiǎn)寫(xiě),如果有shadow則切換回linux*/
#define __xn_exec_downup (__xn_exec_lostage|__xn_exec_switchback)
/* 主域系統(tǒng)不可重啟調(diào)用的簡(jiǎn)寫(xiě) */
#define __xn_exec_nonrestartable (__xn_exec_primary|__xn_exec_norestart)
/*域探測(cè)系統(tǒng)調(diào)用簡(jiǎn)寫(xiě)*/
#define __xn_exec_probing (__xn_exec_conforming|__xn_exec_adaptive)
/*將模式選擇移交給syscall。*/
#define__xn_exec_handover(__xn_exec_current|__xn_exec_adaptive)
使用一個(gè)無(wú)符號(hào)32 位數(shù)的每一位來(lái)表示一種模式,各模式注釋已經(jīng)很清楚,不在解釋,后面文章解析ipipe是如何根據(jù)mode來(lái)處理的。
參考
英特爾 64 位和 IA-32 架構(gòu)軟件開(kāi)發(fā)人員手冊(cè)第 3 卷 :系統(tǒng)編程指南極客時(shí)間專欄-趣談Linux操作系統(tǒng)《linux內(nèi)核源代碼情景分析》
審核編輯 :李倩
-
Linux
+關(guān)注
關(guān)注
87文章
11312瀏覽量
209711 -
操作系統(tǒng)
+關(guān)注
關(guān)注
37文章
6838瀏覽量
123385 -
雙核
+關(guān)注
關(guān)注
0文章
37瀏覽量
15214
原文標(biāo)題:雙核系統(tǒng)調(diào)用(ipipe)
文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論