Linux系统调用-扒开系统调用的三层皮(下)

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

一、实验过程

1. 首先进入实验楼,打开实验楼终端;然后输入命令cd LinuxKernel进入到LinuxKernel目录下,本次实验要给menu加入新的功能,所以输入命令rm menu -rf删除原来的menu,然后输入命令git clone          https://github. 
com/mengning/menu.git重新克隆一个新版本的menu。实验结果如图1所示。
Linux系统调用-扒开系统调用的三层皮(下)_第1张图片
图1
2.输入命令cd menu 进入到menu中,然后输入make rootfs对menu进行重新编译,并启动qume。我们可以在图3中看到QUME中新增了两个命令time和time-asm,实验结果如图2、图3所示。

图2
Linux系统调用-扒开系统调用的三层皮(下)_第2张图片
图3
3. 我们可以再MENUOS中增加自己的系统调用和命令,具体过程如下:

1. 进入menu文件下,并打开menu文件夹下的文件test.c。
2. 将上次实验中所写的两个系统调用的程序粘贴到test.c文件中。
3. 在main函数中添加新的命令,利用MenuConfig函数添加新的命令,代码如下所示;其中第一个参数为MENUOS系统中新加的命令,第二个参数为输入该命令时输出的说明,第三个参数为执行命令时所调用的函数。
 MenuConfig("open","Show FD",Open);
 MenuConfig("open-asm","Show FD(asm)",Open_asm);
4. 根据步骤3添加完新的命令后,输入命令make rootfs对程序进行重新编译,然后我们会看到新添加的命令,输入命令会进行相应的系统调用,实验结果如图4所示:
Linux系统调用-扒开系统调用的三层皮(下)_第3张图片
图4
5. 输入命令cd ..返回到menu目录下,然后输入命令qume -kernel linux-3.18.6/arch/x86/boot/bzImage-initrd rootfs.img -s -S s 启动MENUOS,然后进入gdb 输入命令file linux-3.18.6/vmlinux,在gdb界面中加载符号表;然后输入命令target remote:1234建立gdb和gdbserver之间的连接;然后输入命令break start_kernel设置断点,输入c让qemu上的Linux继续运行,输入list可以查看断点附近的源码,实验结果如图5所示。

图5
6. 在gdb中输入命令b sys_open在open系统调用处打断点,然后开始执行,进行调试,实验结果如图6、图7所示。

图6

图7

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

1. 系统调用的过程如图2-1所示,系统从xyz()函数中调用system_call,然后system_call调用相对应的系统调用sys_xyz()。
Linux系统调用-扒开系统调用的三层皮(下)_第4张图片
图2-1
2. 系统调用的初始化过程如下:
1. 系统首先进入到/init/main.c文件中的start_kernel函数中,
2. 在start_kernel函数中调用函数trap_init()函数,trap_init()函数在\arch\x86\kernel\traps.c文件中实现。
3. 最后在trap_init()函数中执行代码set_system_trap_gate(SYSCALL_VECTOR,&system_call)和set_bit(SYSCALL_VECTOP,used_vectors)绑定system_call函数,使的执行0x80中断时执行system_call开始的汇编代码。

3. 代码执行完int ox80后就会进入到代码ENINRY(system_call)开始执行。执行代码如下所示:

<pre name="code" class="cpp">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)
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


 4. 从system_call开始到iret结束之间的整个过程的流程图如图2-2所示。 
  

Linux系统调用-扒开系统调用的三层皮(下)_第5张图片

图2-2

三、总结

1. 系统调用”是操作系统提供给用户程序进行调用的一些服务。这些服务是系统预先提供的函数,在这一点上系统调用与普通的用户程序是没有区别的。而区别则在于“系统调用”是由操作系统提供给用户的,这些服务更接近底层或者要求的安全性更高,因此由操作系统来统一实现和管理。
2处理器在eax寄存器中拿到系统调用号之后,会到系统调用表中找到该系统调用所对应的入口函数地址,然后执行该函数。
3. 函数的入口地址在在syscall.c中。
4. 中断处理中读取中断号及参数,然后找到中断服务例程并执行,退出中断后进行堆栈切换,返回用户态,继续执行用户程序。














你可能感兴趣的:(Linux系统调用-扒开系统调用的三层皮(下))