函数库调用与系统调用02——系统调用实现

函数库调用与系统调用02——系统调用实现

这里是指arm linux架构(Android4.4),以clone()为例
1 API有的不需要系统调用,比如Math.h里面。有的需要,比如接下来讲的pthread.h

2 在用户空间。创建一个线程,有如下是通过pthread_create()API调用,这个API最终是通过clone()这个系统调用完成
  1. #include <pthread.h>
  2. pthread_create(&tid, &attr, thr_fn, NULL);

  3. =>$android_root/bionic/libc/include/pthrea.h
  4. int pthread_create(pthread_t *thread, pthread_attr_t const * attr,
  5. void *(*start_routine)(void *), void * arg);

  6. =>$adnroid_root/bionic/libc/bionic/pthread_create.cpp
  7. int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr,
  8. void* (*start_routine)(void*), void* arg) {
  9. ...
  10. int tid = __pthread_clone(start_routine, child_stack, flags, arg);//最终调用到这里
  11. }
=>bionic/libc/arch-arm/bionic/Clone.S
这里是通过汇编来实现的。
  1. // int __pthread_clone(void* (*fn)(void*), void* child_stack, int flags, void* arg);
  2. ENTRY(__pthread_clone)
  3. # Push 'fn' and 'arg' onto 'child_stack'.
  4. stmdb r1!, {r0, r3}
  5. # The sys_clone system call only takes two arguments: 'flags' and 'child_stack'.
  6. # 'child_stack' is already in r1, but we need to move 'flags' into position.
  7. mov r0, r2
  8. # System call.
  9. mov ip, r7
  10. ldr r7, =__NR_clone //系统调用号定义在unistd.h #define __NR_clone (__NR_SYSCALL_BASE+120),即120号写入R7
  11. swi #0 //通过swi软中断指令陷入内核
  12. ...
  13. END(__pthread_clone)
关于__NR_clone的定义,在/arch/arm64/include/asm/unistd32.h 中,如下:
#define __NR_clone 120                                                                                                                                                                       
__SYSCALL(__NR_clone, sys_clone)
3 在Kernel空间
首先处理SWI指令
$kernel_root/arch/arm/kernel/entry-common.S
  1. /*=============================================================================
  2. * SWI handler
  3. *-----------------------------------------------------------------------------
  4. */
  5. .align 5
  6. ENTRY(vector_swi) //进入软件中断
  7. sub sp, sp, #S_FRAME_SIZE
  8. stmia sp, {r0 - r12} @ Calling r0 - r12
  9. ARM( add r8, sp, #S_PC )
  10. ARM( stmdb r8, {sp, lr}^ ) @ Calling sp, lr
  11. THUMB( mov r8, sp )
  12. THUMB( store_user_sp_lr r8, r10, S_SP ) @ calling sp, lr
  13. mrs r8, spsr @ called from non-FIQ mode, so ok.
  14. str lr, [sp, #S_PC] @ Save calling PC
  15. str r8, [sp, #S_PSR] @ Save CPSR
  16. str r0, [sp, #S_OLD_R0] @ Save OLD_R0
  17. zero_fp
  18. ...
  19. addne scno, r7, #__NR_SYSCALL_BASE @ put OS number in //从R7把系统调用号读出来,存放在scno里面
  20. ...
  21. adr tbl, sys_call_table @ load syscall table pointer //再根据系统调用号,查询sys_call_table这个表,找到相应的系统调用
  22. ENTRY(sys_call_table)
  23. #include "calls.S"
sys_call_table即calls.S
=>kernel_root/arch/arm/kernel/calls.S
  1. ...
  2. /* 120 */ CALL(sys_clone)//120号对应的就是sys_clone
  3. ...
=>entry-common.S
 ldrcc   pc, [tbl, scno, lsl #2]     @ call sys_* routine//这里执行系统调用。
那么在哪实现sys_clone的呢?不同的系统的调用,在不同的目录里,比如open就是在fs/下面,clone就就在kernel下面。使用宏SYSCALL_DEFINE来配合使用。
=>kernel/fork.c
  1. #ifdef __ARCH_WANT_SYS_CLONE
  2. #ifdef CONFIG_CLONE_BACKWARDS
  3. SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
  4. int __user *, parent_tidptr,
  5. int, tls_val,
  6. int __user *, child_tidptr)
  7. #elif defined(CONFIG_CLONE_BACKWARDS2)
  8. SYSCALL_DEFINE5(clone, unsigned long, newsp, unsigned long, clone_flags,
  9. int __user *, parent_tidptr,
  10. int __user *, child_tidptr,
  11. int, tls_val)
  12. #elif defined(CONFIG_CLONE_BACKWARDS3)
  13. SYSCALL_DEFINE6(clone, unsigned long, clone_flags, unsigned long, newsp,
  14. int, stack_size,
  15. int __user *, parent_tidptr,
  16. int __user *, child_tidptr,
  17. int, tls_val)
  18. #else
  19. SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
  20. int __user *, parent_tidptr,
  21. int __user *, child_tidptr,
  22. int, tls_val)
  23. #endif
  24. {
  25. return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr);
  26. }
  27. #endif
=>
最终是调用forkc.c/do_fork()的.
整个流程是这样的,通了流程,有需要有精力就可以深入细节啦。
referen
1,这是讲得比较全面。读完之后,你大致可以讲得清系统调用的过程
“系统调用就是用户民通过swi指令陷入内核,cpu由user模式进入svc模式的过程,调用的过程大致是先备份当前任务的上下文(相应的堆栈信息),然后通过swi指令的参数,来找到对应的系统调用表,执行完系统调用,把返回值写在指定的寄存器,恢复原来的现场,完成系统调用”
2,同时还讲了系统调用和普通API的区别(主要是有没有陷入内核态)
TOP