2020-2021-1 20209317《Linux内核原理与分析》第六周作业

《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\n"};
	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.系统调用执行过程

系统调用执行流程图如下:

posted @ 2020-11-11 22:08  20209317李明帅  阅读(126)  评论(0编辑  收藏  举报