zoukankan      html  css  js  c++  java
  • 从内核看系统调用

    本周学习了孟宁老师的《Linux内核分析》,按照课程要求,做实验如下:

    首先使用gdb跟踪一个系统调用,我们选择上周实验所写的代码,修改这两段代码成两个系统调用,放入根文件系统中,作为本次实验将要观察的系统调用。

    修改代码如下:

    1) c实现的系统调用

    int mkdir_c(int argc, char **argv)
    {
        if(argc != 2)
            printf("Illegal parameter!!
    ");
    
        if(mkdir(argv[1]) == -1)
        {
            printf("Mkdir error!!
    ");
            return -1;
        }
    
    printf("Mkdir success!! ");
    return 0; }

    2) 使用汇编语言实现的系统调用

    int mkdir_asm(int argc, char **argv)
    {
        if(argc != 2)
            printf("Illagel parameters!!
    ");
    
        int ret = 0;
        asm volatile(
                "mov $0, %%eax
    	"
                "mov $0x27, %%eax
    	"
                "int $0x80
    	"
                "mov %%eax, %0
    	"
                :"=m"(ret)
                :"b"(argv[1])
                );
    
        if(ret == -1)
        {
            printf("Mkdir Error!!
    ");
            return -1;
        }
    
    printf("Mkdir success!! ");
    return 0; }

    打开实验楼环境,进入menu目录下

    cd /home/shiyanlou/LinuxKernel/menu

    打开test.c文件,假如上面两段代码,如图所示:

    在main函数中添加两行代码注册这两个系统调用:

    重新编译,制作成根文件系统镜像。使用qemu仿真环境加载内核和根文件系统,启动系统。为了方便,我们使用把这一系列命令都写入Makefile文件中,所以只需在终端执行如下命令:

    make rootfs

    就能完成全部动作:

    我们可以执行我们所添加的命令了。

    接下来我们使用gdb工具来对系统调用进行调试。

    首先使用qemu进入调试模式,冻结住内核:

    启动gdb,加载调试符号表,开始进行调试:

    在system_call函数处设置了断点,但是无法在此处停止,因为system_call函数所在的entry_32.s文件是汇编代码,gdb对汇编代码的调试能力有限。

    为了了解系统调用机制的处理过程,我们对system_call这段代码进行阅读和分析:

    具体的代码参见:http://codelab.shiyanlou.com/xref/linux-3.18.6/arch/x86/kernel/entry_32.S

    首先内核要初始化系统调用机制,我们知道start_kernel是内核的入口地址,在这个函数中系统完成一系列的初始化工作,所以系统调用机制的初始化工作也必定在这个函数中,其中的trap_init函数就是负责对系统调用机制进行初始化,

    // in init/main.c   start_kernel
    553
    /* 554 * These use large bootmem allocations and must precede 555 * kmem_cache_init() 556 */ 557 setup_log_buf(0); 558 pidhash_init(); 559 vfs_caches_init_early(); 560 sort_main_extable(); 561 trap_init(); 562 mm_init();

    我们进入trap_init函数:

    837
    838#ifdef CONFIG_X86_32
    839    set_system_trap_gate(SYSCALL_VECTOR, &system_call);
    840    set_bit(SYSCALL_VECTOR, used_vectors);
    841#endif
    842

    这一段代码把中断向量表中的0x80号中断指向system_call代码段处,我们进入SYSCALL_VECTOR可以看到这个宏代表的就是0x80:

    50#ifdef CONFIG_X86_32
    51# define SYSCALL_VECTOR            0x80
    52#endif
    53

    接下来我们还分析system_call这段代码:

    489    # system call handler stub
    490    ENTRY(system_call)
    491    RING0_INT_FRAME            # can't unwind into user space anyway
    492    ASM_CLAC
    493    pushl_cfi %eax            # save orig_eax
    494    SAVE_ALL           //保存当前进程上下文,因为系统调用本身就是一种中断,所以和中断机制一样需要保存当前进程的一些信息,以便执行完系统调用后恢复现场
    495    GET_THREAD_INFO(%ebp)
    496                    # system call tracing in operation / emulation
    497    testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
    498    jnz syscall_trace_entry
    499    cmpl $(NR_syscalls), %eax
    500    jae syscall_badsys
    501syscall_call:
    502    call *sys_call_table(,%eax,4)    //调用系统调用对应的处理函数
    503syscall_after_call:
    504    movl %eax,PT_EAX(%esp)        # store the return value
    505syscall_exit:
    506    LOCKDEP_SYS_EXIT
    507    DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
    508                    # setting need_resched or sigpending
    509                    # between sampling and the iret
    510    TRACE_IRQS_OFF
    511    movl TI_flags(%ebp), %ecx
    512    testl $_TIF_ALLWORK_MASK, %ecx    # current->work    //检测当前的任务
    513    jne syscall_exit_work    //是否跳到syscall_exit_work来处理
    514
    515restore_all:
    516    TRACE_IRQS_IRET

    我们来看syscall_exit_work这段代码:

            # perform syscall exit tracing
    655    ALIGN
    656    syscall_exit_work:
    657    testl $_TIF_WORK_SYSCALL_EXIT, %ecx
    658    jz work_pending    //跳转至信号处理,work_reached进程调度处理等
    659    TRACE_IRQS_ON
    660    ENABLE_INTERRUPTS(CLBR_ANY)    # could let syscall_trace_leave() call
    661                    # schedule() instead
    662    movl %esp, %eax
    663    call syscall_trace_leave
    664    jmp resume_userspace
    665END(syscall_exit_work)

    最后我们绘制流程图来表述从system_call到iret这一段代码的执行流程:

    以上便是我对本次试验的全部理解,如有错误,还望指正。

    Allen 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 

  • 相关阅读:
    【每日算法】C语言8大经典排序算法(2)
    C++虚函数与纯虚函数的区别
    C++ 运算符优先级列表
    【每日算法】C语言8大经典排序算法(1)
    C++中的友元函数和友元类
    二叉树前序、中序、后序遍历相互求法
    【转】运算符重载的例析
    从一个二级题来看成员函数重载运算符和友元函数重载运算符
    测试用例管理之 TestLink
    【转】关于LoadRunner的迭代
  • 原文地址:https://www.cnblogs.com/slz-coder150315/p/4394324.html
Copyright © 2011-2022 走看看