第五课-系统调用跟踪

来源:互联网 发布:淘宝怎么搜福利 编辑:程序博客网 时间:2024/05/18 00:00

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


本节课的实验内容主要是跟踪系统调用过程,理解系统调用的整个过程。重点分析系统调用的保存与返回流程。

我从menu开始做起。

  1. 修改menu,添加时间系统调用

修改test.c源代码,增加系统调用函数time的函数如下:

int ShowTime(void){time_t tt;struct tm * t;//get time nowtt = time(NULL);t = localtime(&tt);//show timeprintf("time:%d:%d:%d:%d:%d:%d\n",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);return 0;}int ShowTimeAsm(void){time_t tt;struct tm * t;//get time now//tt = time(NULL);asm volatile("mov $0,%%ebx\n\t""mov $0xd,%%eax\n\t""int $0x80\n\t""mov %%eax,%0\n\t":"=m"(tt));t = localtime(&tt);//show timeprintf("time:%d:%d:%d:%d:%d:%d\n",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);return 0;}int main(){    PrintMenuOS();    SetPrompt("MenuOS>>");    MenuConfig("version","XXX V1.0(Menu program v1.0 inside)",NULL);MenuConfig("time","show time",ShowTime);MenuConfig("time-asm","show time from asm",ShowTimeAsm);    MenuConfig("quit","Quit from XXX",Quit);        ExecuteMenu();}
说明:注意头文件time.h的调用。

在muconfig中使用time就能打印输出当前的时间;输入time-asm就能打印输出当前的时间。

在menu菜单下重新编译,编译指令如下:

gcc -o init linktable.c menu.c test.c -m32 -static -lpthread

因为要生成init可执行程序才能在系统最后被调用,因为当前文件系统中没有其他库的支持,因此程序依赖的动态库必须使用静态库来编译到内核中。

  • 2.制作根文件系统

拷贝menu中生成的init可执行程序到rootfs中

到rootfs文件目录下执行如下语句:

find . | cpio -o -Hnewc | gzip -9 > ../rootfs.img

将会生成新的rootfs.img镜像文件


  • 3.跟踪内核

执行如下语句:

打开一个命令行终端,输入如下语句,执行内核

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

内核执行会弹出新的显示终端。

重新打开一个新的命令行终端:

在此,调试内核

输入gdb

在gdb下进入Linuxkernel的当前目录下

file linux3.18.6/vmlinux

target remote:1234

b start_kernel

b sys_time 在time的系统调用处设置断点,在后面就没有机会设置了,因为无法让内核重新停止了。

c

此时内核运行到start_kernel,也就是内核的入口语句处。

list

可查看当前语句

c

内核继续运行将会进入menu语句中。

输入time后回车

可在gdb中看到sys_time的系统调用停止语句。

内核执行的图片显示:



问题:

在实际操作的过程中,总是会出现运行了time之后,系统停止的时候,就不能再响应鼠标了。结果导致我不得不停止试验,再来一次。代码必须重新写一次。非常慢。

停止的位置如下:


分析代码

虽然此时再也无法响应鼠标了,但是仍然可以人为的分析代码。

代码进入了kernel/time/time.c中第63行,代码如下:

SYSCALL_DEFINE1(time, time_t __user *, tloc){time_t i = get_seconds();if (tloc) {if (put_user(i,tloc))return -EFAULT;}force_successful_syscall_return();return i;}

系统调用,获得秒的时间值。根据老师课程讲解,函数返回将不会看到。


  • 分析sys_call这个调用

源文件如下:


ENTRY(system_call)//只要系统执行了int 0x80的时候,此处就会执行
RING0_INT_FRAME# can't unwind into user space anywayASM_CLACpushl_cfi %eax# save orig_eaxSAVE_ALL://保护现场GET_THREAD_INFO(%ebp)# system call tracing in operation / emulationtestl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)jnz syscall_trace_entrycmpl $(NR_syscalls), %eaxjae syscall_badsyssyscall_call:call *sys_call_table(,%eax,4)//系统调用表,eax就是系统调用号,会指向timesyscall_after_call:movl %eax,PT_EAX(%esp)# store the return value保存返回值syscall_exit:LOCKDEP_SYS_EXITDISABLE_INTERRUPTS(CLBR_ANY)# make sure we don't miss an interrupt 禁止中断时为了不要丢掉中断# setting need_resched or sigpending# between sampling and the iretTRACE_IRQS_OFFmovl TI_flags(%ebp), %ecxtestl $_TIF_ALLWORK_MASK, %ecx# current->workjne syscall_exit_work //会跳转到退出工作处restore_all:TRACE_IRQS_IRETrestore_all_notrace:
<pre name="code" class="objc">#ifdef CONFIG_X86_ESPFIX32movl 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), %ahmovb PT_CS(%esp), %alandl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eaxcmpl $((SEGMENT_LDT << 8) | USER_RPL), %eaxCFI_REMEMBER_STATEje ldt_ss# returning to user-space with LDT SS#endifrestore_nocheck:RESTORE_REGS 4# skip orig_eax/error_codeirq_return:INTERRUPT_RETURN

退出之前需要做的额外工作:

syscall_exit_work:
testl $_TIF_WORK_SYSCALL_EXIT, %ecx
jz work_pending   #跳转到work pending 处其实是处理信号和重新调度
TRACE_IRQS_ON
ENABLE_INTERRUPTS(CLBR_ANY)# could let syscall_trace_leave() call#使能中断和前文中禁止中断是相呼应的
# schedule() instead
movl %esp, %eax
call syscall_trace_leave
jmp resume_userspace//恢复到用户空间去
END(syscall_exit_work)

查看workPending的功能分析:
work_pending:
testb $_TIF_NEED_RESCHED, %cl #观察是否需要进一步的重新schedule,产生进程调度
jz work_notifysig#如果没有进程调度则进行信号处理
work_resched:#/发生进程调度的时候,调用schedule函数
call schedule
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
andl $_TIF_WORK_MASK, %ecx# is there any work to be done other
# than syscall tracing?
jz restore_all
testb $_TIF_NEED_RESCHED, %cl
jnz work_resched #可能产生返回的进程调度过程


work_notifysig: # deal with pending signals and
# notify-resume requests开始处理正在pending的信号
#ifdef CONFIG_VM86
testl $X86_EFLAGS_VM, PT_EFLAGS(%esp)
movl %esp, %eax
jne work_notifysig_v86# returning to kernel-space or
# vm86-space
1:
#else
movl %esp, %eax
#endif
TRACE_IRQS_ON
ENABLE_INTERRUPTS(CLBR_NONE)
movb PT_CS(%esp), %bl
andb $SEGMENT_RPL_MASK, %bl
cmpb $USER_RPL, %bl
jb resume_kernel
xorl %edx, %edx
call do_notify_resume
jmp resume_userspace


#ifdef CONFIG_VM86
ALIGN
work_notifysig_v86:
pushl_cfi %ecx# save ti_flags for do_notify_resume
call save_v86_state# %eax contains pt_regs pointer
popl_cfi %ecx
movl %eax, %esp
jmp 1b
#endif
END(work_pending)





总结:

本课程老师还是希望能够进一步的跟踪系统调用的过程,具体的了解如何执行的过程。注意进程调度的时机。

对信号处理和发生进程调度部分分析的还不够透彻,需要好好的分析。


0 0
原创粉丝点击