1 /* glibc最後會調用到一個INLINE_SYSCALL宏,參數以下 */ 2 INLINE_SYSCALL (clone, 5, CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, NULL, NULL, NULL, &THREAD_SELF->tid); 3 4 /* INLINE_SYSCALL的宏定義以下,能夠看出在INLINE_SYSCALL宏中又使用到了INTERNAL_SYSCALL宏,而INTERNAL_SYSCALL宏最終會調用INTERNAL_SYSCALL_RAW */ 5 #define INLINE_SYSCALL(name, nr, args...) \ 6 ({ unsigned int _sys_result = INTERNAL_SYSCALL (name, , nr, args); \ 7 if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (_sys_result, ), 0)) \ 8 { \ 9 __set_errno (INTERNAL_SYSCALL_ERRNO (_sys_result, )); \ 10 _sys_result = (unsigned int) -1; \ 11 } \ 12 (int) _sys_result; }) 13 14 /* 爲了方便你們理解,將此宏寫爲僞代碼形式 */ 15 int INLINE_SYSCALL (name, nr, args...) 16 { 17 unsigned int _sys_result = INTERNAL_SYSCALL (name, , nr, args); 18 19 if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (_sys_result, ), 0)) { 20 __set_error (INTERNAL_SYSCALL_ERRNO (_sys_result, )); 21 _sys_result = (unsigned int) -1; 22 } 23 return (int)_sys_result; 24 } 25 26 /* 這裏咱們不須要看INTERNAL_SYSCALL宏,只須要看其最終調用的INTERNAL_SYSCALL_RAW宏,須要注意的是,INTERNAL_SYSCALL調用INTERNAL_SYSCALL_RAW時,經過SYS_ify(name)宏將name轉爲了系統調用號 27 * name: 120(經過SYS_ify(name)宏已經將clone轉爲了系統調用號120) 28 * err: NULL 29 * nr: 5 30 * args[0]: CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD 31 * args[1]: NULL 32 * args[2]: NULL 33 * args[3]: NULL 34 * args[4]: &THREAD_SELF->tid 35 */ 36 # define INTERNAL_SYSCALL_RAW(name, err, nr, args...) \ 37 ({ \ 38 register int _a1 asm ("r0"), _nr asm ("r7"); \ 39 LOAD_ARGS_##nr (args) \ 40 _nr = name; \ 41 asm volatile ("swi 0x0 @ syscall " #name \ 42 : "=r" (_a1) \ 43 : "r" (_nr) ASM_ARGS_##nr \ 44 : "memory"); \ 45 _a1; }) 46 #endif
INTERNAL_SYSCALL_RAW實現的結果就是將args[0]存到了r0...args[4]存到了r4中,並將name(120)綁定到r7寄存器。而後經過swi 0x0指令進行了軟中斷。0x0是一個24位的當即數,用於軟中斷執行程序判斷執行什麼操做。當執行這條指令時,CPU會跳轉至中斷向量表的軟中斷指令處,執行該處保存的調用函數,而在函數中會根據swi後面的24位當即數(在咱們的例子中是0x0)執行不一樣操做。在這時候CPU已經處於保護模式,陷入內核中。如今進入到linux內核中後,具體看此時內核是怎麼操做的吧。linux
1 /* 源文件地址: 內核目錄/arch/arm/kernel/entry-common.S */ 2 3 ENTRY(vector_swi) 4 /* 5 * 保存現場 6 */ 7 #ifdef CONFIG_CPU_V7M 8 v7m_exception_entry 9 #else 10 sub sp, sp, #S_FRAME_SIZE 11 stmia sp, {r0 - r12} @ 將r0~r12保存到棧中 12 ARM( add r8, sp, #S_PC ) 13 ARM( stmdb r8, {sp, lr}^ ) @ Calling sp, lr 14 THUMB( mov r8, sp ) 15 THUMB( store_user_sp_lr r8, r10, S_SP ) @ calling sp, lr 16 mrs r8, spsr @ called from non-FIQ mode, so ok. 17 str lr, [sp, #S_PC] @ Save calling PC 18 str r8, [sp, #S_PSR] @ Save CPSR 19 str r0, [sp, #S_OLD_R0] @ Save OLD_R0 20 #endif 21 zero_fp 22 alignment_trap r10, ip, __cr_alignment 23 enable_irq 24 ct_user_exit 25 get_thread_info tsk 26 27 /* 28 * 如下代碼根據不一樣arm體系結構獲取系統調用號 29 */ 30 31 #if defined(CONFIG_OABI_COMPAT) 32 33 /* 34 * 若是內核配置了OABI兼容選項,會先判斷是否爲THUMB,如下爲THUMB狀況(咱們分析的時候能夠忽略這段,通常狀況是不走這一段的) 35 */ 36 #ifdef CONFIG_ARM_THUMB 37 tst r8, #PSR_T_BIT 38 movne r10, #0 @ no thumb OABI emulation 39 USER( ldreq r10, [lr, #-4] ) @ get SWI instruction 40 #else 41 USER( ldr r10, [lr, #-4] ) @ get SWI instruction 42 #endif 43 ARM_BE8(rev r10, r10) @ little endian instruction 44 45 #elif defined(CONFIG_AEABI) 46 47 /* 48 * 咱們主要看這裏,EABI將系統調用號保存在r7中 49 */ 50 #elif defined(CONFIG_ARM_THUMB) 51 /* 先判斷是否爲THUMB模式 */ 52 tst r8, #PSR_T_BIT 53 addne scno, r7, #__NR_SYSCALL_BASE 54 USER( ldreq scno, [lr, #-4] ) 55 56 #else 57 /* EABI模式 */ 58 USER( ldr scno, [lr, #-4] ) @ 獲取系統調用號 59 #endif 60 61 adr tbl, sys_call_table @ tbl爲r8,這裏是將sys_call_table的地址(相對於此指令的偏移量)存入r8 62 63 #if defined(CONFIG_OABI_COMPAT) 64 /* 65 * 在EABI體系中,若是swi跟着的當即數爲0,這段代碼不作處理,而若是是old abi體系,則根據系統調用號調用old abi體系的系統調用表(sys_oabi_call_table) 66 * 其實說白了,在EABI體系中,系統調用時使用swi 0x0進行軟中斷,r7寄存器保存系統調用號 67 * 而old abi體系中,是經過swi (系統調用號|magic)進行調用的 68 */ 69 bics r10, r10, #0xff000000 70 eorne scno, r10, #__NR_OABI_SYSCALL_BASE 71 ldrne tbl, =sys_oabi_call_table 72 #elif !defined(CONFIG_AEABI) 73 bic scno, scno, #0xff000000 74 eor scno, scno, #__NR_SYSCALL_BASE 75 #endif 76 77 local_restart: 78 ldr r10, [tsk, #TI_FLAGS] @ 檢查系統調用跟蹤 79 stmdb {r4, r5} @ 將第5和第6個參數壓入棧 80 81 tst r10, #_TIF_SYSCALL_WORK @ 判斷是否在跟蹤系統調用 82 bne __sys_trace 83 84 cmp scno, #NR_syscalls @ 檢測系統調用號是否在範圍內,NR_syscalls保存系統調用總數 85 adr lr, BSYM(ret_fast_syscall) @ 將返回地址保存到lr寄存器中,lr寄存器是用於函數返回的。 86 ldrcc pc, [tbl, scno, lsl #2] @ 調用相應系統調用例程,tbl(r8)保存着系統調用表(sys_call_table)地址,scno(r7)保存着系統調用號120,這裏就轉到相應的處理例程上了。 87 88 add r1, sp, #S_OFF 89 2: cmp scno, #(__ARM_NR_BASE - __NR_SYSCALL_BASE) 90 eor r0, scno, #__NR_SYSCALL_BASE @ put OS number back 91 bcs arm_syscall 92 mov why, #0 @ no longer a real syscall 93 b sys_ni_syscall @ not private func 94 95 #if defined(CONFIG_OABI_COMPAT) || !defined(CONFIG_AEABI) 96 /* 97 * We failed to handle a fault trying to access the page 98 * containing the swi instruction, but we're not really in a 99 * position to return -EFAULT. Instead, return back to the 100 * instruction and re-enter the user fault handling path trying 101 * to page it in. This will likely result in sending SEGV to the 102 * current task. 103 */ 104 9001: 105 sub lr, lr, #4 106 str lr, [sp, #S_PC] 107 b ret_fast_syscall 108 #endif 109 ENDPROC(vector_swi) @ 返回
好的,終於跳轉到了系統調用表,如今咱們看看系統調用表是怎麼樣的一個形式dom
1 /* 文件地址: linux內核目錄/arch/arm/kernel/calls.S */ 2 3 /* 0 */ CALL(sys_restart_syscall) 4 CALL(sys_exit) 5 CALL(sys_fork) 6 CALL(sys_read) 7 CALL(sys_write) 8 /* 5 */ CALL(sys_open) 9 CALL(sys_close) 10 CALL(sys_ni_syscall) /* was sys_waitpid */ 11 CALL(sys_creat) 12 CALL(sys_link) 13 /* 10 */ CALL(sys_unlink) 14 CALL(sys_execve) 15 CALL(sys_chdir) 16 CALL(OBSOLETE(sys_time)) /* used by libc4 */ 17 CALL(sys_mknod) 18 /* 15 */ CALL(sys_chmod) 19 CALL(sys_lchown16) 20 CALL(sys_ni_syscall) /* was sys_break */ 21 CALL(sys_ni_syscall) /* was sys_stat */ 22 CALL(sys_lseek) 23 /* 20 */ CALL(sys_getpid) 24 CALL(sys_mount) 25 CALL(OBSOLETE(sys_oldumount)) /* used by libc4 */ 26 CALL(sys_setuid16) 27 CALL(sys_getuid16) 28 /* 25 */ CALL(OBSOLETE(sys_stime)) 29 CALL(sys_ptrace) 30 CALL(OBSOLETE(sys_alarm)) /* used by libc4 */ 31 CALL(sys_ni_syscall) /* was sys_fstat */ 32 CALL(sys_pause) 33 34 ...................... 35 ...................... 36 37 /* 120 */ CALL(sys_clone) /* 120在此,以前傳進來的系統調用號120進入內核後會到這 */ 38 CALL(sys_setdomainname) 39 CALL(sys_newuname) 40 CALL(sys_ni_syscall) /* modify_ldt */ 41 CALL(sys_adjtimex) 42 /* 125 */ CALL(sys_mprotect) 43 CALL(sys_sigprocmask) 44 CALL(sys_ni_syscall) /* was sys_create_module */ 45 CALL(sys_init_module) 46 CALL(sys_delete_module) 47 48 ...................... 49 ...................... 50 51 /* 375 */ CALL(sys_setns) 52 CALL(sys_process_vm_readv) 53 CALL(sys_process_vm_writev) 54 CALL(sys_kcmp) 55 CALL(sys_finit_module) 56 /* 380 */ CALL(sys_sched_setattr) 57 CALL(sys_sched_getattr) 58 CALL(sys_renameat2) 59 CALL(sys_seccomp) 60 CALL(sys_getrandom) 61 /* 385 */ CALL(sys_memfd_create) 62 CALL(sys_bpf) 63 #ifndef syscalls_counted 64 .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls 65 #define syscalls_counted 66 #endif 67 .rept syscalls_padding 68 CALL(sys_ni_syscall) 69 .endr
CALL爲一個宏,而咱們使用的那一行CALL(sys_clone)配合ldrcc pc,[tbl,scno,lsl #2]使用的結果就是把sys_clone的地址放入pc寄存器。具體咱們仔細分析一下,首先先看看CALL宏展開,而後把CALL代入ldrcc,結果就很清晰了函數
1 /* CALL(x)宏展開 */ 2 #define CALL(x) .equ NR_syscalls,NR_syscalls+1 3 #include "calls.S" 4 5 .ifne NR_syscalls - __NR_syscalls 6 .error "__NR_syscalls is not equal to the size of the syscall table" 7 .endif 8 9 /* 主要是後面這一段, 10 * 上面一段主要用於統計系統調用數量,並將數量保存到NR_syscalls中,具體實現說明能夠參考http://www.tuicool.com/articles/QFj6zq 11 */ 12 13 #undef CALL 14 /* 其實就是生成一個數爲x,至關於.long sys_clone,由於sys_clone是函數名,因此.long生成的是sys_clone函數名對應的地址 */ 15 #define CALL(x) .long x 16 17 #ifdef CONFIG_FUNCTION_TRACER 18 19 20 /* 配合ldrcc一塊兒看,原來ldrcc是這樣 */ 21 ldrcc pc, [tbl, scno, lsl #2] 22 23 /* 把CALL(x)代入ldrcc,最後是這樣 */ 24 ldrcc pc, sys_clone(函數地址)
清楚的看出來,ldrcc最後是將sys_clone的函數地址存入了pc寄存器,而sys_clone函數內核是怎麼定義的呢,以下學習
1 /* 文件地址: linux內核目錄/kernel/Fork.c */ 2 3 /* 如下代碼根據不一樣的內核配置定義了不一樣的clone函數 4 * 其最終都調用的do_fork函數,咱們先看看SYSCALL_DEFINE是怎麼實現的吧,實如今此代碼片斷後面 5 */ 6 #ifdef __ARCH_WANT_SYS_CLONE 7 #ifdef CONFIG_CLONE_BACKWARDS 8 SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, 9 int __user *, parent_tidptr, 10 int, tls_val, 11 int __user *, child_tidptr) 12 #elif defined(CONFIG_CLONE_BACKWARDS2) 13 SYSCALL_DEFINE5(clone, unsigned long, newsp, unsigned long, clone_flags, 14 int __user *, parent_tidptr, 15 int __user *, child_tidptr, 16 int, tls_val) 17 #elif defined(CONFIG_CLONE_BACKWARDS3) 18 SYSCALL_DEFINE6(clone, unsigned long, clone_flags, unsigned long, newsp, 19 int, stack_size, 20 int __user *, parent_tidptr, 21 int __user *, child_tidptr, 22 int, tls_val) 23 #else 24 SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, 25 int __user *, parent_tidptr, 26 int __user *, child_tidptr, 27 int, tls_val) 28 #endif 29 { 30 return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr); 31 } 32 33 34 /************************************************ 35 * 我是代碼分界線 36 ************************************************/ 37 38 /* 文件地址: linux內核目錄/include/linux.h */ 39 40 #define SYSCALL_DEFINE0(sname) \ 41 SYSCALL_METADATA(_##sname, 0); \ 42 asmlinkage long sys_##sname(void) 43 44 #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) 45 #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__) 46 #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) 47 #define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__) 48 #define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__) 49 #define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) 50 51 #define SYSCALL_DEFINEx(x, sname, ...) \ 52 SYSCALL_METADATA(sname, x, __VA_ARGS__) \ 53 __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) 54 55 #define __PROTECT(...) asmlinkage_protect(__VA_ARGS__) 56 #define __SYSCALL_DEFINEx
能夠看出系統調用是使用SYSCALL_DEFINEx進行定義的,以咱們的例子,實際上最後clone函數被定義爲ui
1 /* 展開前 */ 2 3 SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, 4 int __user *, parent_tidptr, 5 int __user *, child_tidptr, 6 int, tls_val) 7 #endif 8 { 9 /* 應用層默認fork參數(CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, NULL, NULL, NULL, &THREAD_SELF->tid) */ 10 return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr); 11 } 12 13 /* 展開後 */ 14 15 asmlinkage long sys_clone (unsigned long clone_flags, unsigned long newsp, int __user * parent_tidptr, int __user * child_tidptr, int tls_val) 16 { 17 return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr); 18 }
終於看到最後系統會調用do_fork函數進行操做,接下來咱們看看do_fork函數spa
1 /* 應用層的fork最後會經過sys_clone系統調用調用到此函數 */ 2 /* 應用層默認fork參數(CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, NULL, NULL, NULL, &THREAD_SELF->tid) 3 * clone_flags: CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD 4 * stack_start: NULL 5 * stack_size: NULL 6 * parent_tidptr: NULL 7 * child_tidptr: &THREAD_SELF->tid 8 * pid: NULL 9 */ 10 long do_fork(unsigned long clone_flags, 11 unsigned long stack_start, 12 unsigned long stack_size, 13 int __user *parent_tidptr, 14 int __user *child_tidptr) 15 { 16 struct task_struct *p; 17 int trace = 0; 18 long nr; 19 20 /* 判斷是否進行跟蹤 */ 21 if (!(clone_flags & CLONE_UNTRACED)) { 22 if (clone_flags & CLONE_VFORK) 23 trace = PTRACE_EVENT_VFORK; 24 else if ((clone_flags & CSIGNAL) != SIGCHLD) 25 trace = PTRACE_EVENT_CLONE; 26 else 27 trace = PTRACE_EVENT_FORK; 28 29 if (likely(!ptrace_event_enabled(current, trace))) 30 trace = 0; 31 } 32 33 /* 調用copy_process進行初始化,返回初始化好的struct task_struct結構體,當咱們調用fork時返回兩次的緣由也是在這個函數當中,下回分析 */ 34 p = copy_process(clone_flags, stack_start, stack_size, 35 child_tidptr, NULL, trace); 36 37 38 if (!IS_ERR(p)) { 39 /* 建立成功 */ 40 struct completion vfork; 41 struct pid *pid; 42 43 trace_sched_process_fork(current, p); 44 45 /* 獲取子進程PID */ 46 pid = get_task_pid(p, PIDTYPE_PID); 47 /* 返回子進程pid所屬的命名空間所看到的局部PID */ 48 nr = pid_vnr(pid); 49 50 if (clone_flags & CLONE_PARENT_SETTID) 51 put_user(nr, parent_tidptr); 52 53 if (clone_flags & CLONE_VFORK) { 54 p->vfork_done = &vfork; 55 init_completion(&vfork); 56 get_task_struct(p); 57 } 58 59 /* 將新進程加入到CPU的運行隊列中 */ 60 wake_up_new_task(p); 61 62 /* 跟蹤纔會用到 */ 63 if (unlikely(trace)) 64 ptrace_event_pid(trace, pid); 65 66 /* 若是是vfork調用,則在此等待vfork的進程結束 */ 67 if (clone_flags & CLONE_VFORK) { 68 if (!wait_for_vfork_done(p, &vfork)) 69 ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid); 70 } 71 72 put_pid(pid); 73 } else { 74 /* 建立失敗 */ 75 nr = PTR_ERR(p); 76 } 77 /* 返回新進程PID(新進程在這會返回0) */ 78 return nr; 79 }
在do_fork函數中,首先會根據clone_flags判斷是否對父進程進行了跟蹤(調試使用),若是進行了函數跟蹤(還須要判斷是否對子進程進行跟蹤),以後調用copy_process(do_fork的核心函數,以後的文章會對它進行分析),在copy_process中會對子進程的許多結構體和參數進行初始化(同時在fork正常狀況中爲何會返回兩次也是在此函數中實現的),do_fork最後就判斷是不是經過vfork建立,若是是vfork建立,則會使父進程阻塞直到子進程結束釋放所佔內存空間後才繼續執行,最後do_fork子進程pid。
調試