设为首页 加入收藏

TOP

关于Linux系统如何实现fork的研究(一)
2015-02-13 18:24:19 来源: 作者: 【 】 浏览:91
Tags:关于 Linux 系统 如何 实现 fork 研究

引言


? ? fork函数是用于在linux系统中创建进程所使用,而最近看了看一个fork()调用是怎么从应用到glibc,最后到内核中实现的,这片文章就聊聊最近对这方面研究的收获吧。我们主要聊聊从glibc库进入内核,再从内核出来的情景,而从应用到glibc这部分本片文章就不详细说明了。为了方便期间,我们的硬件平台为arm,linux内核为3.18.3,glibc库版本为2.20,可从http://ftp.gnu.org/gnu/glibc/下载源码。


Glibc到kernel


? ? 我们设定硬件平台为arm,glibc库版本为2.20,因为不同的CPU体系结构中,glibc库通过系统调用进入kernel库的方法是不一样的。当glibc准备进入kernel时,流程如下?



?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内核中后,具体看此时内核是怎么操作的吧。


?


? 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? ?

首页 上一页 1 2 3 4 5 下一页 尾页 1/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇Spring 4.0 中的 WebSocket 架构 下一篇对entry-common.S和call.S的部分..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: