跟踪分析Linux内核5.0系统调用处理过程

学号后三位:069

原创作品转载请注明出处 + https://github.com/mengning/linuxkernel/

一、实验内容

主要分为两部分:

  1. 升级内核为5.0
  2. 分析系统调用的处理过程

二、升级内核

升级前版本为:4.19.4
在这里插入图片描述

下载内核源码:https://github.com/mengning/linux/tree/v5.0
在这里插入图片描述

将文件解压,进入Linux-5.0目录下,输入如下指令:
sudo apt install libssl-dev(可能还需安装其他依赖)
在这里插入图片描述

输入命令make mrproper和sudo make menuconfig配置内核选项,出现内核配置图形界面:
在这里插入图片描述

可根据需求裁剪相关模块,保存后退出;编译:
sudo make bzImage -j8
在这里插入图片描述

进入Linux-5.0/arch/x86/boot目录下,可看到编译好的文件:
在这里插入图片描述

make modules_install #把编译出的内核模块复制到/lib/modules/${KERNEL_VERSION}
make install 安装内核:
在这里插入图片描述

内核升级完成,修改grub,将新内核作为默认启动项:
sudo ln -s System.map-5.0.2 System.map
在这里插入图片描述

生成文件系统:
sudo mkinitramfs -o /boot/initrd.img-5.0.2
在这里插入图片描述

更改grub:
sudo gedit /etc/default/grub
作出如下修改:
在这里插入图片描述

保存后退出, sudo update-grub.

重启(按住shift),可选择新内核——5.0.2版本(官网最新版)
在这里插入图片描述

至此,内核升级部分完成。

三、ASM和C代码使用系统调用

根据要求,我需要分析的系统调用为第69号系统调用:ssetmask
在这里插入图片描述

首先配置环境:
cd LinuxKernel
rm -rf menu
git clone https://github.com/mengning/menu.git

在这里插入图片描述

修改test.c,加入ssetmask函数代码(C版本和asm版本):

在这里插入图片描述

增加命令:
在这里插入图片描述

make rootfs
在这里插入图片描述

下面以调试方式启动qemu:
qemu -kernel linux-5.0.2/arch/x86/boot/bzImage -initrd rootfs.img -s -S
在这里插入图片描述

再打开一个终端,进行gdb调试:(中间,实验楼奔溃了,又重新来了一次)
file linux-5.0.2/vmlinux
target remote:1234
在这里插入图片描述

分析system_call()函数:

首先把系统调用号和这个异常处理程序可以用到的所有CPU寄存器保存到相应的栈中,不包括由控制单元已自动保存的eflags、cs、eip、ss和esp寄存器。

pushl %eax
SAVE_ALL
movl $0xffffe000, %ebx /* or 0xfffff000 for 4-KB stacks */
andl %esp, %ebx

接下来检查thread_info结构flag字段的TIF_SYSCALL_TRACE和TIF_SYSCALL_AUDIT标志之一是否被置为1,即检查是否有某一调试程序正在跟踪执行程序对系统调用的调用。如果系统调用号无效则把-ENOSYS值存放在栈中曾保存eax寄存器的单元中,当进程恢复在用户态的执行时会在eax中得到一个负的返回码。

cmpl $NR_syscalls, %eax
jb nobadsys
movl $(-ENOSYS), 24(%esp)
jmp resume_userspaces

最后调用与eax中所包含的系统调用号对应的特定服务例程。

call *sys_call_table(0, %eax, 4)

四、分析系统调用

保护现场与恢复现场

当调用系统调用时,会陷入内核态,此时就涉及了上下文切换问题。当用户切换到内核态时,就要把用户态寄存器上下文保存起来,同时把内核态寄存器的值放到当前CPU中。int指令触发中断机制会在堆栈上保存一些寄存器的值,会保存用户态栈顶地址、当时的状态字、当时的CS:EIP的值。并且CS:EIP寄存器的值会指向中断处理程序的入口,对于系统调用来讲是指向system_call等等如此事务。
最后的iret与中断信号发生时的动作相反,也就是恢复现场的操作。

系统调用号及参数传递过程

Linux操作系统有200多个系统调用,内核就是通过系统调用号来给这些systemcall来编号,加以区分,以明确知道用户想要执行的是哪个系统调用。用户在使用系统调用时需要利用EAX寄存器传递一个名为系统调用号的参数。

除了系统调用号外,系统调用也可能需要传递参数,普通函数调用时通过将参数入栈的方式传递的。而系统调用,涉及从用户态到内核态,堆栈不同,无法用此方式传递参数。所以,系统调用的参数传递方式是通过寄存器来传递的。主要有EBX\ECX\EDX\ESI\EDI\EBP,六个寄存器。如果所需传递的参数超过6个,则可用寄存器存储内存地址,用内存来存储更多参数。

五、总结

系统调用工作机制

如下图所示:
在这里插入图片描述

User Mode表示用户态,Kernel Mode表示内核态。
xyz()史一个API函数,封装了一个系统调用,会触发int 0x80中断,对应system_call内核代码的起点,也就是中断服务历程的入口,内部有sys_xyz ()处理函数,执行完后会ret_from_sys_call,这里是进程调度的时机。总的来说,系统调用的三层机制为:

  • xyz()
  • system_call
  • sys_xyz()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值