xenomai内核解析之双核系统调用(一)
版权声明:本文为本文为博主原创文章,转载请注明出处。如有错误,欢迎指正。博客地址:https://www.cnblogs.com/wsg1100/
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_SYSCALL5
,XENOAI_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_flags
和ipipe_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则切换回去*/
#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 卷 :系统编程指南
极客时间专栏-趣谈Linux操作系统
《linux内核源代码情景分析》
xenomai内核解析之双核系统调用(一)的更多相关文章
- 【原创】xenomai内核解析--双核系统调用(二)--应用如何区分xenomai/linux系统调用或服务
版权声明:本文为本文为博主原创文章,转载请注明出处.如有错误,欢迎指正. 1. 引出问题 上一篇文章xenomai内核解析--双核系统调用(一)以X86处理器为例,分析了xenomai内核调用的流程, ...
- 【原创】xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
版权声明:本文为本文为博主原创文章,转载请注明出处.如有问题,欢迎指正.博客地址:https://www.cnblogs.com/wsg1100/ 1.概述 上篇文章xenomai内核解析--实时IP ...
- 【xenomai内核解析】系列文章大纲
xenomai内核解析 本博客为本人学习linux实时操作系统框架xenomai的一些记录,主要剖析xenomai内核实现,以及与linux相关的知识.方便读者定位具体文章,现列出本博客大纲,后续会陆 ...
- xenomai内核解析---内核对象注册表—xnregistry(重要组件)
1. 概述 上篇文章xenomai内核解析--同步互斥机制(一)--优先级倒置讲到,对于所有内核对象: xnregistry:保存内核对象,提供内核对象存储和快速检索. xnsynch:资源抽象,提供 ...
- 【原创】xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(二)--实时与非实时关联(bind流程)
版权声明:本文为本文为博主原创文章,转载请注明出处.如有问题,欢迎指正.博客地址:https://www.cnblogs.com/wsg1100/ 1.概述 上篇文章介绍了实时端socket创建和配置 ...
- xenomai内核解析--双核系统调用(三)--如何为xenomai添加一个系统调用
版权声明:本文为本文为博主原创文章,转载请注明出处.如有错误,欢迎指正. @ 目录 一.添加系统调用 二.Cobalt库添加接口 三.应用使用 一.添加系统调用 下面给xenomai添加一个系统调用g ...
- xenomai内核解析之xenomai的组成结构
@ 目录 一.xenomai 3 二.xenomai3 结构 这是第二篇笔记. 一.xenomai 3 从xenomai3开始支持两种方式构建linux实时系统,分别是cobalt 和 mercury ...
- xenomai内核解析之信号signal(二)---xenomai信号处理机制
xenomai信号 上篇文章讲了linux的信号在内核的发送与处理流程,现在加入了cobalt核,Cobalt内核为xenomai线程提供了信号机制.下面一一解析xenomai内核的信号处理机制. 1 ...
- 【原创】xenomai内核解析--同步互斥机制(一)--优先级倒置
版权声明:本文为本文为博主原创文章,转载请注明出处.如有错误,欢迎指正.博客地址:https://www.cnblogs.com/wsg1100/ 目录 一.xenomai 资源管理简要 二.优先级倒 ...
随机推荐
- DBCP连接池和事物
工具类案例 public static final String DRIVER = "com.mysql.jdbc.Driver"; public static final Str ...
- MySQL常见6个考题在实际工作中的运用
题目一 MyISAM和InnoDB的区别,什么时候选择MyISAM 参考回答 InnoDB是目前MySQL主流版本(5.6.5.7.8.0)默认的存储引擎,支持事务.外键.行级锁,对于并发条件下要求数 ...
- 设计并测试Trapezium类 代码参考
#include <iostream> using namespace std; class Trapezium { private: int x1,y1,x2,y2,x3,y3,x4,y ...
- DQN(Deep Q-learning)入门教程(六)之DQN Play Flappy-bird ,MountainCar
在DQN(Deep Q-learning)入门教程(四)之Q-learning Play Flappy Bird中,我们使用q-learning算法去对Flappy Bird进行强化学习,而在这篇博客 ...
- Spring AMQP:RabbitTemplate SimpleMessageListenerContainer
一.RabbitTemplate介绍 RabbitTemplate:消息模板,在与Spring AMQP整合时,进行发送消息的关键类. 包括了可靠性投递消息方法.回调监听消息接口ConfirmCall ...
- 手写 new
/* 基于内置的 new 关键词,我们可以创建 Dog 的一个实例 zhangsan ,实例可以调用原型上的属性和方法 需求:自己实现一个 _new 方法,也可以模拟出内置 new 后的结果 */ / ...
- Java实现 LeetCode 641 设计循环双端队列(暴力)
641. 设计循环双端队列 设计实现双端队列. 你的实现需要支持以下操作: MyCircularDeque(k):构造函数,双端队列的大小为k. insertFront():将一个元素添加到双端队列头 ...
- Java实现 LeetCode 30 串联所有单词的子串
30. 串联所有单词的子串 给定一个字符串 s 和一些长度相同的单词 words.找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置. 注意子串要与 words 中的单词完全匹配, ...
- 第九届蓝桥杯JavaC组省赛真题
解题代码部分来自网友,如果有不对的地方,欢迎各位大佬评论 题目1.哪天返回 题目描述 小明被不明势力劫持.后被扔到x星站再无问津.小明得知每天都有飞船飞往地球,但需要108元的船票,而他却身无分文. ...
- Github 创建SSH密钥克隆仓库详解
1.登录Github主页,点击任意一个repository,点击Clone or download,点击Use SSH 2.点击add a new pulbic key,创建公钥 3.下面图中的Tit ...