作业信息
这个作业属于哪个课程 | <2020-2021-1Linux内核原理与分析)> |
---|---|
这个作业要求在哪里 | <2020-2021-1Linux内核原理与分析第六周作业> |
这个作业的目标 | 使用gdb跟踪系统调用内核函数sys_time,在system_call汇编代码中的系统调用内核处理函数,整体上理解系统调用的内核处理过程 |
作业正文 | https://www.cnblogs.com/baoxiyuan/p/13950953.html |
1.给MenuOS增加命令
首先强制删除当前的menu目录,然后用git clone重新克隆一个新版本的menu。
代码如下:
rm -rf menu
geit clone https://github.com/mengning/menu.git
make rootfs
查看test.c会发现里面增加了两行代码,一个是MenuConfig("time"),另一个是MenuConfig("time-asm")
2.使用gdb跟踪系统调用内核函数sys_time
首先返回LinuxKernel目录下,然后启动内核。先启动gdb,将3.18.6的内核加载进来,之后连接到target remote1234.
代码如下:
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -S -s
file linux-3.18.6/vmlinux
target remote:1234
在start_kernel处设置断点,在sys_time处设置断点。time命令执行到一半会停住。
当使用finish命令将这个函数全部执行完后,再单步执行,一直到return i,获得的就是当前系统时间time的数值。
system_call的实现位于linux-3.18.6/arch/x86/kernel/entry_32.S#490,但是当当MenuOs中执行time-asm时
会突然会停在sys_time位置。
3.在system_call汇编代码中的系统调用内核处理函数
ENTRY(system_call)
RING0_INT_FRAME
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:
movl TI_flags(%ebp), %ecx
testl $_TIF_ALLWORK_MASK, %ecx # 检测是否所有工作已完成
jne syscall_exit_work # 未完成,则去执行这些任务
restore_all:
TRACE_IRQS_IRET # iret 从系统调用返回
irq_return:
Interrupt_RETURN
int 0x80和system_call是通过中断向量匹配起来的,而系统调用用户态接口和系统调用的内核
处理函数是通过系统调用号匹配起来的。
sys_call_table是一个系统调用的表,eax寄存器传递的系统调用号,使用者在调用它时会根据
eax寄存器值来调用对应的系统调用,内核处理函数。eax寄存器是13,即在调用完sys_time之后需要
先保存它的返回值,在退出之前根据需要有一个syscall_exit_work,如果没有,则恢复现场并iret
返回用户态。一旦进入syscall_exit_work,里面会有一个进程调度实际,这也是最常见的进程调度时机。