2019-2020-1 20199315《Linux内核原理与分析》第六周作业
实验楼实验五
实验楼环境研究MenuOS
1.在MenuOS中添加并运行getpid命令
进入LinuxKernel目录,删除menu目录
$cd LinuxKernel
$ls
$rm -rf menu
$ls
从github上克隆一个menu,该版本中已经写了time和time_asm两个系统调用
$git clone https://github.com/mengning/menu.git
运行make rootfs对menu内文件进行编译,打开MenuOS输入help查询系统调用函数
$cd menu
$make rootfs
输入help命令,测试新加入的time以及time-asm
在menu目录中找到test.c文件打开(vim或gedit test.c),添加并写入getPid()方法,函数名应该避免与系统调用函数getpid()重复。
int getPid(int argc,char *argv[]){
int pid;
pid=getpid();
printf("pid=%d\n",pid);
return 0;
}
查看一下新扩展的两个函数,如橙色框所示;写入getPid()方法,如红色框所示
再次使用make rootfs对menu内文件进行编译,由于test.c中通过MenuConfig()定义了调用函数名,在MenuOS中直接输入getPid可以调用,获得当前进程号,该进程也叫根进程。它负责产生其他所有用户进程。所有的进程都会被挂在这个进程下。
实验中,发现不知道为什么,我的进程号大到如此惊人……
一看原来是函数里面写错了:
int getPid(int argc,char *argv[]){
int pid;
pid-getpid(); /* 是的,下面的printf中等号写成短横线就算了,这里都写错了2333 */
printf("pid-%d\n",pid);
return 0;
}
改完之后又出现了新的问题……
不知道咋搞= =,问了娄君也不得而知,索性关了重新编译了一次,没问题了……希望如果老师看到的话可以解疑QAQ
- gdb跟踪调用time函数的过程
退回LinuxKernel目录,
$cd ..
shift+ctrl+o切换横屏,然后在终端1将QEMU挂起
$qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -S -s
在终端2打开gdb,在start_kernel设置断点
$gdb
$file linux-3.18.6/vmlinux /* 在gdb界面中targe remote之前加载符号表 */
$target remote:1234 /* 建立gdb和gdbserver之间的连接,按c让qemu上的Linux继续运行 */
$b start_kernel /* 断点的设置可以在target remote之前,也可以在之后 */
又连接超时了……
过了会重启gdb又好了……
设置断点
程序执行到断点处
继续执行,执行time命令,可发现此命令执行到一半卡住了,出现了一个断点。继续执行,直到出现return i
设置断点(system_call),继续执行可发现time有返回
实验分析
system_call的代码:
ENTRY(system_call)
RING0_INT_FRAME
ASM_CLAC
pushl_cfi %eax /* 保存系统调用号 */
SAVE_ALL /* 可以用到的所有CPU寄存器保存到栈中 */
GET_THREAD_INFO(%ebp) /* ebp用于存放当前进程thread_info结构的地址 */
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) /* 合法,对照系统调用号在系统调用表中寻找相应服务例程 */
movl %eax,PT_EAX(%esp) /* 保存返回值到栈中 */
syscall_exit:
testl $_TIF_ALLWORK_MASK, %ecx /* 检查是否需要处理信号 */
jne syscall_exit_work /* 需要,进入 syscall_exit_work */
restore_all:
TRACE_IRQS_IRET /* 不需要,执行restore_all恢复,返回用户态 */
irq_return:
INTERRUPT_RETURN /* 相当于iret */
给MenuOS增加time和time-asm命令
命令:1、强制删除:rm menu -rf
2、克隆:git clone (后跟需要克隆数据所在的位置)
3、自动编译,自动生成根文件系统,并自动启动:make rootfs
给MenuOS增加time和time-asm命令步骤:
1、更新menu代码到最新版
2、在main函数中增加MenuCongfig
3、增加对应的time和time-asm函数
4、make rootfs
使用gdb跟踪调用内核函数sys-time
跟踪方法和上次所学内容一样:
注:sys_time返回后进入汇编代码处理,gdb无法继续进行追踪
执行int 0x80之后执行system_call对应的代码
系统调用在内核代码中的处理过程
1、系统调用机制的初始化
\init\main.c start_kernel
trap_init()
\arch\x86\kernel\traps.c
#fdef CONFIG_x86_32
set_system_trap_gate(SYSTALL_VECTOR,&system_call)(SYSTALL_VECTOR:系统调用的工作向量,&system_call:汇编代码的入口,系统执行int 0x80就跳转到该处执行)
set_bit(SYSCALL_VECTOR,used_vectors)
#endif
2、system_call伪代码:
system_call位置:/ arch/x86/kernel/entry_32.S中
系统调用是特殊的中断:存在保存现场和恢复现场
SAVE_ALL保存现场
restore_all恢复现场
call *sys_call_table(,%eax,4)调用系统调用函数:eax存的是系统调用号,根据系统调用号去找sys_call_table中的中断处理。
INTERRUPT_RETURN,就是iret。
分析system_call代码,系统调用的流程大致为:
总结:
发生中断时,CPU暂停执行当前程序,产生中断响应。中断响应主要工作是:
(1)中止当前程序的执行;
(2)保存原程断点现场
(3)转到相应的处理程序。
接着中断处理程序进行相应处理:
(1)保存中断程序现场
(2)分析中断原因,转入相应处理程序
(3)调用中断处理程序
(4)中断返回。
此为本人Linux学习第六周的内容,如有不足,还请批评指正,不胜感激。
以上