前面说了glibc以及标准C库函数和系统调用,但总感觉有点隔靴挠痒,下面就来追踪一下系统调用的源码。(linux-3.3内核,glibc-2.17源码)


系统调用以open函数为例

怎么查呢?

用  man 2 open 查看open的用法,看到需要包含三个头文件sys/types.h、sys/stat.h、fcntl.h,看了一下前两个,不相关,

好像是fcntl.h,但这个头文件里也没给出open的定义,但这个头文件又包含了一个头文件io/fcntl.h,跟下去,看到168行:


extern int open (const char *__file, int __oflag, ...) __nonnull ((1));


引进了open函数,查找它的定义(我用的是source insight,直接按住Ctrl点击函数,就可以跳到该函数定义的地方),出现三个宏两个定义,oldfileops.c和fileops.c参数不匹配,另一个宏不可以,fcntl2.h里的也不像。应该是loadmsgcat.c里的

# define open(name, flags) open_not_cancel_2 (name, flags)


此时open_not_cancel_2对应glibc-2.17\sysdeps\unix\sysv\linux\Not-cancel.h 26行

#define open_not_cancel_2(name, flags) \

   INLINE_SYSCALL (open, 2, (const char *) (name), (flags))


每个硬件平台的INLINE_SYSCALL封装不一样,对于ARM来说(在glibc-2.17\ports\sysdeps\unix\sysv\linux\arm\Sysdep.h)

#define INLINE_SYSCALL(name, nr, args...) \

  ({ unsigned int _sys_result = INTERNAL_SYSCALL (name, , nr, args); \

     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; })


跟踪上面的宏里INTERNAL_SYSCALL


#else /* ARM */

# undef INTERNAL_SYSCALL_RAW

# define INTERNAL_SYSCALL_RAW(name, err, nr, args...) \

  ({ \

       register int _a1 asm ("r0"), _nr asm ("r7"); \

       LOAD_ARGS_##nr (args) \

       _nr = name; \

       asm volatile ("swi 0x0 @ syscall " #name \

    : "=r" (_a1) \

    : "r" (_nr) ASM_ARGS_##nr \

    : "memory"); \

       _a1; })

#endif


看到上面嵌入的汇编指令 swi 异常,它会根据异常向量表实行系统调用,nr代表要实行的系统调用的编号,每一个系统调用对应一个编号。关于 swi 异常及系统调用请参见其他博文,这里只需要知道这条汇编指令能够根据我们传进来的系统调用函数的编号调用内核里定义的系统调用函数,从这里开始,系统调用就进入到内核态了。为什么会进入到内核态,进入到内核态以后又做了什么?

这就涉及到swi异常处理了,请参见之后的博文。