nsh启动应用的过程

函数调用

用户空间:

  • nsh_session()
    • nsh_parse()
      • nsh_parse_command()
        • nsh_execute()
          • nsh_fileapp()
            • posix_spawn() in 'PROXY_posix_spawn.c'

nsh一系列的函数主要用于解析命令行,得到argc、argv等参数,然后通过posix_spawn()新建任务。

在启用了KERNAL_BUILD的情况下,用户空间不能直接调用nuttx内核提供的的posix_spawn()函数,而是通过节点(PROXY)传递需要调用函数序号(SYS_posix_spawn)和参数进svc中断,在svc中断中根据函数序号找到内核中的函数并调用

用户空间的posix_spawn函数如下,调用该函数后进入svc中断

c
#  define sys_call6(nbr, parm1, parm2, parm3, parm4, parm5, parm6) \
({                                                                 \
  register uintptr_t reg6 __asm__("r6") = (uintptr_t)(parm6);      \
  register uintptr_t reg5 __asm__("r5") = (uintptr_t)(parm5);      \
  register uintptr_t reg4 __asm__("r4") = (uintptr_t)(parm4);      \
  register uintptr_t reg3 __asm__("r3") = (uintptr_t)(parm3);      \
  register uintptr_t reg2 __asm__("r2") = (uintptr_t)(parm2);      \
  register uintptr_t reg1 __asm__("r1") = (uintptr_t)(parm1);      \
  register uintptr_t reg0 __asm__("r0") = (uintptr_t)(nbr);        \
  __asm__ __volatile__                                             \
  (                                                                \
    "svc %1"                                                       \
    : "=r"(reg0)                                                   \
    : "i"(SYS_syscall), "r"(reg0), "r"(reg1), "r"(reg2),           \
      "r"(reg3), "r"(reg4), "r"(reg5), "r"(reg6)                   \
    : "memory"                                                     \
  );                                                               \
  reg0;                                                            \
})

int posix_spawn(FAR pid_t * parm1, FAR const char * parm2, FAR const posix_spawn_file_actions_t * parm3, FAR const posix_spawnattr_t * parm4, FAR char * const parm5[], FAR char * const parm6[])
{
  return (int)sys_call6((unsigned int)SYS_posix_spawn, (uintptr_t)parm1, (uintptr_t)parm2, (uintptr_t)parm3, (uintptr_t)parm4, (uintptr_t)parm5, (uintptr_t)parm6);
}

内核空间

触发svc中断时,r0寄存器中保存了需要的系统调用函数的序号,剩下若干个寄存器则保存了系统调用的参数。

出发svc中断后的工作如下:

  • arm_syscall:svc中断函数
    • 保存LR和SPSR到sys模式栈: srsdb sp!, #PSR_MODE_SYS
    • 切换到sys模式:cpsid if, #PSR_MODE_SYS
    • 寄存器压栈
    • arm_syscall(),switch进入default分支
      • 修改刚才压入栈中的寄存器
    • 切换到svc模式
  • dispatch_syscall()
    • 出栈恢复寄存器,作为syscall的参数
    • 从g_stublookup[]对应的syscall并执行
    • 出发svc中断,r0保存为SYS_syscall_return
  • arm_syscall
    • arm_syscall(),switch进入SYS_syscall_return分支
      • 修改刚才压入栈中的寄存器
    • 出栈,回到用户模式
资源收藏
求职笔记 . 面经