【问题标题】:How does a system call travels from user space to kernel space and back to user space?系统调用如何从用户空间传输到内核空间并返回到用户空间?
【发布时间】:2014-08-02 07:35:15
【问题描述】:

我研究了一些文章,其中我得到的信息是,像 open() 这样的系统调用调用 glibc 中的包装函数,然后引发一个陷阱,将上下文从用户空间切换到内核空间,然后 cpu 寄存器用于在内核空间调用系统调用参数/参数。

但我仍然想我缺少系统调用调用的分步过程或详细顺序。如果人们可以提供考虑 ARM arch 作为参考的步骤,那就太好了。提前致谢。

【问题讨论】:

  • “一个陷阱出现了”。这意味着“跳转到预定义的地址并将模式切换到内核”。差不多就是这样。您还需要什么详细信息?哪个寄存器翻转哪个位?
  • 陷阱处理程序是由一个标签的 jmp 指令组成(这意味着陷阱处理程序是一个汇编代码)还是它是一个 C 代码并且它所在的确切位置?还有谁确切调用了陷阱处理程序和什么时候?
  • 出于安全原因,有一个特殊的陷阱指令可以同时进行跳转和切换。否则无法切换到内核模式。没有标签,跳转地址是硬连线在CPU中的。
  • 在 ARM 上称为软件中断。你知道中断是如何工作的吗? syscall 类似,但特殊汇编指令 触发中断而不是硬件。 特殊汇编器glibc中的一部分。在this questions 中有两个用户空间调用exit() 的例子。一个寄存器是一个函数号,一个跳转表索引,用于获取内核代码。 C Syscalls

标签: linux linux-kernel linux-device-driver embedded-linux


【解决方案1】:

系统调用的处理分为两部分。

  1. 从系统调用开始到进行 处理器异常进入 previllege 模式。 对于 ARM,此模式将是 SVC(主管)模式。 这部分涉及用正确的系统调用号填充通用寄存器,要在系统调用中传递的参数, 并将处理器的状态触发为特权模式。
  2. 第二部分(比第一部分复杂一点)涉及在特权模式下处理系统调用,并从特权模式返回。

2a。在 ARM 的异常表中,控件由异常向量(在本例中为 SVC)引导到异常的公共处理程序。 以下是 SVC 的异常向量:

[文件:arch\arm\kernel\entry-armv.S]

W(ldr)  pc, __vectors_start + 0x1000

以下是异常处理程序的入口:

[文件:arch\arm\kernel\entry-armv.S]

vector_\name:
    .if \correction
    sub lr, lr, #\correction
    .endif

2b。在这个公共处理程序结束时,r0,lr 和 spsr 分别保存在堆栈上的 [SP]、[SP+4] 和 [SP+8] 地址。

[文件:arch\arm\kernel\entry-armv.S]

stmia   sp, {r0, lr}        @ save r0, lr
mrs lr, spsr
str lr, [sp, #8]        @ save spsr

2c。然后,通用处理程序将控制器转移/分支到 SVC 特定处理程序:

[文件:arch\arm\kernel\entry-armv.S]

mrs lr, spsr 
...
and lr, lr, #0x0f
...
 ARM(   ldr lr, [pc, lr, lsl #2]    )
movs    pc, lr          @ branch to handler in SVC mode
...
.word   vector_swi

在公共处理程序结束时,控制器被转移到SVC处理程序vector_swi。

2d。现在让我们看看如何在 SVC 处理程序中处理 syscall。 将 SP 按以下顺序在新的 FRAME 处推进后,调用者的上下文保存如下:

[文件:arch\arm\kernel\entry-common.S]

ENTRY(vector_swi)
...

(r0-r12,sp,lr)_usr_mode, lr_exp(本例中为exp=svc)

此上下文将用于通过存储 r0-r12,sp,lr,pc 通过此上下文的值从 SVC 模式返回到用户模式。

sub sp, sp, #S_FRAME_SIZE
stmia   sp, {r0 - r12}          @ Calling r0 - r12
ARM(    add r8, sp, #S_PC       )
ARM(    stmdb   r8, {sp, lr}^       )   @ Calling sp, lr
...
mrs r8, spsr            @ called from non-FIQ mode, so ok.
str lr, [sp, #S_PC]         @ Save calling PC

2e。系统调用号根据过程调用标准获取,最终存储在scno(r7 register)中。

[文件:arch\arm\kernel\entry-common.S]

elif defined(CONFIG_AEABI)

    /*
     * Pure EABI user space always put syscall number into scno (r7).
     */
#elif defined(CONFIG_ARM_THUMB)
    /* Legacy ABI only, possibly thumb mode. */
    tst r8, #PSR_T_BIT          @ this is SPSR from save_user_regs
    addne   scno, r7, #__NR_SYSCALL_BASE    @ put OS number in
    ...

2f。然后,sys_call_table的地址存储在tbl register(r8)中。

[文件:arch\arm\kernel\entry-common.S]

adr tbl, sys_call_table     @ load syscall table pointer

sys_call_table 包含根据其编号的系统调用列表。 文件 call.S 包含列表。

[文件:arch\arm\kernel\entry-common.S]

ENTRY(sys_call_table)
#include "calls.S"
#undef ABI
...

2g。然后,正确的系统调用处理程序将通过分支到具有系统调用编号偏移量的 sys_call_table 来参与。 重发地址设置在标签“ret_fast_syscall”处。

[文件:arch\arm\kernel\entry-common.S]

adr lr, BSYM(ret_fast_syscall)  @ return address
    ldrcc   pc, [tbl, scno, lsl #2]     @ call sys_* routine
    ...

2 小时。然后,以这样一种方式恢复用户模式寄存器,即 (r0-r12,sp,lr,pc)_usr_mode 从步骤 2d 中存储的值中存储。 这是在宏“restore_user_regs”中完成的。

[文件:arch\arm\kernel\entry-common.S]

ret_fast_syscall:
...
restore_user_regs fast = 1, offset = S_OFF
...

[文件:arch\arm\kernel\entry-header.S]

macro   restore_user_regs, fast = 0, offset = 0
    mov r2, sp
    ...

    .if \fast
    ldmdb   sp, {r1 - r12}          @ get calling r1 - r12
    .else
    ldmdb   sp, {r0 - r12}          @ get calling r0 - r12
    .endif
    add sp, sp, #S_FRAME_SIZE - S_SP
    movs    pc, lr              @ return & move spsr_svc into cpsr
    .endm

【讨论】:

    【解决方案2】:

    软件中断异常用于在 ARM 的情况下调用系统调用。它将执行地址存储在物理地址0x08的函数。

    Syscall 包装库函数登陆 SYSCALL 的体系结构特定实现(检查 libc 源代码的sysdeps/unix 目录)。 在我们的例子中,来自sysdeps/unix/sysv/linux/arm/syscall.S 文件的系统调用将被执行。在这个函数中,它将系统调用号存储在R7R0-R6 用于向系统调用发送参数。

    示例汇编代码:

         mov  r7, #SYSCALL NO
         mov  r0, #ARG1
         mov  r1, #ARG2
         swi 0x0
    

    当产生软件异常时,会调用vector_swi() <arch/arm/kernel/entry-common.S>。此函数从 R7 获取系统调用号, 从sys_call_table中查找并执行注册的函数地址。

    检查以下内核文件以了解实现细节:

    1. include/linux/syscalls.h
    2. arch/arm/include/asm/unistd.h
    3. arch/arm/kernel/calls.S
    4. arch/arm/kernel/entry_common.S
    5. arch/arm/kernel/sys_arm.c

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-22
      • 1970-01-01
      • 2012-06-21
      • 2016-06-26
      • 2016-07-19
      • 2012-06-23
      相关资源
      最近更新 更多