Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1184923
  • 博文数量: 573
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 66
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-28 16:21
文章分类

全部博文(573)

文章存档

2018年(3)

2016年(48)

2015年(522)

分类: LINUX

2015-12-09 15:27:00

系统调用-进程创建函数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                         // 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
        #include
        /* 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)
*********************


阅读(613) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~