第五节 系统调用的三个层次(下) ——20135203齐岳

第五节 系统调用的三个层次(下)

By 20135203齐岳

本周的课程主要内容有三点:

  1. 在MenuOS中通过添加代码增加自定义的系统调用命令
  2. 使用gdb跟踪调试内核
  3. 简单分析system_call代码了解系统调用在内核代码中的处理过程

由于本周实验是在Kali虚拟机上进行操作的,具体实现过程中的代码和实验楼里的环境有一定的差异,在解决了各种报错之后终于完成了实验,在这次的实验报告中将较为详细的记录实验过程中遇到的问题以及解决方案,以便以后更加熟练地理解与掌握Kali的操作原理和步骤。

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

给MenuOS增加time和time-asm命令

  • 在源代码中增加getpid和getpid-asm的代码

    Kali虚拟机中menu文件夹存放的目录为/home/YL/menu,打开目录即可看到内核相关代码,通过vim test.c指令打开并编辑源代码,在其中加入自定义添加的函数代码,如下图所示。

  • 在main函数中增加MenuConfig

    在main函数中添加对应的函数调用代码,格式与代码中原有的函数调用格式保持一致。

  • make rootfs(将分步编译的过程编写在脚本当中自动生成)

    进行这一步的时候无论是使用make rootfs脚本编译还是make all都会出现错误:Nothing to be done,make clean指令也无效。其原因是该文件目录下有一个名为rootfs的文件夹,导致编译失败,删除该文件夹后即可成功编译,结果如下图所示。

使用gdb跟踪系统调用内核函数sys_time

  • 启动内核到调试状态

      qemu-system-x86_64 -kernel bzImage -initrd /home/YL/rootfs.img -S -s
    
  • 重新启动一个终端进行调试

    在内核中加载符号表:

      file /usr/src/linux-source-4.4/vmlinux
    

由于Kali是64位机所以要进行设置否则会报错"Remote 'g'packet reply is too long"。

	set arch i386:x86-64

之后使用1234端口连接并开始调试。运行结果如下图所示。

![](http://images2015.cnblogs.com/blog/744942/201603/744942-20160325172505417-1560444924.jpg)
  • 设置断点并单步跟踪调试

    注意不能在start _ kernel处设置断点因为此时还没有完整设置好64位的运行环境,因此无法设置断点进行调试。

    s单步执行,sys _ getpid 函数返回之后进入汇编代码处理,gdb无法继续跟踪,在sys _ call处设置断点也无法停下来调试。

系统调用在内核代码中的处理过程

系统调用在内核代码中的工作机制和初始化

系统调用的工作机制

具体内容见上周实验报告:http://www.cnblogs.com/July0207/p/5277774.html

系统调用机制的初始化

initmain.c start _ kernel中调用了trap _ init()函数。

archx86kernel raps.c代码中定义了系统调用的初始化。

#ifdef CONFIG_X86_32
set_system_trap_gate(SYSCALL_VECTOR, &system_call);
//系统调用的中断向量和system_call的入口。一旦执行0x80,系统就自动跳转到system _call执行
set_bit(SYSCALL_VECTOR, used_vectors);
#endif
分析system_call伪代码
ENTRY(system_call)			#0x80的下一条 指令
	RING0_INT_FRAME			# can't unwind into user space anyway
	ASM_CLAC
	pushl_cfi %eax			# save orig_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 # 比较eax中的系统调用号和最大syscall,超过则无效
	jae syscall_badsys # 无效的系统调用 直接返回
syscall_call:
	call *sys_call_table(,%eax,4) # 调用实际的系统调用程序
syscall_after_call:
	movl %eax,PT_EAX(%esp)		# 将系统调用的返回值eax存储在栈中
syscall_exit:
	LOCKDEP_SYS_EXIT
	DISABLE_INTERRUPTS(CLBR_ANY)	# make sure we don't miss an interrupt
				
	TRACE_IRQS_OFF
	movl TI_flags(%ebp), %ecx
	testl $_TIF_ALLWORK_MASK, %ecx	# 判断当前的任务是否需要进程调度
	jne syscall_exit_work  			# 未完成,则去执行这些任务
restore_all:
	TRACE_IRQS_IRET			# iret 从系统调用返回
系统调用流程分析

参考资料

【原创作品转载请注明出处】 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

原文地址:https://www.cnblogs.com/July0207/p/5320325.html