Linux5.0系统调用过程分析实验

学号085 原创作品转载请注明出处
本实验来源 https://github.com/mengning/linuxkernel/

实验简介

本实验要求举例跟踪分析Linux内核5.0系统调用处理过程,编译内核5.0,选择系统调用号后两位与您的学号后两位相同的系统调用进行跟踪分析,给出相关关键源代码及实验截图,仔细分析系统调用、保护现场与恢复现场、系统调用号及参数传递过程,阐明自己对系统调用工作机制的理解。

实验环境

  • Unbuntu 16
  • gcc 4.8

实验初探

1、下载Linux内核5.0内核代码,并配置编译Linux内核

mkdir LinuxKernel
下载内核源码:Linux内核5.0 source code并解压到LinuxKernel目录下
cd linux-5.0.2
make menuconfig,找到kernel hacking,->Compile-time checks and compiler options,选择 [*]compile the kernel with debug info
make

在这里插入图片描述
在这里插入图片描述
make时报错,错误和解决方案如下图(此处参考自 https://blog.csdn.net/zxhio/article/details/80312316)

在这里插入图片描述
2. 制作根文件系统

cd ~/LinuxKernel/
mkdir rootfs
git clone https://github.com/mengning/menu.git
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

实验中遇到的错误及解决方案如下图
在这里插入图片描述
在这里插入图片描述
运行结果
在这里插入图片描述
3. 启动MenuOS

qemu-system-i386 -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img

在这里插入图片描述

跟踪调试内核启动

qemu-system-i386 -kernel linux-5.0.1/arch/x86/boot/bzImage -initrd rootfs.img -S -s -append nokaslr

加-append nokaslr选项,是为了防止start_kernel断点断不住 详情见知乎 https://www.zhihu.com/question/270476360

cd LinuxKernel/linux-5.0.1
gdb vmlinux
(gdb) target remote:1234

输入help查询指令
在这里插入图片描述
系统调用过程

在这里插入图片描述
在这里插入图片描述

实验分析

首先,几乎所有的内核模块均会在start_kernel进行初始化。在start_kernel中,会对各项硬件设备进行初始化,包括一些page_address、tick等等,直到最后需要执行的rest_init中,会开始让系统跑起来。那rest_init这个过程中,会调用kernel_thread()来创建内核线程kernel_init,它创建用户的init进程,初始化内核,并设置成1号进程,这个进程会继续做相关的系统初始化。然后,start_kernel会调用kernel_thread并创建kthreadd,负责管理内核中得所有线程,然后进程ID会被设置为2。最后,会创建idle进程(0号进程),不能被调度,并利用循环来不断调号空闲的CPU时间片,并且从不返回。

本文参考自https://blog.csdn.net/pianogirl123/article/details/50866778

增加系统调用

根据学号后两位85,在/usr/include/asm/unistd_32.h中可查得#define __NR_readlink 85
在test.c中增加函数ReadLink()在这里插入图片描述

int ReadLink(int argc, char *argv[])
{
#define PATH_MAX 32
    char buf[PATH_MAX];
    int ret;
    ret = readlink("/proc/self/exe", buf, PATH_MAX);
    if (ret == -1)
        perror("readlink");
    else
        printf("%.*s\n", ret, buf);
}
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("ReadLink", "read link of /proc/self/exe", ReadLink);
    ExecuteMenu();
}

实验总结

  • 系统调用的触发及参数传递
    当调用一个系统调用时,CPU从用户态切换到内核态并开始执行一个system_call和系统调用内核函数。在Linux中通过执行int 0x80来触发系统调用,内核为每个系统调用分配一个系统调用号,用户态进程必须明确指明系统调用号,需要使用EAX寄存器来传递。
    系统调用可能需要参数,但是不能通过像用户态进程函数中将参数压栈的方式传递,因为用户态和内核态有不同的堆栈,必须通过寄存器的方式传递参数。
    概括:EAX用来传递系统调用号,EBX、ECX、EDX、ESI、EDI、EBP用来传递参数,若参数较多,则把指向内存的指针存入寄存器。
  • 系统调用流程
    系统调用机制初始化:
    \init\main.c start_kernel
trap_init();

\arch\x86\kernel\traps.c

#ifdef CONFIG_X86_32
//系统调用的中断向量和system_call汇编代码的入口
//一旦执行int 0x80,CPU就跳转到system_call这个位置来执行
set_system_trap_gate(SYSCALL_VECTOR, &system_call);
set_bit(SYSCALL_VECTOR, used_vectors); 
#endif

系统调用的工作机制,一旦start_kernel初始化好之后,在代码中一旦出现int 0x80的指令,就会立即跳转到system_call这个位置。
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传递过来的就是系统调用号
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(宏),系统调用过程到这里结束

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值