分析system call中断处理过程
一、实验过程
1.打开实验楼虚拟机,输入以下命令
cd ~/LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
结果如下图所示:
2.在test.c文件中增加Helloworld函数,删除一行多余的MenuConfig和在main函数中增加MenuConfig语句,编译并执行
int helloworld(int argc,char* argv[])
{
char message[] = {"helloworld,20232816\n"};
int length = 21;
write(1,message,length);
return 0;
}
3.再次运行menu中添加了helloworld命令后,运行显示
4.使用gdb调试(之前课上做过)
条件变量以及打印。
行断点、函数断点、查看断点信息
使用 gdb 跟踪调试Linux内核的启动过程。在start_kernel、sys_time处设置断点。
二、实验分析
通过课本知识讲解明白了在 start_kernel 中的 trap_init()(定义在/arch/x86/kernel/traps.c中)里将中断向量0x80和system_call中断服务程序入口进行了绑定。
#ifdef CONFIG_X86_32
set_system_trap_gate(SYSCALL_VECTOR, &system_call); //绑定中断向量0x80和system_call中断服务程序入口,其中SYSCALL_VECTOR定义为0x80
set_bit(SYSCALL_VECTOR, used_vectors);
#endif
system_call中断服务程序入口代码如下:
ENTRY(system_call)
RING0_INT_FRAME #can't unwind into user space anyway
ASM_CLAC
pushl_cfi %eax #保存系统调用号
SAVE_ALL #保存现场,将CPU寄存器保存在堆栈中
GET_THREAD_INFO(%ebp) #ebp用于存放当前进程thread_info结构的地址
testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
jnz syscall_trace_entry
cmpl $(NR_syscalls), %eax #检查系统调用号(系统调用号应小于NR_syscalls)
jae syscall_badsys #不合法,跳入到异常处理
syscall_call:
call *sys_call_table(,%eax,4) #合法,对照系统调用号在系统调用表中寻找相应服务例程
syscall_after_call:
movl %eax,PT_EAX(%esp) #保存返回值到栈中
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 #检查是否有任务需要处理
jne syscall_exit_work #需要,进入 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 #iret
流程图:
三、实验总结
在32位x86架构上,使用int 0x80指令进行系统调用。它将中断向量号0x80传递给处理器,从而触发内核中的中断处理程序。系统调用号(syscall number)存储在寄存器eax中。系统调用的参数存储在寄存器ebx、ecx、edx、esi、edi和ebp中。
执行系统调用后,返回值存储在eax寄存器中。在64位x86架构上,使用syscall指令进行系统调用。它通过相应的寄存器传递系统调用号和参数。系统调用号存储在rax寄存器中。系统调用的参数存储在rdi、rsi、rdx、r10、r8和r9寄存器中。
执行系统调用后,返回值存储在rax寄存器中。system call涉及syscall exit_work内部处理的一些关键点,大致的过程是syscall_exit_work需要跳转到work_pending,里面有 work notifysig 处理信号。