0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

雙核系統(tǒng)調(diào)用(ipipe)

Linux閱碼場(chǎng) ? 來(lái)源:Linux閱碼場(chǎng) ? 作者:順剛 ? 2022-05-06 11:11 ? 次閱讀

雙核系統(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)的?

e3b36ab8-ccd0-11ec-bce3-dac502259ad0.png

為什么需要系統(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)題:

  1. 雙核共存時(shí),如何區(qū)分應(yīng)用發(fā)起的系統(tǒng)調(diào)用是xenomai內(nèi)核調(diào)用還是linux內(nèi)核調(diào)用?

  2. 一個(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ò))。

e3cd33d0-ccd0-11ec-bce3-dac502259ad0.png

相關(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ù):

#define XENOMAI_DO_SYSCALL(nr, op, args...)      ({                  unsigned __resultvar;            asm volatile (                LOADARGS_##nr              "movl %1, %%eax
	"            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)#defineXENOMAI_SYSCALL5(op,a1,a2,a3,a4,a5)XENOMAI_DO_SYSCALL(5,op,a1,a2,a3,a4,a5)

每個(gè)宏中,內(nèi)嵌另一個(gè)宏DOSYSCALL,即實(shí)現(xiàn)系統(tǒng)調(diào)用的int指令:int$0x80

#defineDOSYSCALL"int$0x80
	"

系統(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_flagsipipe_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 */#ifdef CONFIG_IPIPE  unsigned long    ipipe_flags;  struct ipipe_threadinfo ipipe_data;#endif};

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));  .......
  __xn_status_return(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ù)的寄存器也變了。e3f3a1f0-ccd0-11ec-bce3-dac502259ad0.png

#define DO_SYSCALL(name, nr, args...)      ({                unsigned long __resultvar;        LOAD_ARGS_##nr(args)          LOAD_REGS_##nr            asm volatile (              "syscall
	"            : "=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)

這里將系統(tǒng)調(diào)用號(hào)使用 __xn_syscode(op)處理了一下,把最高位置1,表示Cobalt系統(tǒng)調(diào)用,然后使用syscall 指令。

#define __COBALT_SYSCALL_BIT  0x10000000#define__xn_syscode(__nr)(__COBALT_SYSCALL_BIT|(__nr))

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_SYSRET64END(entry_SYSCALL_64)

這里先保存了很多寄存器到 pt_regs 結(jié)構(gòu)里面,例如用戶態(tài)的代碼段、數(shù)據(jù)段、保存參數(shù)的寄存器.

e40ad8c0-ccd0-11ec-bce3-dac502259ad0.png

然后調(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中:

#define sc_cobalt_bind        0#define sc_cobalt_thread_create      1#define sc_cobalt_thread_getpid      2  ......#definesc_cobalt_extend96

bind()函數(shù)在內(nèi)核代碼中對(duì)應(yīng)的聲明和實(shí)現(xiàn)為:


/*聲明*/#define COBALT_SYSCALL_DECL(__name, __args)    long CoBaLt_ ## __name __argsstatic COBALT_SYSCALL_DECL(bind, lostage,          (struct cobalt_bindreq __user *u_breq));/*實(shí)現(xiàn)*/#define COBALT_SYSCALL(__name, __mode, __args)    long CoBaLt_ ## __name __argsstatic 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)用的模式:

#define __COBALT_CALL_ENTRIES __COBALT_CALL_ENTRY(bind)#define__COBALT_CALL_MODES__COBALT_MODE(lostage)

實(shí)時(shí)系統(tǒng)調(diào)用表 cobalt_syscalls[]定義在文件 kernelcobaltposixsyscall.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};

_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[] = {  [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中所有的系統(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)核源代碼情景分析》

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 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)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    全志T113異構(gòu)處理器的使用基于Tina Linux5.0——異構(gòu)通信驗(yàn)證

    6、通信驗(yàn)證 6.1、C906小創(chuàng)建通訊節(jié)點(diǎn) 在C906小串口終端建立兩個(gè)通訊節(jié)點(diǎn)用于監(jiān)聽(tīng)數(shù)據(jù),輸入eptdev_bind test 2 cpu0 >eptdev_bin
    發(fā)表于 11-20 09:47

    Vivado中FFT IP的使用教程

    本文介紹了Vidado中FFT IP的使用,具體內(nèi)容為:調(diào)用IP>>配置界面介紹>>IP端口介紹>>MATLAB生成測(cè)試數(shù)據(jù)>>測(cè)試verilogHDL>>TestBench仿真
    的頭像 發(fā)表于 11-06 09:51 ?999次閱讀
    Vivado中FFT IP<b class='flag-5'>核</b>的使用教程

    cpu和單核cpu的區(qū)別

    CPU與單核CPU在多個(gè)方面存在顯著差異,這些差異主要體現(xiàn)在處理能力、性能、運(yùn)行效率、功耗以及適用場(chǎng)景等方面。 一、概念與結(jié)構(gòu) CPU :指在一個(gè)處理器上集成兩個(gè)運(yùn)算核心,通過(guò)
    的頭像 發(fā)表于 09-24 16:17 ?3103次閱讀

    dsp和單核dsp的區(qū)別

    DSP(Digital Signal Processor,數(shù)字信號(hào)處理器)與單核DSP在多個(gè)方面存在顯著差異,這些差異主要體現(xiàn)在處理能力、任務(wù)分配、資源利用以及適用場(chǎng)景等方面。 一、處理能力
    的頭像 發(fā)表于 09-24 16:14 ?844次閱讀

    TMS320F2837xD微控制器數(shù)據(jù)表

    電子發(fā)燒友網(wǎng)站提供《TMS320F2837xD微控制器數(shù)據(jù)表.pdf》資料免費(fèi)下載
    發(fā)表于 08-01 12:59 ?0次下載
    TMS320F2837xD<b class='flag-5'>雙</b><b class='flag-5'>核</b>微控制器數(shù)據(jù)表

    OMAP5910處理器數(shù)據(jù)表

    電子發(fā)燒友網(wǎng)站提供《OMAP5910處理器數(shù)據(jù)表.pdf》資料免費(fèi)下載
    發(fā)表于 08-01 11:48 ?0次下載
    OMAP5910<b class='flag-5'>雙</b><b class='flag-5'>核</b>處理器數(shù)據(jù)表

    使用STM32CUBEMX生成 H745代碼,利用cubeide debug時(shí)發(fā)現(xiàn)M7阻塞進(jìn)入error_handler是為什么?

    使用STM32CUBEMX 生成 H745代碼,利用cubeide debug時(shí)發(fā)現(xiàn)M7因?yàn)橐韵麓a阻塞進(jìn)入error_handler, 請(qǐng)問(wèn)是為什么? /* USER CODE BEGIN
    發(fā)表于 05-20 07:16

    關(guān)于FPGA IP

    對(duì)于深入學(xué)習(xí)使用FPGA的小伙伴們,特別是一些復(fù)雜的、大規(guī)模的設(shè)計(jì)應(yīng)用,適宜的IP核對(duì)開(kāi)發(fā)能起到事半功倍的作用。IP的概念與我們sdk里庫(kù)的概念相似。IP即電路功能模塊,用戶可以直接調(diào)用這些模塊
    發(fā)表于 04-29 21:01

    STM32H745ZGTx芯片使用內(nèi)部FLASH,掛載FATFS為什么打不開(kāi)文件?

    STM32H745ZGTx芯片使用內(nèi)部FLASH,掛載FATFS為何打不開(kāi)文件?
    發(fā)表于 04-07 07:11

    STM32H747如何用JLINK調(diào)試?

    1.我之前是在STM32H747的官方開(kāi)發(fā)板discover上進(jìn)行調(diào)試,板子上自帶了STlink調(diào)試器,按照官方文檔配置可以進(jìn)行調(diào)試 2.目前自己設(shè)計(jì)的板子上是調(diào)試接口是SWD接口,手上只有
    發(fā)表于 03-28 08:58

    stm32H747的IAP升級(jí)要怎么做?

    H747我看有2個(gè)Hex文件,生成的Bin文件也有2個(gè)。Bootloader要怎么處理呢?也是2個(gè)Bootloader程序嗎?那我要IAP升級(jí)程序的話要怎么處理了?
    發(fā)表于 03-28 08:50

    關(guān)于核問(wèn)題的求證

    突擊了解了一下hpm芯片,感覺(jué)挺好的,單核就達(dá)到了CORTEX-M7的速度。關(guān)于(主要是hpm6280)有些問(wèn)題想求證一下。 1-共享內(nèi)存交換數(shù)據(jù),信箱做通知,互斥,信號(hào)量用,二者結(jié)合起到間數(shù)
    發(fā)表于 02-06 21:14

    PSoC架構(gòu)中都可以訪問(wèn)全部外設(shè)嗎?

    你好!如標(biāo)題:PSoC 架構(gòu)中兩個(gè)內(nèi)核對(duì)芯片的全部外設(shè)都有直接訪問(wèn)能力嘛?如果都可以直接訪問(wèn),那IPC模塊的主要應(yīng)用場(chǎng)景是哪些呢?
    發(fā)表于 02-02 11:44

    Linux內(nèi)核中信號(hào)相關(guān)的系統(tǒng)調(diào)用

    正如我們所知,運(yùn)行在用戶態(tài)下的程序可以發(fā)送和接收信號(hào)。這意味著必須定義一組系統(tǒng)調(diào)用來(lái)允許這類操作。不幸的是,由于歷史原因,有些系統(tǒng)調(diào)用可能功能相同。 因此,其中一些
    的頭像 發(fā)表于 01-20 09:34 ?759次閱讀

    請(qǐng)問(wèn)ADSP BF609的運(yùn)行同時(shí)跑兩個(gè)系統(tǒng),需要如何設(shè)置,要注意哪些問(wèn)題?

    請(qǐng)問(wèn)ADSP BF609的運(yùn)行同時(shí)跑兩個(gè)系統(tǒng),需要如何設(shè)置,要注意哪些問(wèn)題?另外對(duì)另一個(gè)運(yùn)行的實(shí)時(shí)性處理要求的高一些,尤其是系統(tǒng)啟動(dòng)時(shí)
    發(fā)表于 01-12 08:08