Linux system call的来龙去脉浅析

ARM 218浏览

Linux system call的来龙去脉
Application通常都是调用一个C library中一个library库函数,该库函数最后又会通ARM的swi assemble instruction。SWI Instruction执行后,ARM进入Supervisor Mode,之后Linux Kernel会进行一系列的处理,最后调用我们在Linux Kernel中看到那些sys_XXX system call function。我们大多数工程师都知道如何调用库函数,但对后面发生的一切却并清楚。

C Lib入口
首先我们的入手点是:我们要创建一个process:int pid = fork()。fork() GCC libc 中一个函数,我们不妨看看fork在gcc libc中的实现:(glibc-2.7nptlsysdepsunixsysvlinuxfork.c)
pid_t
__libc_fork (void)
{
  。。。。。。。(此处我们不关注,省略,详见glibc-2.7nptlsysdepsunixsysvlinuxfork.c)
#ifdef ARCH_FORK
  pid = ARCH_FORK ();
#else
# error "ARCH_FORK must be defined so that the CLONE_SETTID flag is used"
  pid = INLINE_SYSCALL (fork, 0);
#endif
。。。。。。(此处我们不关注,省略)
  return pid;
}
weak_alias (__libc_fork, __fork)
libc_hidden_def (__fork)
weak_alias (__libc_fork, fork)
ARCH_FORK的定义如下:
#define ARCH_FORK()       
  INLINE_SYSCALL (clone, 5,      
    CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, 
    NULL, NULL, NULL, &THREAD_SELF->tid)

而INLINE_SYSCALL定义在:glibc-ports-2.7sysdepsunixsysvlinuxarmsysdep.h:
#undef INLINE_SYSCALL
#define INLINE_SYSCALL(name, nr, args...)    
  ({ unsigned int _sys_result = INTERNAL_SYSCALL (name, , nr, args); 
     if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (_sys_result, ), 0)) 
       {        
  __set_errno (INTERNAL_SYSCALL_ERRNO (_sys_result, ));  
  _sys_result = (unsigned int) -1;    
       }        
     (int) _sys_result; })

而INTERNAL_SYSCALL_RAW定义在:glibc-ports-2.7sysdepsunixsysvlinuxarmeabisysdep.h:
#undef INTERNAL_SYSCALL_RAW
#define INTERNAL_SYSCALL_RAW(name, err, nr, args...)  
  ({        
       register int _a1 asm ("r0"), _nr asm ("r7");  
       LOAD_ARGS_##nr (args)     
       _nr = name;      
       asm volatile ("swi 0x0 @ syscall " #name 
       : "=r" (_a1)    
       : "r" (_nr) ASM_ARGS_##nr   
       : "memory");    
       _a1; })
#undef INTERNAL_SYSCALL
#define INTERNAL_SYSCALL(name, err, nr, args...)  
 INTERNAL_SYSCALL_RAW(SYS_ify(name), err, nr, args)

简而言之:load 该system call参数到ARM registers中,其中系统调用号存在R7 register中。之后执行指令swi。由ARM Architecture Manual中Seciton 2.6可以知道ARM系统进入Exception SWI Entry。

我们也可以通过对我们使用的GCC compiler使用的lib/libc-2.5.so用arm-linux-objdump –S –D –t 将lib/libc-2.5.so de-assembling后分析assembler code得到同样的结论:

0008f010 <__fork>:

   8f010: e92d4ff0 stmdb       sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}

   8f014: e59fa340          ldr     sl, [pc, #832]     ; 8f35c <.text+0x7accc>

   8f018: e28db020        add  fp, sp, #32         ; 0x20

   8f01c:  e24dd02c         sub  sp, sp, #44        ; 0x2c

   。。。

   8f128: e1a02003        mov  r2, r3

   8f12c:  e1a01003        mov  r1, r3

   8f130: e59f022c          ldr     r0, [pc, #556]     ; 8f364 <.text+0x7acd4>

   8f134: e3a07078        mov  r7, #120    ; 0x78

   8f138: ef000000          svc   0x00000000  
注:
svc就是swi120对应着sys_clone的系统调用号,详见:arch/arm/kernel/calls.S

   8f13c:  e3700a01        cmn r0, #4096 ; 0x1000

   8f140: e1a09000        mov  r9, r0

   。。。

 

Linux kernel对system call的处理
ArmLinux 2.6.23 exception vector table locates in entry-armv.S:
 .globl __vectors_start
__vectors_start:
 swi SYS_ERROR0
 b vector_und + stubs_offset
 ldr pc, .LCvswi + stubs_offset
 b vector_pabt + stubs_offset
 b vector_dabt + stubs_offset
 b vector_addrexcptn + stubs_offset
 b vector_irq + stubs_offset
 b vector_fiq + stubs_offset

 .globl __vectors_end
__vectors_end:
而.LCvswi 对应vector_swi(其定义在entry-common.S)。它工作就是提取参数,获得system call no,然后去arch/arm/kernel/calls.S中找到对应sys_clone()。从而创建process。System call no定义在include/asm-arm/unistd.h。
vector_swi这段assemble code对ARM assembler比较熟悉的朋友可以去自己读一读。