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

本文深入剖析Linux系统调用机制,通过一个简单的menu小程序跟踪系统调用过程,揭示软中断INT0x80如何触发系统调用及system_call函数如何根据系统调用号查找系统调用表sys_call_table来调用具体的系统调用服务例程。

作者:吴乐 山东师范大学

《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函数,然后执行相应的服务例程。但是由于是代表用户进程,所以这个执行过程并不属于中断上下文,而是进程上下文。因此,系统调用执行过程中,可以访问用户进程的许多信息,可以被其他进程抢占,可以休眠。当系统调用完成后,把控制权交回到发起调用的用户进程前,内核会有一次调度。如果发现有优先级更高的进程或当前进程的时间片用完,那么会选择优先级更高的进程或重新选择进程执行。

<think>我们正在讨论中断处理过程,需要结合引用内容进行分析。引用[1]和[2]提供了中断机制的基本概念和分类,引用[3]则提供了一个具体的实验环境来分析中断处理过程。用户的问题是如何分析计算机系统中的中断处理过程。 根据引用[1]和[2],中断处理过程一般包括以下几个步骤: 1. 中断请求:由硬件或软件发出中断信号。 2. 中断响应:处理器在满足一定条件(如当前指令执行完毕,且中断允许)时响应中断。 3. 保存现场:处理器保存当前程序的上下文(如程序计数器、状态寄存器等)。 4. 中断服务:执行中断服务程序(ISR)来处理中断事件。 5. 恢复现场:从中断返回,恢复之前保存的上下文,继续执行原程序。 引用[3]提供了一个具体的实验环境(Linux 0.11)来观察中断处理过程,特别是系统调用(int 0x80)的执行。 因此,我们可以结合理论和实验分析中断处理过程。 步骤: 1. 中断触发:例如,执行一条int指令(软件中断)或硬件设备发出中断信号。 2. 处理器响应中断,根据中断类型号在中断描述符表(IDT)中找到对应的中断门描述符。 3. 特权级检查:如果发生特权级变化,需要切换栈(如从用户态切换到内核态)。 4. 保存现场:将当前的EFLAGS、CS、EIP以及错误码(如果有)压入栈中(如果是特权级变化,还会压入SS和ESP)。 5. 跳转到中断服务程序(ISR)的入口点。 6. 在ISR中,通常会保存其他寄存器的值(通用寄存器等),然后执行具体的中断处理逻辑。 7. 处理完成后,恢复保存的寄存器,然后执行IRET指令返回。IRET会从栈中弹出EIP、CS和EFLAGS,如果特权级有变化,还会弹出SS和ESP。 在引用[3]的实验中,用户需要观察执行int 0x80指令前后的寄存器状态和栈状态,这正好是中断处理过程中保存现场的关键步骤。 下面我们结合引用[3]的实验问题来具体分析: 问题1:在0号进程执行fork系统调用中的陷入指令(int 0x80)之前,当前指令位置(CS:EIP)和栈位置(SS:ESP)分别是多少? 问题2:使用si命令执行了该指令后,新指令位置和栈位置分别是多少? 问题3:此时栈中保存的恢复点位置和用户栈位置分别是多少? 这些问题的答案就是中断处理过程中保存现场的具体体现。 因此,我们可以这样组织回答: 1. 中断处理的一般过程(理论) 2. 结合实验(引用[3])的具体分析 注意:由于引用[3]是一个实验,我们无法直接得到答案,但可以描述在中断处理过程中这些寄存器和栈的变化情况。 另外,引用[2]提到中断的分类,我们可以简要提及,但重点在中断处理过程。 由于用户要求分析中断处理过程,我们将重点放在步骤的详细描述上。 注意:在回答中,我们需要写出引用[3]实验的具体答案(因为实验需要用户自己操作),但可以描述在中断发生时,这些寄存器值是如何被保存的。 结构: 一、中断处理过程概述 二、中断处理详细步骤(核心) 三、结合系统调用(int 0x80)的具体过程 四、实验观察建议(根据引用[3]) 最后,生成相关问题。 注意:使用LaTeX格式的数学表达式,但中断处理过程中可能数学表达式多,我们可以在描述栈变化时使用公式表示栈指针的变化。 例如:当发生中断时,栈指针的变化可以表示为: $$ \text{新栈指针} = \text{原栈指针} - k $$ 其中k为压入栈的字节数。 开始回答:</think>### 计算机系统中的中断处理过程分析 #### 1. **中断处理核心流程** 中断处理过程遵循严格的硬件-软件协作机制,主要分为以下阶段: 1. **中断触发** - 硬件中断:外设通过中断请求线(IRQ)向中断控制器(如APIC)发送信号[^1] - 软件中断:执行`int n`指令触发系统调用或异常 - 关键寄存器:中断描述符表寄存器(IDTR)定位中断向量表 2. **现场保存** 处理器自动将关键状态压入内核栈: ```mermaid graph LR A[中断发生] --> B[保存EFLAGS] B --> C[保存CS:EIP] C --> D[保存错误码(可选)] D --> E[特权级切换时保存SS:ESP] ``` 数学表示: $$ \text{新栈帧} = \begin{cases} \text{EFLAGS} \\ \text{CS} \\ \text{EIP} \\ \text{ErrorCode} \\ \text{SS:ESP (若CPL变化)} \end{cases} $$ 3. **中断分发** - 中断控制器将中断向量号发送给CPU - CPU通过IDT索引中断门描述符 - 加载目标代码段选择符和偏移量到CS:EIP 4. **中断服务程序(ISR)执行** - 保存通用寄存器状态 - 执行具体中断处理逻辑 - 通过`iret`指令返回 5. **现场恢复** 处理器自动从栈中弹出: ```c EIP = pop() CS = pop() EFLAGS = pop() if (特权级变化) { ESP = pop() SS = pop() } ``` #### 2. **关键硬件机制** 1. **中断描述符表(IDT)** - 包含256个门描述符(8字节/个) - 门类型:任务门/中断门/陷阱门 - 结构示例: | 偏移31:16 | 属性 | 选择符 | 偏移15:0 | |-----------|------|--------|----------| | 0x0010 | 0x8E00 | 0x0008 | 0x2000 | 2. **特权级切换** - 用户态(CPL=3)→内核态(CPL=0)时: - TSS中加载新SS0:ESP0 - 栈切换避免用户栈污染 - 数学表示: $$ \Delta \text{Privilege} = \begin{cases} 0 & \text{无切换} \\ 1 & \text{切换栈帧} \end{cases} $$ #### 3. **系统调用实例分析** 以Linux 0.11的fork系统调用(`int 0x80`)为例[^3]: ```c // 用户空间调用 movl $2, %eax // fork系统调用号 int $0x80 // 内核处理流程 entry.s: _system_call: push %ds..%fs // 保存寄存器 call _sys_fork // 执行系统调用 iret // 中断返回 ``` 关键寄存器变化: | 阶段 | CS:EIP | SS:ESP | 栈内容 | |------|--------|--------|--------| | 执行int前 | 用户CS:下条指令 | 用户栈 | - | | 执行int后 | 内核CS:system_call | 内核栈 | 用户EIP, 用户CS, EFLAGS | | iret返回后 | 用户CS:原下条指令 | 用户栈 | - | #### 4. **性能优化技术** 1. **中断延迟优化** - 中断嵌套(NMI除外) - 中断优先级分组 - 关键路径:最小化关中断时间 $$ \text{最坏延迟} = \sum_{i=1}^{n} (\text{ISR}_i\text{执行时间}) $$ 2. **下半部机制** | 机制 | 执行上下文 | 可中断性 | 典型应用 | |------|------------|----------|----------| | 软中断 | 中断上下文 | 中断 | 网络收包 | | tasklet | 软中断上下文 | 中断 | 块设备IO | | 工作队列 | 进程上下文 | 可调度 | 文件操作 | > ⚠️ **总结**:中断处理是硬件触发→软件响应的精密协作过程,核心在于**现场保存的完整性**和**特权级切换的安全性**。现代OS通过IDT路由、内核栈隔离和下半部机制实现高效处理[^1][^2]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值