ID:298
原创作品转载请注明出处
本实验来源 https://github.com/mengning/linuxkernel/
实验环境
ubuntu 18.04 虚拟机
VMware workstation 14 Player
实验目的
举例跟踪分析Linux内核5.0系统调用处理过程
编译内核5.0
qemu -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img
选择系统调用号后两位与您的学号后两位相同的系统调用进行跟踪分析
https://github.com/mengning/menu
给出相关关键源代码及实验截图,撰写一篇博客(署真实姓名或学号最后3位 编号),并
在博客文章中注明“原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/ ”, 博客内容的具体要求如下:
题目自拟,内容围绕系统调用进行;
博客中需要使用实验截图
博客内容中需要仔细分析系统调用、保护现场与恢复现场、系统调用号及参数传递过程
总结部分需要阐明自己对系统调用工作机制的理解。
实验步骤
1、下载Linux5.0内核源码
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.0.tar.xz
在当前目录解压
cd linux-5.0
make menuconfig,找到kernel hacking,->Compile-time checks and compiler options,选择 [*]compile the kernel with debug info
(执行过程中若发生错误,根据提示进行安装:
sudo apt-get install libncurses-dev
sudo apt-get install flex
sudo apt-get install bison
sudo apt-get install libssl-dev)
make -4j
2、制作根文件系统
git clone https://github.com/mengning/menu.git
mkdir rootfs
cd menu
gcc -o init linktable.c menu.c test.c -m32 -static -lpthread
cd ../rootfs
cp ../menu/init ./
find init . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
3、启动Menu OS
qemu-system-x86_64 -kernel ../arch/x86/boot/bzImage -initrd ../rootfs.img
4、跟踪系统调用
1.查询系统调用表,确定分析的系统调用
由于未找到98号系统调用函数的相关信息,所以我选择了82号系统调用函数来代替。
2.在MenuOS的test.c中插入 select系统调用的代码
int Select(int argc, char *argv[]){
fd_set rfds;
struct timeval tv;
int retval;
FD_ZERO(&rfds);
FD_SET(0, &rfds);
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(1, &rfds, NULL, NULL, &tv);
if (retval == -1)
perror("select()");
else if (retval)
printf("Data is available now.\n");
else
printf("No data within five seconds.\n");
return 0;
}
int main()
{
PrintMenuOS();
SetPrompt("MenuOS>>");
MenuConfig("version","MenuOS V1.0(Based on Linux 3.18.6)",NULL);
MenuConfig("quit","Quit from MenuOS",Quit);
MenuConfig("time","Show System Time",Time);
MenuConfig("time-asm","Show System Time(asm)",TimeAsm);
MenuConfig("select","select xxx",Select);
ExecuteMenu();
}
3.重新编译MenuOS,执行Select函数
cd menu
gcc -pthread -o init linktable.c menu.c test.c -m32 -static
cd ../rootfs
cp ../menu/init ./
find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
4.通过gdb追踪调用过程
cd ~/LinuxKernel/linux-5.0.1
sudo gdb
# 在gdb中输入下面命令
file vmlinux
target remote:1234
# 打断点
b do_select
# 继续运行
c
系统调用过程分析:
在使用int 0x80中断之后,CPU会运行arch/x86/entry/entry_32.S中的指令
//这段代码就是系统调用处理的过程,其它的中断过程也是与此类似
//系统调用就是一个特殊的中断,也存在保护现场和回复现场
ENTRY(system_call)//这是0x80之后的下一条指令
RING0_INT_FRAME # can't unwind into user space anyway
ASM_CLAC
pushl_cfi %eax # save orig_eax
SAVE_ALL//保护现场
GET_THREAD_INFO(%ebp)
# system call tracing in operation / emulation
testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
jnz syscall_trace_entry
cmpl $(NR_syscalls), %eax
jae syscall_badsys
syscall_call:
// 调用了系统调用处理函数,实际的系统调用服务程序
call *sys_call_table(,%eax,4)//定义的系统调用的表,eax传递过来的就是系统调用号,在例子中就是调用的systime
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//退出之前,syscall_exit_work
//进入到syscall_exit_work里边有一个进程调度时机
restore_all:
TRACE_IRQS_IRET
restore_all_notrace://返回到用户态
#ifdef CONFIG_X86_ESPFIX32
movl 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), %ah
movb PT_CS(%esp), %al
andl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax
cmpl $((SEGMENT_LDT << 8) | USER_RPL), %eax
CFI_REMEMBER_STATE
je ldt_ss # returning to user-space with LDT SS
#endif
restore_nocheck:
RESTORE_REGS 4 # skip orig_eax/error_code
irq_return:
INTERRUPT_RETURN//iret(宏),系统调用过程到这里结束
实验总结
通过这次试验,我进一步学习了系统调用的相关知识。系统调用就是一种用户态到内核态最后再到用户态的一种过程。调用一个系统调用时,执行int 0x80指令触发系统调用后系统从用户态进入内核态,system_call()函数通过系统调用号查找系统调用表来查找具体系统调用服务进程。内核态通过汇编代码直接操作内存和寄存器,由于涉及上下文的切换问题,在进入中断程序前,我们需要利用堆栈保护现场,完成了系统调用之后,从堆栈中获得数据恢复现场。系统调提供了统一的接口。因为系统调用、异常和中断是外界进入内核的仅有通道,这就保证了不同进程对内核空间的操作是可知并可控的,这为OS多任务调度和虚拟内存实现提供了基础。