1.用户进程使用如open();的C库函数。

2从库函数(open)中使用"int 0x80"或是syscall命令进入system_call。

glibc 中的部分源码
//sysdeps/unix/sysv/linux/x86_64/sysdeps.h

INLINE_SYSCALL--->INTERNAL_SYSCALL--->INTERNAL_SYSCALL_NCS--->syscall.
# define INLINE_SYSCALL(name, nr, args...) \
  ({                                          \
    unsigned long int resultvar = INTERNAL_SYSCALL (name, , nr, args);          \
    if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (resultvar, ), 0))          \
      {                                          \
    __set_errno (INTERNAL_SYSCALL_ERRNO (resultvar, ));              \
    resultvar = (unsigned long int) -1;                      \
      }                                          \
    (long int) resultvar; })
    
# define INTERNAL_SYSCALL(name, err, nr, args...) \
  INTERNAL_SYSCALL_NCS (__NR_##name, err, nr, ##args)


  # define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
  ({                                          \
    unsigned long int resultvar;                          \
    LOAD_ARGS_##nr (args)                              \
    LOAD_REGS_##nr                                  \
    asm volatile (                                  \
    "syscall\n\t"                                  \//x86_64的关键指令syscall.
    : "=a" (resultvar)                                  \
    : "0" (name) ASM_ARGS_##nr : "memory", "cc", "r11", "cx");              \
    (long int) resultvar; })

有一个问题:在i386的CPU上,我们知道linux在初始化时为中断号0x80准备了一个中断处理函数(trap_init-->set_system_trap_gate(SYSCALL_VECTOR, &system_call);)。当发生int 0x80时,CPU通过一系列的动作(这个硬件动作,其它网文介绍很多),到达中断处理函数。那么在x86_64的CPU上,syscall指令能跳到哪去呢?CPU是如何处理这个指令的呢?需要OS为这个指令准备哪些信息?还有它又是怎么回来的?

3 CPU完成一系列切换的动作。

4.进入中断处理函数,更准确的说法是进入内核。

在arch/x86/kernel/entry_64.S#455或是在arch/x86/kernel/entry_32.S#L498(在entry_32.s为例)

    

ENTRY(system_call)

 499        RING0_INT_FRAME                 # can't unwind into user space anyway

 500        pushl_cfi %eax                  # save orig_eax

 501        SAVE_ALL

 502        GET_THREAD_INFO(%ebp)

 503                                        # system call tracing in operation / emulation

 504        testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)

 505        jnz syscall_trace_entry

 506        cmpl $(nr_syscalls), %eax

 507        jae syscall_badsys

 508syscall_call:

 509        call *sys_call_table(,%eax,4)

 510        movl %eax,PT_EAX(%esp)          # store the return value

 511syscall_exit:

 512        LOCKDEP_SYS_EXIT

 513        DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt

 514                                        # setting need_resched or sigpending

 515                                        # between sampling and the iret

 516        TRACE_IRQS_OFF

 517        movl TI_flags(%ebp), %ecx

 518        testl $_TIF_ALLWORK_MASK, %ecx  # current->work

 519        jne syscall_exit_work

当进入了system_call之后。
SAVE_ALL:???
。。。。
进入sys_call_table.xxx 中。
sys_call_table的定义在哪里,它的引用在哪里?为什么在内核和x86中的很多文件中都有它????
sys_call_table的定义:(在  linux/arch/x86/kernel/syscall_table_32.S)。
    ENTRY(sys_call_table)

   2        .long sys_restart_syscall       /* 0 - old "setup()" system call, used for restarting */

   3        .long sys_exit

   4        .long ptregs_fork

   5        .long sys_read

   6        .long sys_write

   7        .long sys_open          /* 5 */

   8

这样就可以找到内核定义的sys_function函数了。

5.运行各种各样的系统调用的函数。
 
那么各种各样的sys_function都是定义在哪里呢?
例:
SYSCALL_DEFINE3(semop, int, semid, struct sembuf __user *, tsops,
		unsigned, nsops)
{
函数实体。
。。。。。
。。。。。
}
在一些文件中,我们可以找到以上这样的语句。

在syscalls.h中定义如下:
#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)

它由一系列的宏,逐步定义的,如何做的??
这些宏的具体定义的内涵是什么?


SYSCALL_DEFINE3(semop,int,semid,......)这个宏,把"semop"的函数,定义成
sys_semop(int semid,struct sembuf __user* tsops,unsigned nsops);
这个sys_semop函数就是
sys_call_table中的一部分了。
这样用户进程在使用系统调用时,read,write,或是semget,semop时,就可以调用到相应的sys_xxxx函数了。

附:cond_syscall解释
/*
 * "Conditional" syscalls
 *
 * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
 * but it doesn't work on all toolchains, so we just do it by hand
 */
#ifndef cond_syscall
#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
#endif
在sys_ni.c中:cond_syscall(sys_fanotify_mark);
cond_syscall定义如下:
/*
* "Conditional" syscalls
*
* What we want is __attribute__((weak,alias("sys_ni_syscall"))),
* but it doesn't work on all toolchains, so we just do it by hand */
#ifndef cond_syscall#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")
#endif
asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall")分析如下:以上嵌入式汇编翻译成汇编代码如下:
.weak    #x    .set   #x,sys_ni_syscall
以上的含义是:在编译时,告诉编译器,当没有符号x时,就用sys_ni_syscall这个符号来代替符号x.

 
在http://www.acsu.buffalo.edu/~charngda/cc.html中说明如下:
#pragma weak symbol1=symbol2     
Declare symbol1 as a weak alias of symbol2. Equivalently, one can use
#pragma weak symbol1=symbol2   定义 symbol1作为symbol2的弱别名,与如下代码等效。
         __asm__(".weak symbol1"); //定义symbol1是弱符号。
        __asm__(".set symbol1,symbol2"); //把symbol1与symbol2联系起来。
A better way to achieve this is through the "weak, alias" function attributes.
还有一种更好的实现“弱,别名”函数属性的方式。
 

Logo

更多推荐