IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    通过系统调用学习linux

    lazydba发表于 2011-03-28 11:50:59
    love 0
    系统调用是应用程序和linux内核交互的主要接口,或许可以通过学习各个系统调用的具体实现来加深对linux的理解。

    应用程序运行在用户态,syscall的实现是运行在内核态,需要有一种机制从用户态切换到内核态,然后才能执行syscall的代码,状态转换通常是由cpu提供的指令来实现,如中断int 0x80(cpu当然也可以提供其他实现,如x86下的sysenter)。内核启动的时候会设置好中断表,中断表可以理解为中断号码和中断处理程序的一个映射,比如int 0x80对应的中断处理程序就是系统调用处理程序,在x86_64下,处理程序叫做system_call,在arch/x86/kernel/entry_64.S里实现。

    每个系统调用都有一个编号,system_call通过系统调用编号来调用具体的系统调用:call *sys_call_table(,%rax,8) 。

    x86_64下sys_call_table的定义可以在arch/x86/kernel/Syscall_64.c里面找到,它include了arch/x86/include/asm/unistd_64.h这个文件:

    /*
    * This file contains the system call numbers.
    *
    * Note: holes are not allowed.
    */

    /* at least 8 syscall per cacheline */
    #define __NR_read 0
    __SYSCALL(__NR_read, sys_read)
    #define __NR_write 1
    __SYSCALL(__NR_write, sys_write)
    #define __NR_open 2
    __SYSCALL(__NR_open, sys_open)
    #define __NR_close 3
    __SYSCALL(__NR_close, sys_close)
    #define __NR_stat 4
    __SYSCALL(__NR_stat, sys_newstat)
    #define __NR_fstat 5


    sys_call_table将每个系统调用编号和具体的系统调用实现关联起来。系统调用的具体实现分布在内核代码各处。如open这个系统调用是在fs/open.c中实现的:
    SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
    {
    long ret;

    if (force_o_largefile())
    flags |= O_LARGEFILE;

    ret = do_sys_open(AT_FDCWD, filename, flags, mode);
    /* avoid REGPARM breakage on x86: */
    asmlinkage_protect(3, ret, filename, flags, mode);
    return ret;
    }

    SYSCALL_DEFINE3是一个宏,在include/linux/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__)

    上面宏展开之后,应当是这个样子:
    sys_open (const char __user *, filename, int, flags, int, mode)
    {
    ...
    }

    sys_open的具体实现是在do_sys_open这个函数里完成,这个函数和一般c编写的函数没有太大的差别,这不过它是允许在内核态的。

    各个系统调用基本处理流程基本上应该是差不多的,通过搜索SYSCALL_DEFINE[0-6],可以找到各个系统调用的具体实现。

    如 find /path/to/src -name *.c | xargs grep 'SYSCALL_DEFINE[0-6]'

    __END__


沪ICP备19023445号-2号
友情链接