xenomai內核解析之雙核系統調用(一)

版權聲明:本文為本文為博主原創文章,轉載請註明出處。如有錯誤,歡迎指正。博客地址:https://www.cnblogs.com/wsg1100/

目錄

  • xenomai 內核系統調用
    • 一、32位Linux系統調用
    • 二、32位實時系統調用
    • 三、 64位系統調用
    • 五、 實時系統調用表cobalt_syscalls
    • 六、實時系統調用權限控制cobalt_sysmodes
    • 參考

xenomai 內核系統調用

解析系統調用是了解內核架構最有力的一把鑰匙,在這之前先搞懂xenomai與linux兩個內核共存后系統調用是如何實現的。

為什麼需要系統調用

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如下:

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定義:

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實現如下:

__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系統調用表生成後面解析(此圖來源於網絡)。

相關內核調用執行完后,一直返回到 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函數實現如下:

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_SYSCALL5XENOAI_SYSCALL宏在\include\asm\xenomai\syscall.h中聲明,XENOMAI_SYSCALL5中的’5’代表’該系統調用有五個參數:

#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

#define DOSYSCALL  "int $0x80\n\t"

系統調用過程硬件處理及中斷入口上節一致,從do_syscall_32_irqs_on開始不同,有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;/*取出系統調用號*/
	int ret;
	
	ret = pipeline_syscall(ti, nr, regs);/*pipeline 攔截系統調用*/
	......
done:
	syscall_return_slowpath(regs);
}

套路和ipipe接管中斷類似,在關鍵路徑上攔截系統調用,然後調用ipipe_handle_syscall(ti, nr, regs)讓ipipe來接管處理:

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_flagsipipe_data,ipipe_flags用來來標示一個線程是實時還是非實時,_TIP_HEAD置位表示已經是實時上下文。對於需要切換到xenomai上下文的系統調用_TIP_NOTIFY置位。

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執行代碼如下:

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 指令。並且傳遞參數的寄存器也變了。

#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 指令。

#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。這裏面有這樣的代碼:

wrmsrl(MSR_LSTAR, (unsigned long)entry_SYSCALL_64);

rdmsr 和 wrmsr 是用來讀寫特殊模塊寄存器的。MSR_LSTAR 就是這樣一個特殊的寄存器, 當 syscall 指令調用的時候,會從這個寄存器裏面拿出函數地址來調用,也就是調entry_SYSCALL_64。
該函數在’entry_64.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 結構裏面,例如用戶態的代碼段、數據段、保存參數的寄存器.

然後調用 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;	/*取出系統調用號*/
	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中:


#define sc_cobalt_bind				0
#define sc_cobalt_thread_create			1
#define sc_cobalt_thread_getpid			2
	......
#define sc_cobalt_extend			96

bind()函數在內核代碼中對應的聲明和實現為:

/*聲明*/
#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函數后如下:

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中會生成如下兩項,第一項為系統調用入口,第二項為系統調用的模式:

#define __COBALT_CALL_ENTRIES __COBALT_CALL_ENTRY(bind)
#define __COBALT_CALL_MODES	__COBALT_MODE(lostage)

實時系統調用表cobalt_syscalls[]定義在文件kernel\cobalt\posix\syscall.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中生成的每一項來填充:

#define __COBALT_CALL_ENTRY(__name)				\
	[sc_cobalt_ ## __name] = __syshand__(__name),		\
	__COBALT_CALL32_ENTRY(__name, __syshand__(__name))

__COBALT_CALL32_ENTRY是定義兼容的系統調用,宏展開如下,相當於在數組的多個位置定義包含了同一項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系統調用在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*/
	.....
};

相應的數組cobalt_sysmodes[]中的內容如下:

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系統調用為例:

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中所有的系統調用模式定義如下:

/*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則切換回linux*/
#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來處理的。

參考

英特爾® 64 位和 IA-32 架構軟件開發人員手冊第 3 卷 :系統編程指南
極客時間專欄-趣談Linux操作系統
《linux內核源代碼情景分析》

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

聚甘新