分析system_call中断处里过程

孙业毅 原创作品 转载请注明出处

Linux内核分析》MOOC课程:http://mooc.study.163.com/course/USTC-1000029000

第五讲 扒开系统调用的三层皮(下) 

@2015.04


一、理论知识

通过gdb我们可以给系统调用内核处里程序如sys_write, sys_time设置断点,并让程序停在断点处,进行断点跟踪系统调用处里过程。由于system_call是完全用汇编写就一个的函数,虽然我们也可以在system_call处设置断点,但却无法让系统停在system_call处,所以也无法通过单步跟踪学习其处里流程。但system_call是所有系统调用的入口,也是程序由用户态转入内核态执行时无法越过的一个函数,其重要性不言而喻,所以我们跟随老师简化的汇编代码以及源代码学习其主要的流程。

二、实验过程

实验环境是使用本课程配备的实验楼虚拟机环境,打开命令行客户端,cd LinuxKernel目录,使用命令rm -rf menu删除原来的代码,使用git clone https://github.com/mengning/menu.git获取menu的最新代码,然后 cd menu 进入menu子文件夹,使用gedti test.c打开文件,讲我上周实验的代码拷贝改写成为menu的两个菜单项,主要代码部分如下:

int SayHello(int argc, char *argv[])
{
        char* msg = "Hello World";
        printf("%s", msg);

        printf("\n");

        return 0;
}

int SayHelloAsm(int argc, char *argv[])
{
        char* msg = "Hello World";
        int len = 11;
        int result = 0;

        __asm__ __volatile__("movl %2, %%edx;\n\r" 
                "movl %1, %%ecx;\n\r" 
                 "movl $1, %%ebx;\n\r" 
                 "movl $4, %%eax;\n\r" 
                  "int  $0x80" 
                :"=m"(result) 
                :"m"(msg),"r"(len)  
                :"%eax"); 

        printf("\n");

        return 0;
}

int main()
{
    PrintMenuOS();
    SetPrompt("MenuOS>>");
    MenuConfig("version","MenuOS V1.0(Based on Linux 3.18.6)",NULL);
    MenuConfig("quit","Quit from MenuOS",Quit);
    MenuConfig("time","Show System Time",Time);
    MenuConfig("time-asm","Show System Time(asm)",TimeAsm);
<strong>    MenuConfig("sayhello","Say Hello World",SayHello);
    MenuConfig("sayhello-asm","Say Hello World",SayHelloAsm);</strong>
    ExecuteMenu();
}

这里主要就是加了两个菜单项 sayhello 和 sayhello-asm 及其对应的实现函数,保存 test.c 函数,使用 make rootfs 编译运行 menuos 系统,输入help 可以看到我们新加入的菜单:


使用 gdb 跟踪相关的函数过程,在我前述的博客中有笔记详细的描述,有兴趣的可以参考下:http://blog.csdn.net/sunyeyi/article/details/44517109,这里主要就是说明下,我们可以在 gdb 里使用 b system_call 设置断点,但是却无法让程序停在这个地方。当然网上也有资料提供了其他技术来跟踪这个函数,由于个人时间能力所限,没有去研究!


三、通过源代码分析 system_call 处理流程 

先看下系统调用机制的初始化,在start_kernel函数中,trap_init函数就是完成系统调用初始化的。

\init\main.c start_kernel

trap_init();

在trap_init()函数中,关键代码如下,通过中断向量,将system_call函数和0x80绑定:

\arch\x86\kernel\traps.c

#ifdef CONFIG_X86_32
    set_system_trap_gate(SYSCALL_VECTOR, &system_call);
    set_bit(SYSCALL_VECTOR, used_vectors);
#endif

据此,用户态代码只要执行 int 0x80 中断,就会由 system_call 函数来处里。

再来看看system_call源代码,http://codelab.shiyanlou.com/xref/linux-3.18.6/arch/x86/kernel/entry_32.S,490行处。


根据这段源代码,参考老师在视频中的讲述,我们可以得到system_call的简化流程图如下:


这里我们需要特别注意的就是,在系统调用system_call返回之前,会检查当前进程是否需要执行syscall_exit_work,如果不需要就restore_all,然后中断返回了;如果需要处里,那么就在syscall_exit_work流程中查看是否有进程信号需要处理,若有就处里,是否需要进程调度,若有就调用schedule,切换到其他进程执行;然后才restore_all,中断返回。


四、总结

通过中断向量表,int 0x80和system_call关联起来;system_call又是通过系统调用号,将每一个系统调用和特定的系统调用服务例程关联起来;在system_call返回用户态之前,会执行syscall_exit_work,work_notifysig,schedule等函数以应对可能的进程信号处理和进程调度。





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值