《linux内核原理与分析》第六周作业
这个作业属于哪个课程 | 2020-2021-1 Linux内核原理与分析 |
这个作业要求在哪里 | 2020-2021-1Linux内核原理与分析第六周作业 |
这个作业的目标 | 给MenuOS添加上周实现的系统调用、使用gdb分析sys_time执行过程、了解系统调用执行过程 |
作业正文 | 本博客链接 |
一、实验相关
1.将上周实现的系统调用添加到MenuOS中
使用如下代码克隆新的MenuOs,如图看到新的MenuOs中包含了time命令和time-asm命令
git clone https://github.com/mengning/menu.git
make rootfs
然后给MenuOs添加上周编写的系统调用,代码如下:
int helloworld(int argc,char* argv[]){
char message[] = {"hello world 20209317
"};
int length = 21;
write(1,message,length);
return 0;
}
使用MenuConfig将新的命令加入到MenuOs中:
重新执行make进行编译并运行,结果如下:
2.使用gdb跟踪系统调用内核函数sys_time
同第三周,使用qemu命令启动内核
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -S -s
新打开一个shell窗口,启动gdb,将内核加载进入gdb,通过端口1234进行调试,在start_kernel和sys_time两个函数处设置断点,然后在qemu中执行time指令发现并没有在断点处停止,又尝试time-asm指令,这次停止了,但是gdb中显示找不到相关文件夹所在位置,但是我在linux-3.18.6/kernel/time/time.c中找到了相关系统调用代码,在系统调用的头文件linux-3.18.6/arch/x86/syscalls/sys_call.tbl中找到了系统调用号和对应的time处理函数,这里猜测是因为现在使用的gcc编译器版本比实验要求的版本高太多,在编译的同时添加调试信息会更改系统调用名为32位系统调用以适配编译器
在这里手动打开time.c查看具体的调用实现,如下图:
3.分析sys_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 //切换到ring0即内核态
ASM_CLAC
pushl_cfi %eax # save orig_eax //将系统调用号放入eax寄存器中
SAVE_ALL //保存现场,将cs:eip,标志寄存器,用户栈顶地址,部分寄存器保存到堆栈中
GET_THREAD_INFO(%ebp) //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) //根据系统调用表,调取相应的系统调用处理函数,每个表项栈4个字节,所以这里要乘以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 //系统调用返回
二、实验收获
1.中断向量0x80和system_call终端服务程序入口的关系
用户态代码通过调用库函数执行系统调用,库函数中使用了syscall来触发系统调用,而系统在启动时,通过trap_init和其中的set_system_trap_gate函数进行初始化,在这里绑定了0x80和system_call,后面系统一旦执行init 0x80,CPU就会直接跳转到system_call函数中执行。
中断向量:是指中断服务程序入口地址的偏移量与段基值,一个中断向量占据4字节空间。中断向量表是8088系统内存中最低端1K字节空间,它的作用就是按照中断类型号从小到大的顺序存储对应的中断向量,总共存储256个中断向量。在中断响应过程中,CPU通过从接口电路获取的中断类型号(中断向量号)计算对应中断向量在表中的位置,并从中断向量表中获取中断向量,将程序流程转向中断服务程序的入口地址。
2.系统调用执行过程
系统调用执行流程图如下: