lab3:基于VS Code的Linux内核调试环境搭建及start_kernel跟踪分析

文章详细介绍了如何编译Linux内核,包括下载源码、打补丁、配置和编译。同时,讨论了内核启动过程,特别是start_kernel函数的作用,以及内核如何通过BusyBox构建内存根文件系统。在调试方面,提到了设置断点进行内核调试的方法,分析了进程创建的过程,如kernel_init和kthreadd的生成。
摘要由CSDN通过智能技术生成

内核准备

内核和相关环境

wget https://raw.github.com/mengning/mykernel/master/mykernel-2.0_for_linux-5.4.34.patch
sudo apt install axel
axel -n 20 https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.4.34.tar.xz
xz -d linux-5.4.34.tar.xz
tar -xvf linux-5.4.34.tar
cd linux-5.4.34
patch -p1 < ../mykernel-2.0_for_linux-5.4.34.patch
sudo apt install build-essential gcc-multilib
sudo apt install qemu # install QEMU
sudo apt install libncurses5-dev bison flex libssl-dev libelf-dev

内核配置

make defconfig # Default configuration is based on 'x86_64_defconfig'
make menuconfig

打开debug相关选项 输入y和n进行选中或取消选中

Kernel hacking  --->
    Compile-time checks and compiler options  --->
        [*] Compile the kernel with debug info
        [*]   Provide GDB scripts for kernel debugging
 [*] Kernel debugging

关闭KASLR,否则会导致打断点失败

Processor type and features ---->
    [] Randomize the address of the kernel image (KASLR)

编译内核

make -j$(nproc) # nproc gives the number of CPU cores/threads available

制作根文件系统

电脑加电启动首先由bootloader加载内核,内核紧接着需要挂载内存根文件系统,其中包含必要的设备驱动和工具,bootloader加载根文件系统到内存中,内核会将其挂载到根目录/下,然后运行根文件系统中init脚本执行一些启动任务,最后才挂载真正的磁盘根文件系统。我们这里为了简化实验环境,仅制作内存根文件系统。这里借助BusyBox 构建极简内存根文件系统,提供基本的用户态可执行程序。

首先从https://www.busybox.net下载 busybox源代码解压,解压完成后,跟内核一样先配置编译,并安装。

axel -n 20 https://busybox.net/downloads/busybox-1.31.1.tar.bz2
tar -jxvf busybox-1.31.1.tar.bz2
cd busybox-1.31.1
make menuconfig

编译安装:

make -j$(nproc) && make install

新建一个.vscode文件夹,将配置文件放入该文件夹内,配置如下五个文件:c_cpp_properties.json,init,launch.json,settings.json,tasks.json。
在这里插入图片描述

start_kernel分析

Linux内核的起点是start_kernel函数。因此,我们可以在start_kernel处设置一个断点,启动调试,程序将在该断点处暂停,从start_kernel开始进行跟踪分析。通过逐步执行进行跟踪分析,我们可以发现0号进程init_task被设为整个系统的初始进程。也就是说,0号进程是手动创建的,其他进程都是由0号进程创建的。在内核引导时,init_task会被创建并启动,它是所有其他进程的起点。
在这里插入图片描述

我们继续逐步执行,start_kernel函数执行各种初始化操作。

在start_kernel函数的末尾,我们可以看到arch_call_rest_init()函数体内调用了rest_init()函数。因此,我们可以在rest_init函数内设置一个断点,进入其函数体内部。这个函数由0号进程执行。
在这里插入图片描述

kernel_thread函数创建了kernel_init进程,对应1号进程,它是所有用户进程的祖先。

然后,kernel_thread函数创建了kthreadd进程,对应2号进程,它是所有内核进程的祖先。

我们进入kernel_thread函数查看,该函数通过do_fork函数来创建进程。通过kernel_thread函数的代码可以看出,1号进程和2号进程最终都是通过do_fork创建的。用户空间通过系统调用fork来创建进程,最终也是通过do_fork来完成的。

进程的创建过程大致是这样的:父进程通过fork系统调用进入内核_do_fork函数,复制进程描述符及相关进程资源(采用写时复制技术),分配子进程的内核堆栈,并对内核堆栈和进程的关键上下文进行初始化,最后将子进程放入就绪队列。fork系统调用返回后,子进程根据设置的内核堆栈和进程的关键上下文开始执行。

我们查看kernel_init函数的定义,可以看到它会调用run_init_process函数。
在这里插入图片描述

run_init_process函数的do_execve函数用于加载可执行文件、运行init程序并执行exec系统调用。1号进程完成了用户空间的初始化。

我们再来查看kthreadd函数的定义。2号进程被创建并完成初始化。
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值