`
womendu
  • 浏览: 1481523 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

系统调用-进程创建函数glibc部分2

阅读更多

* 本文的系统调用的分析基于ARM体系结构
* CrossTool:gcc-3.4.5,libc库:glibc-2.3.6
* 本文的内容来自glibc源代码

上一篇文章中讲述了用户进程如何使用fork、vfork、clone、pthread_create函数,本文将基于glibc-2.3.6源码来跟踪
这些系统调用是怎么使调用用户进程从用户态切换到内核态的。

系统调用是操作系统提供的服务,用户程序通过各种系统调用,来引用内核提供的各种服务,系统调用的执行让
用户程序陷入内核,该陷入动作由swi软中断完成。当用户态的进程调用一个系统调用时,CPU通过软中断切换到
SVC模式并开始执行一个内核函数,完成用户态进程需要完成的工作。

fork():
path: glibc-2.3.6/nptl/sysdeps/unix/sysv/linux/pt-fork.c
#include <unistd.h> // include __libc_fork()函数声明

pid_t __fork (void)
{
return __libc_fork ();
}
strong_alias (__fork, fork) // ./include/libc-symbols.h
// 实际上这里告诉编译器fork这个名字就代表着__fork这个名字,如果这里没有
// __fork()的定义,编译器就会报错。
// 类似地,存在weak_alias(XXX, xxx),也是取别名,但是编译器在XXX没有定义
// 的时候,不会报错。
// 可以参考强符号、弱符号,强引用和弱引用来理解它

vfork():
path: glibc-2.3.6/sysdeps/generic/vfork.c
#include <errno.h>
#include <unistd.h>
/* If we don't have vfork, fork is close enough. */

__pid_t __vfork (void)
{
return __fork ();
}
libc_hidden_def (__vfork)

weak_alias (__vfork, vfork)
可以看出调用vfork是__vfork的别名,并且实际上是调用了__fork函数来实现__vfork函数的

clone():
path: glibc-2.3.6/sysdeps/unix/sysv/linux/arm/clone.S
该文件中是用汇编代码实现了__clone函数,存在:
weak_alias (__clone, clone)

__clone()函数声明如下:
extern int __clone (int (*__fn) (void *__arg), void *__child_stack, int __flags, void *__arg, ...);
path: glibc-2.3.6/include/sched.h

pthread_create():
该函数的源码位置比较特殊,位于目录linuxthread目录下。
path:linuxthread/pthread.c

...
__pthread_create_2_1()
__pthread_create_2_0(),最终还是调用__pthread_create_2_1()来实现的。

*********************
__libc_fork():
path: glibc-2.3.6/nptl/sysdeps/unix/sysv/linux/fork.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
...
这里pid = INLINE_SYSCALL (fork, 0);语句会得到执行,下面是3个重要的头文件:
sysdeps/unix/sysv/linux/arm/sysdep.h
sysdeps/unix/arm/sysdep.h
sysdeps/unix/sysdep.h
sysdeps/arm/sysdep.h
INLINE_SYSCALL这个宏定义于文件sysdeps/unix/sysv/linux/arm/sysdep.h中,该处的宏定义很复杂:
...
#define SYS_ify(syscall_name) (__NR_##syscall_name) // 获取中断号
// 中断号定义在:arm平台文件系统(制作文件系统时,需要拷贝交叉工具链中的头文件和库)中include/asm/unistd.h
/***
#if defined(__thumb__) || defined(__ARM_EABI__)
#define __NR_SYSCALL_BASE 0
#else
#define __NR_SYSCALL_BASE 0x900000
#endif

/*

* This file contains the system call numbers.

*/
#define __NR_restart_syscall (__NR_SYSCALL_BASE+ 0)
#define __NR_exit (__NR_SYSCALL_BASE+ 1)
#define __NR_fork (__NR_SYSCALL_BASE+ 2)
#define __NR_read (__NR_SYSCALL_BASE+ 3)
#define __NR_write (__NR_SYSCALL_BASE+ 4)
#define __NR_open (__NR_SYSCALL_BASE+ 5)
...
...
#define __NR_vfork (__NR_SYSCALL_BASE+ 190)
...
#define __NR_clone (__NR_SYSCALL_BASE+ 120)
...
...
***/
...
#undef INLINE_SYSCALL
#define INLINE_SYSCALL(name, nr, args...) \
({ unsigned int _sys_result = INTERNAL_SYSCALL (name, , nr, args); \ // note1
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; })

#undef INTERNAL_SYSCALL_DECL
#define INTERNAL_SYSCALL_DECL(err) do { } while (0)

/**
讲解AT&T汇编与GCC内嵌汇编语法网址,虽然该处是基于pc机的讲解,但是和arm体系结构还是很相似的:
http://blog.chinaunix.net/u/21862/showart_141532.html

下面摘录关键部分
带有C/C++表达式的内联汇编格式为:
__asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify);

操作约束符号:
r I,O 表示使用一个通用寄存器 寄存器约束
m I,O 表示使用系统所支持的任何一种内存方式,不需要借助寄存器〔关键字m〕 内存约束
i I 表示输入表达式是一个立即数(整数),不需要借助任何寄存器 立即数约束
F I 表示输入表达式是一个立即数(浮点数),不需要借助任何寄存器 立即数约束
**/

#undef INTERNAL_SYSCALL // note1
#define INTERNAL_SYSCALL(name, err, nr, args...) \
({ unsigned int _sys_result; \
{ \
register int _a1 asm ("a1"); \ // 定义一个寄存器变量,存放在a1 <==> r0
LOAD_ARGS_##nr (args) \
asm volatile ("swi %1 @ syscall " #name \ // swi 产生一个软中断异常,%1代表第一个输入参数
: "=r" (_a1) \ // _a1 = rx
: "i" (SYS_ify(name)) ASM_ARGS_##nr \ // 输入参数:软中断号(立即数),参数列表
: "memory"); \ // 告诉编译器内存可能被改变
_sys_result = _a1; \
} \
(int) _sys_result; })

#undef INTERNAL_SYSCALL_ERROR_P
#define INTERNAL_SYSCALL_ERROR_P(val, err) \
((unsigned int) (val) >= 0xfffff001u)

#undef INTERNAL_SYSCALL_ERRNO
#define INTERNAL_SYSCALL_ERRNO(val, err) (-(val))

#define LOAD_ARGS_0()
#define ASM_ARGS_0
#define LOAD_ARGS_1(a1) \
_a1 = (int) (a1); \
LOAD_ARGS_0 ()
#define ASM_ARGS_1 ASM_ARGS_0, "r" (_a1)
#define LOAD_ARGS_2(a1, a2) \
register int _a2 asm ("a2") = (int) (a2); \
LOAD_ARGS_1 (a1)
#define ASM_ARGS_2 ASM_ARGS_1, "r" (_a2)
#define LOAD_ARGS_3(a1, a2, a3) \
register int _a3 asm ("a3") = (int) (a3); \
LOAD_ARGS_2 (a1, a2)
#define ASM_ARGS_3 ASM_ARGS_2, "r" (_a3)
#define LOAD_ARGS_4(a1, a2, a3, a4) \
register int _a4 asm ("a4") = (int) (a4); \
LOAD_ARGS_3 (a1, a2, a3)
#define ASM_ARGS_4 ASM_ARGS_3, "r" (_a4)
#define LOAD_ARGS_5(a1, a2, a3, a4, a5) \
register int _v1 asm ("v1") = (int) (a5); \
LOAD_ARGS_4 (a1, a2, a3, a4)
#define ASM_ARGS_5 ASM_ARGS_4, "r" (_v1)
#define LOAD_ARGS_6(a1, a2, a3, a4, a5, a6) \
register int _v2 asm ("v2") = (int) (a6); \
LOAD_ARGS_5 (a1, a2, a3, a4, a5)
#define ASM_ARGS_6 ASM_ARGS_5, "r" (_v2)
#define LOAD_ARGS_7(a1, a2, a3, a4, a5, a6, a7) \
register int _v3 asm ("v3") = (int) (a7); \
LOAD_ARGS_6 (a1, a2, a3, a4, a5, a6)
#define ASM_ARGS_7 ASM_ARGS_6, "r" (_v3)

APCS 是arm过程调用标准(ARM Procedure Call Standard)。
APCS 对我们通常称为 R0 到 R14 的寄存器起了不同的名字,如下:

Reg# APCS 意义
R0 a1 工作寄存器
R1 a2 "
R2 a3 "
R3 a4 "
R4 v1 必须保护
R5 v2 "
R6 v3 "
R7 v4 "
R8 v5 "
R9 v6 "
R10 sl 栈限制
R11 fp 桢指针
R12 ip
R13 sp 栈指针
R14 lr 连接寄存器
R15 pc 程序计数器
*********************

*********************
__clone()

/* int clone(int (*fn)(void *arg), void *child_stack, int flags, void *arg); */

.text
ENTRY(__clone)
@ sanity check args
cmp r0, #0
cmpne r1, #0
moveq r0, #-EINVAL
beq PLTJMP(syscall_error)

@ insert the args onto the new stack
str r3, [r1, #-4]! // 线程函数参数入栈,(child_stack)
str r0, [r1, #-4]! // 线程函数指针入栈

@ do the system call
@ get flags
mov r0, r2 // flags 存入r0
@ new sp is already in r1
swi SYS_ify(clone) // swi __NR_clone, 产生软中断异常
movs a1, a1
blt PLTJMP(C_SYMBOL_NAME(__syscall_error))
RETINSTR(ne, lr)

@ pick the function arg and call address off the stack and execute
ldr r0, [sp, #4]
mov lr, pc
ldr pc, [sp]

@ and we are done, passing the return value through r0
b PLTJMP(_exit)
*********************

未完待续!

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics