实验五 分析 system_call 中断处理过程
实验内容:
1.按照之前做过的实验,打开QEMU
cd ~/LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
2.在test.c文件中增加Helloworld函数和在main函数中增加MenuConfig语句,编译并执行
代码如下:
int helloworld(int argc,char* argv[])
{
char message[] = {"helloworld,20222813\n"};
int length = 21;
write(1,message,length);
return 0;
}
MenuConfig("helloworld","show helloworld",helloworld);
3.make rootfs
再次运行Menu可以发现命令中添加了helloworld,可运行显示:
4.跟踪测试参考了之前写过的博客。使用 gdb 跟踪调试Linux内核的启动过程。在start_kernel、sys_time处设置断点,启动MenuOS后执行time命令,time命令仅执行一半
实验总结:
在linux-3.18.6/arch/x86/kernel/entry_32.s中找到system_call汇编代码并分析:
ENTRY(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结构的地址
# system call tracing in operation / emulation
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) # 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 //有任务,进入system_exit_work进行进程调度
restore_all:
TRACE_IRQS_IRET //恢复现场
restore_nocheck:
RESTORE_REGS 4 # skip orig_eax/error_code
irq_return:
INTERRUPT_RETURN //系统调用返回
系统调用的内核处理过程:
system call涉及syscall exit_work内部处理的一些关键点,大致的过程是syscall_exit_work需要跳转到work_pending,里面有 work notifysig 处理信号。还有work_resched是需要重新调度的,这里是进程调度的时机点call schedule,调度完之后就会跳转到restore all,恢复现场返回系统调用到用户态