Linux内核分析(五)

来源:互联网 发布:ssm项目源码和视频讲解 编辑:程序博客网 时间:2024/06/10 11:41

Linux 内核分析——【实验五:系统调用运行机制】
前文提到系统调用是通过int 0x80来产生的,所以从本质上来说它是一种中断。那么什么是中断呢?中断被定义为一个事件,该事件改变了处理器执行的指令顺序。在linux系统下设置了256个中断,每个中断由0~255之间的数来标识,系统调用对应的就是0x80。
首先,我们总结一下系统调用的执行过程:
1、程序调用libc库中封装的系统调用函数。
2、调用中断int 0x80 陷入内核。
3、在内核中执行system_call函数(实际上是一段汇编代码),将系统调用号(eax)和可以所有相关寄存器保存到内核堆栈中(由SAVE_ALL完成),然后根据系统调用号在系统调用表中查找到对应的系统调用服务例程。
4、执行该服务例程。
5、执行完毕后,转入ret_from_sys_call 例程,从系统调用返回

接着,我们通过一个简单的例子,来了解系统调用函数在进入system_call到iret的执行过程。代码如下:
5-1

运行qemu并使用gdb进行调试,步骤如下:
5-2

将断点设在system_call位置,可以看到system_call在一个entry_32.S的汇编文件中。继续执行,但gdb并没有在system_call的位置停止,而是直接运行结束了。
下面,我们直接打开entry_32.S文件,找到system_call的位置进行分析:

# system call handler stubENTRY(system_call)            #系统调用处理入口(内核态)    RING0_INT_FRAME         # can't unwind into user space anyway    ASM_CLAC    pushl_cfi %eax          # save orig_eax #保存eax,也就是调用号     SAVE_ALL              # 保存寄存器     GET_THREAD_INFO(%ebp) # 获取thread_info结构中ebp的值                     # system call tracing in operation / emulation    testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)   # 检测是否由系统跟踪     jnz syscall_trace_entry     # 有系统跟踪则先去执行     cmpl $(NR_syscalls), %eax   # 比较输入的系统调用号 是否大于 最大的系统调用号     jae syscall_badsys          # 大于 则无效,退出 syscall_call:    call *sys_call_table(,%eax,4) # 在系统调用表中的调用相应的服务例程,eax为调用号,4字节对齐 syscall_after_call:                   movl %eax,PT_EAX(%esp)      # store the return value # 保存返回值 syscall_exit:    LOCKDEP_SYS_EXIT               # 用于调试,只有开启调试后才会检测系统调用深度     DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt                    # setting need_resched or sigpending                    # between sampling and the iret    TRACE_IRQS_OFF        # 关闭中断跟踪     movl TI_flags(%ebp), %ecx   # 检测是否还有其他任务     testl $_TIF_ALLWORK_MASK, %ecx # current->work    jne syscall_exit_worksyscall_exit_work:    testl $_TIF_WORK_SYSCALL_EXIT, %ecx    jz work_pending     # 测试是否退出前还有工作要处理,如果有的话跳转到work_pending      TRACE_IRQS_ON       # 开启系统中断跟踪     ENABLE_INTERRUPTS(CLBR_ANY)  # could let syscall_trace_leave() call    # 允许中断   # schedule() instead    movl %esp, %eax    call syscall_trace_leave    jmp resume_userspace   # 恢复用户空间 END(syscall_exit_work)work_pending:    testb $_TIF_NEED_RESCHED, %cl  # 是否有需要继续调度的相关信号     jz work_notifysig     # 跳转到处理信号相关的代码处 work_resched:    call schedule     # 时间调度, 进程调度的时机在这里处理     LOCKDEP_SYS_EXIT    DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt                    # setting need_resched or sigpending                    # between sampling and the iret    TRACE_IRQS_OFF    movl TI_flags(%ebp), %ecx    andl $_TIF_WORK_MASK, %ecx    # is there any work to be done other  是否还有其他工作要处理                    # than syscall tracing?    jz restore_all      #如果没有的话就恢复中断上下文,也就是恢复进入之前保存的寄存器相关内容    testb $_TIF_NEED_RESCHED, %cl    jnz work_reschedwork_notifysig:        # deal with pending signals and                        # notify-resume requests                    #ifdef CONFIG_VM86    testl $X86_EFLAGS_VM, PT_EFLAGS(%esp)    movl %esp, %eax    jne work_notifysig_v86        # returning to kernel-space or                                  # vm86-spacerestore_all:    TRACE_IRQS_IRET          # 恢复中断跟踪 restore_all_notrace:#ifdef CONFIG_X86_ESPFIX32    movl PT_EFLAGS(%esp), %eax  # mix EFLAGS, SS and CS    # Warning: PT_OLDSS(%esp) contains the wrong/random values if we    # are returning to the kernel.    # See comments in process.c:copy_thread() for details.    movb PT_OLDSS(%esp), %ah    movb PT_CS(%esp), %al    andl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax    cmpl $((SEGMENT_LDT << 8) | USER_RPL), %eax    CFI_REMEMBER_STATE    je ldt_ss           # returning to user-space with LDT SS#endifrestore_nocheck:    RESTORE_REGS 4          # skip orig_eax/error_codeirq_return:    INTERRUPT_RETURN

系统调用函数的运行流程如下:
5-5

注意:系统调用的初始化工作,是在内核启动之初/linux-3.18.6/init/main.c文件中
start_kernel() —> trap_init() —> set_system_trap_gate(SYSCALL_VECTOR, &system_call);

附录:
查看系统调用号:linux-3.18.6/arch/x86/syscalls/syscall_32.tbl

# 32-bit system call numbers and entry vectors## The format is:# <number> <abi> <name> <entry point> <compat entry point>## The abi is always "i386" for this file.#0   i386    restart_syscall     sys_restart_syscall1   i386    exit            sys_exit2   i386    fork            sys_fork            stub32_fork3   i386    read            sys_read4   i386    write           sys_write5   i386    open            sys_open            compat_sys_open6   i386    close           sys_close7   i386    waitpid         sys_waitpid         sys32_waitpid8   i386    creat           sys_creat9   i386    link            sys_link10  i386    unlink          sys_unlink11  i386    execve          sys_execve          stub32_execve12  i386    chdir           sys_chdir13  i386    time            sys_time            compat_sys_time14  i386    mknod           sys_mknod15  i386    chmod           sys_chmod16  i386    lchown          sys_lchown1617  i386    break18  i386    oldstat         sys_stat19  i386    lseek           sys_lseek           compat_sys_lseek20  i386    getpid          sys_getpid21  i386    mount           sys_mount           compat_sys_mount22  i386    umount          sys_oldumount23  i386    setuid          sys_setuid1624  i386    getuid          sys_getuid1625  i386    stime           sys_stime           compat_sys_stime26  i386    ptrace          sys_ptrace          compat_sys_ptrace27  i386    alarm           sys_alarm28  i386    oldfstat        sys_fstat29  i386    pause           sys_pause30  i386    utime           sys_utime           compat_sys_utime31  i386    stty32  i386    gtty33  i386    access          sys_access34  i386    nice            sys_nice35  i386    ftime36  i386    sync            sys_sync37  i386    kill            sys_kill38  i386    rename          sys_rename39  i386    mkdir           sys_mkdir40  i386    rmdir           sys_rmdir41  i386    dup         sys_dup42  i386    pipe            sys_pipe43  i386    times           sys_times           compat_sys_times44  i386    prof45  i386    brk         sys_brk46  i386    setgid          sys_setgid1647  i386    getgid          sys_getgid1648  i386    signal          sys_signal49  i386    geteuid         sys_geteuid1650  i386    getegid         sys_getegid1651  i386    acct            sys_acct52  i386    umount2         sys_umount53  i386    lock54  i386    ioctl           sys_ioctl           compat_sys_ioctl55  i386    fcntl           sys_fcntl           compat_sys_fcntl6456  i386    mpx57  i386    setpgid         sys_setpgid58  i386    ulimit59  i386    oldolduname     sys_olduname60  i386    umask           sys_umask61  i386    chroot          sys_chroot62  i386    ustat           sys_ustat           compat_sys_ustat63  i386    dup2            sys_dup264  i386    getppid         sys_getppid65  i386    getpgrp         sys_getpgrp66  i386    setsid          sys_setsid67  i386    sigaction       ... ...

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

1 0
原创粉丝点击