• 通过实验分析system_call中断处理过程


    作者:吴乐 山东师范大学

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

    本实验目的:通过以一个简单的menu小程序,跟踪系统调用的过程,分析与总结系统调用的机制和三层进入的过程。

    一、实验步骤

    1.使用gdb在sys_time处设置断点并list找到的代码

    2.用s(step)跟踪断点

    3.当进入system_call的时候gdb无法继续跟踪,实验结束,找到源代码进行分析

    二、system_call对应的汇编代码的工作过程

    1.库函数触发中断,并给出系统调用号;2.操作系统通过中断描述符表找到对应的中断处理函数:

           于是我们看到了 : ENTRY(system_call)

            进一步找到对应的宏定义:/linux-3.18.6/include/linux/linkage.h

            #define ENTRY(name)
                                                    .globl name ASM_NL 
                                                    ALIGN ASM_NL 

                                                    name:

          ENTRY(system_call)

    RING0_INT_FRAME			# can't unwind into user space anyway
    	ASM_CLAC
    	pushl_cfi %eax			# save orig_eax
    	SAVE_ALL			# 保存现场
    	GET_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)	# 这里将真正调用对应的系统调用,调用号的意义在这里也表现出来了sys_call_table + 4 * %eax即为系统调用的地址
    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_work
    
    restore_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
    #endif
    restore_nocheck:
    	RESTORE_REGS 4			# skip orig_eax/error_code
    irq_return:
    	INTERRUPT_RETURN
    

      

             一下在附上系统调用表/linux-3.18.6/arch/frv/kernel/entry.S ,摘录了部分
     

    ###################################################################################################
    	.balign		L1_CACHE_BYTES
    	.globl		system_call
    system_call:
    	LEDS		0x6101
    	movsg		psr,gr4			; enable exceptions
    	ori		gr4,#PSR_ET,gr4
    	movgs		gr4,psr
    
    	sti		gr7,@(gr28,#REG_SYSCALLNO)
    	sti.p		gr8,@(gr28,#REG_ORIG_GR8)
    
    	subicc		gr7,#nr_syscalls,gr0,icc0
    	bnc		icc0,#0,__syscall_badsys
    
    	ldi		@(gr15,#TI_FLAGS),gr4
    	andicc		gr4,#_TIF_SYSCALL_TRACE,gr0,icc0
    	bne		icc0,#0,__syscall_trace_entry
    
    __syscall_call:
    	slli.p		gr7,#2,gr7
    	sethi		%hi(sys_call_table),gr5
    	setlo		%lo(sys_call_table),gr5
    	ld		@(gr5,gr7),gr4
    	calll		@(gr4,gr0)
    
    
    ###############################################################################
    #
    # return to interrupted process
    #
    ###############################################################################
    __syscall_exit:
    	LEDS		0x6300
    
    	# keep current PSR in GR23
    	movsg		psr,gr23
    
    	ldi		@(gr28,#REG_PSR),gr22
    
    	sti.p		gr8,@(gr28,#REG_GR(8))	; save return value
    

      

    三、总结:系统调用处理过程与中断处理的机制

    系统调用是通过软中断指令 INT 0x80 实现的,而这条INT 0x80指令就被封装在C库的函数中。(软中断和我们常说的硬中断不同之处在于,软中断是由指令触发的,而不是由硬件外设引起的。)

    INT 0x80 这条指令的执行会让系统跳转到一个预设的内核空间地址,它指向系统调用处理程序,即system_call函数。

    (注意:!!!系统调用处理程序system_call 并不是系统调用服务例程,系统调用服务例程是对一个具体的系统调用的内核实现函数,而系统调用处理程序是在执行系统调用服务例程之前的一个引导过程,是针对INT 0x80这条指令,面向所有的系统调用的。简单来讲,执行任何系统调用,都是先通过调用C库中的函数,这个函数里面就会有软中断 INT 0x80 语句,然后转到执行系统调用处理程序 system_call ,system_call 再根据具体的系统调用号转到执行具体的系统调用服务例程。)

      system_call函数是怎么找到具体的系统调用服务例程的呢?通过系统调用号查找系统调用表sys_call_table!软中断指令INT 0x80执行时,系统调用号会被放入 eax 寄存器中,system_call函数可以读取eax寄存器获取,然后将其乘以4,生成偏移地址,然后以sys_call_table为基址,基址加上偏移地址,就可以得到具体的系统调用服务例程的地址了!然后就到了系统调用服务例程了。需要说明的是,系统调用服务例程只会从堆栈里获取参数,所以在system_call执行前,会先将参数存放在寄存器中,system_call执行时会首先将这些寄存器压入堆栈。system_call退出后,用户可以从寄存器中获得(被修改过的)参数。

     另外:系统调用通过软中断INT 0x80陷入内核,跳转到系统调用处理程序system_call函数,然后执行相应的服务例程。但是由于是代表用户进程,所以这个执行过程并不属于中断上下文,而是进程上下文。因此,系统调用执行过程中,可以访问用户进程的许多信息,可以被其他进程抢占,可以休眠。当系统调用完成后,把控制权交回到发起调用的用户进程前,内核会有一次调度。如果发现有优先级更高的进程或当前进程的时间片用完,那么会选择优先级更高的进程或重新选择进程执行。

  • 相关阅读:
    循环语句
    流程控制
    特殊的赋值运算符
    位运算符
    运算符
    八种基本类型
    cmd基础命令
    springboot项目部署到tomcat步骤以及常见问题
    【算法问题】如何实现大整数相加
    【算法问题】删除k个数字后的最小值
  • 原文地址:https://www.cnblogs.com/wule/p/4381705.html
Copyright © 2020-2023  润新知