system_call中断处理过程

 

张雨梅   原创作品转载请注明出处

 

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

     

1.给menuos添加命令

  改写menu下的test.c,在main中添加两行,

    MenuConfig("getuid","show system uid",Getuid);
    MenuConfig("getuidasm","show system uidasm",Getuidasm);

 并在main函数中添加Getuid、Getuidasm函数,其内容与上次实验一样。

运行结果如下图所示:

可以看到该系统支持getuid,getuidasm命令。

 

  2.使用gdb跟踪分析一个系统调用内核函数

       在终端输入

        qemu  -kernel  linux3.18.6/arch/x86/boot/bzImage  -initrd  rootfs.img  -s -S

        gdb

        file linux-3.18.6/vmlinux     加载调试相关内核的符号集

       target remote:1234  

       内核函数返回后进入汇编代码处理,gdb无法继续跟踪,因为当进入system call时,系统已经在挂起状态了。    

 

3. 分析系统调用的过程,从system_call开始到iret结束

    代码: http://codelab.shiyanlou.com/xref/linux-3.18.6/arch/x86/kernel/entry_32.S#490

490ENTRY(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
514
515restore_all:
516    TRACE_IRQS_IRET
517restore_all_notrace:
518#ifdef CONFIG_X86_ESPFIX32
519    movl PT_EFLAGS(%esp), %eax    # mix EFLAGS, SS and CS
520    # Warning: PT_OLDSS(%esp) contains the wrong/random values if we
521    # are returning to the kernel.
522    # See comments in process.c:copy_thread() for details.
523    movb PT_OLDSS(%esp), %ah
524    movb PT_CS(%esp), %al
525    andl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax
526    cmpl $((SEGMENT_LDT << 8) | USER_RPL), %eax
527    CFI_REMEMBER_STATE
528    je ldt_ss            # returning to user-space with LDT SS
529#endif
530restore_nocheck:
531    RESTORE_REGS 4            # skip orig_eax/error_code
532irq_return:
533    INTERRUPT_RETURN

SAVE_ALL 保存现场,将寄存器的值压入堆栈当中,压入堆栈的顺序对应着结构体struct pt_regs ,当出栈的时候,就将这些值传递到结构体struct pt_regs里面的成员,从而实现从汇编代码向C程序传递参数。struct pt_regs 位于:linux/arch/x86/include/asm/ptrace.h。  

GET_THREAD_INFO(%ebp) 获得当前进程的thread_info结构的地址,获取当前进程的信息,保存在ebp中。

testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp) jnz syscall_trace_entry  判断是否 trace调用,若需要,进入syscall_trace_entry

cmpl $(NR_syscalls), %eax  jae syscall_badsys   判断系统调用号是否超出系统调用表的最大值,如果大于,进入syscall_badsys

call *sys_call_table(,%eax,4)  进入到系统调用表查找到系统调用服务程序的入口函数的地址,把eax保存的系统调用号乘以4再与sys_call_table表的基址相加,即得到想要调用的函数的入口地址。sys_call_table位于:linux/arch/x86/kernel/syscall_table_32.S

movl %eax,PT_EAX(%esp)   保存函数返回值

movl TI_flags(%ebp), %ecx      testl $_TIF_ALLWORK_MASK, %ecx     jne syscall_exit_work  检测当前任务是否需要处理,如果需要转入syscall_exit_work

restore_all  恢复现场

INTERRUPT_RETURN 中断返回,即iret

下面分析中间几次判断之后可能调转到的函数做了什么工作。

syscall_trace_entry

1 syscall_trace_entry
2     movl $-ENOSYS,PT_EAX(%esp)
3     movl %esp, %eax
4     call syscall_trace_enter
5     /* What it returned is what we'll actually use.  */
6         cmpl $(NR_syscalls), %eax
7     jnae syscall_call
8     jmp syscall_exit
9 END(syscall_trace_entry)

       检查系统调用号是否大于系统调用表中的最大值,如果不大于,执行到syscall-call,如果大于,执行到system_exit。

syscall_badsys

1 syscall_badsys:
2     movl $-ENOSYS,%eax
3     jmp syscall_after_call
4 END(syscall_badsys)

       enosys表示文件系统不支持调用,对于这个命令的功能,猜测是把文件系统不支持调用的信号传给eax,执行到system_after_call,eax作为返回值输出。

syscall_exit_work

 1 syscall_exit_work:
 2     testl $_TIF_WORK_SYSCALL_EXIT, %ecx
 3     jz work_pending
 4     TRACE_IRQS_ON
 5     ENABLE_INTERRUPTS(CLBR_ANY)    # could let syscall_trace_leave() call
 6                     # schedule() instead
 7     movl %esp, %eax
 8     call syscall_trace_leave
 9     jmp resume_userspace
10 END(syscall_exit_work) 

   $_TIF_WORK_SYSCALL_EXIT与ecx相与为0,则执行work_pending。否则调用trace_leave,然后恢复用户空间。

work_pending

work_pending:
594    testb $_TIF_NEED_RESCHED, %cl
595    jz work_notifysig
596work_resched:
597    call schedule
598    LOCKDEP_SYS_EXIT
599    DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
600                    # setting need_resched or sigpending
601                    # between sampling and the iret
602    TRACE_IRQS_OFF
603    movl TI_flags(%ebp), %ecx
604    andl $_TIF_WORK_MASK, %ecx    # is there any work to be done other
605                    # than syscall tracing?
606    jz restore_all
607    testb $_TIF_NEED_RESCHED, %cl
608    jnz work_resched
609
610work_notifysig:                # deal with pending signals and
611                    # notify-resume requests
612#ifdef CONFIG_VM86
613    testl $X86_EFLAGS_VM, PT_EFLAGS(%esp)
614    movl %esp, %eax
615    jne work_notifysig_v86        # returning to kernel-space or
616                    # vm86-space
6171:
618#else
619    movl %esp, %eax
620#endif
621    TRACE_IRQS_ON
622    ENABLE_INTERRUPTS(CLBR_NONE)
623    movb PT_CS(%esp), %bl
624    andb $SEGMENT_RPL_MASK, %bl
625    cmpb $USER_RPL, %bl
626    jb resume_kernel
627    xorl %edx, %edx
628    call do_notify_resume
629    jmp resume_userspace
630
631#ifdef CONFIG_VM86
632    ALIGN
633work_notifysig_v86:
634    pushl_cfi %ecx            # save ti_flags for do_notify_resume
635    call save_v86_state        # %eax contains pt_regs pointer
636    popl_cfi %ecx
637    movl %eax, %esp
638    jmp 1b
639#endif
640END(work_pending)

      判断是否需要重新调度程序,用work_notifysig处理信号,调用schedule函数,然后返回到system_exit_work。

system_call到iret的流程图如下图。

 restore_all之后,即执行IRET。

 

实验总结

     上次实验总结了system_call的大致过程是保存现场,根据系统调用号调用对应的系统函数,然后恢复现场。而这只是system_call的简要概括,其实际还有相关配置,不同阶段不同情况的处理,是一个相对复杂的过程。这篇文章介绍了system_call执行的大概轮廓,有些地方也可能不准确,具体语句的作用还要进一步研究。

 
 
 

转载于:https://www.cnblogs.com/icecri/p/4393403.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值