基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析

目录

1、安装开发工具

2、下载内核源代码

3、配置内核选项

 4、编译和运行内核

5、制作根文件系统

6、配置VSCode调试Linux内核 

 7、跟踪分析Linux内核的启动过程


1、安装开发工具

sudo apt install build-essential

sudo apt install qemu # install QEMU

sudo apt install libncurses5-dev bison flex libssl-dev libelf-dev

2、下载内核源代码

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

内核源码文件如下:

3、配置内核选项

make defconfig # Default configuration is based on 'x86_64_defconfig'

make menuconfig

# 打开debug相关选项
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)

 4、编译和运行内核

make -j$(nproc) # nproc gives the number of CPU cores/threads available
# 测试一下内核能不能正常加载运行,因为没有文件系统最终会kernel panic

qemu-system-x86_64 -kernel arch/x86/boot/bzImage

基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析_第1张图片

5、制作根文件系统

  •  •电脑加电启动首先由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
#记得要编译成静态链接,不用动态链接库。

Settings  --->
    [*] Build static binary (no shared libs)

然后编译安装,默认会安装到源码目录下的 _install 目录中

make -j$(nproc) && make install

然后制作然后制作内存根文件系统镜像,大致过程如下:

mkdir rootfs
#需要到linux-5.4.34文件夹下来创建rootfs文件夹

cd rootfs

cp ../busybox-1.31.1/_install/* ./ -rf

mkdir dev proc sys home

sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} dev/

准备init脚本文件放在根文件系统跟目录下(rootfs/init),添加如下内容到init文件:

#!/bin/sh
mount -t proc none /proc #mount命令是对proc和sys进行挂载
mount -t sysfs none /sys
echo "Wellcome MengningOS!"
echo "--------------------"
cd home
/bin/sh
#给init脚本添加可执行权限
chmod +x init

 打包成内存根文件系统镜像

find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz 

测试挂载根文件系统,看内核启动完成后是否执行init脚本

qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz 
# 改成了:
qemu-system-x86_64 -kernel ./arch/x86/boot/bzImage -initrd rootfs.cpio.gz

基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析_第2张图片

 •下面具体看看如何使用gdb跟踪调试Linux内核。使用gdb跟踪调试内核,加两个参数,一个是-s,在TCP 1234端口上创建了一个gdb-server。可以另外打开一个窗口,用gdb把带有符号表的内核镜像vmlinux加载进来,然后连接gdb server,设置断点跟踪内核。若不想使用1234端口,可以使用-gdb tcp:xxxx来替代-s选项),另一个是-S代表启动时暂停虚拟机,等待 gdb 执行 continue指令(可以简写为c)。

 

qemu-system-x86_64 -kernel ./arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s
#其中-S意思是Stopped,-s为gdb提供一个调试端口tcp:1234。
# 纯命令行下启动虚拟机

基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析_第3张图片

再打开一个窗口,启动gdb,把内核符号表加载进来,建立连接

cd linux-5.4.34/

gdb vmlinux

(gdb) target remote:1234

(gdb) b start_kernel

c、bt、list、next、step....

6、配置VSCode调试Linux内核 

先下载vscode,并装上以下插件

基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析_第4张图片

在vscode中打开前面准备好的linux-5.4.34文件夹前,需进行配置

在linux-5.4.34文件夹下新建一个.vscode文件夹,把配置文件里的文件全部放入.vscode文件夹内:

基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析_第5张图片

接下来就可以在vscode里打开linux-5.4.34文件夹:可以看到.vscode配置文件夹已经出现了

基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析_第6张图片

 7、跟踪分析Linux内核的启动过程

因为linux内核的起点是"start_kernel"函数,因此先在start_kernel处打断点,从start_kernel开始进行跟踪分析:

基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析_第7张图片

 接着一边stepover一边观察start_kernel执行的一些初始化操作,首先第一步会跳转到set_task_stack_end_magic(&init_task);     即创建0号进程init_task进程。

0号进程init_task被设置整个系统的第一个进程(0进程是手工创建的静态进程,其他进程都是0号进程创建的)在内核引导时,init_task会被创建并启动,它是所有其他进程的祖先。

 接下来start_kernel依次执行内核各个重要子系统的初始化,比如cpu、驱动程序、中断处理程序、物理内存管理器、虚拟内存管理器等等

基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析_第8张图片

 最后来到结尾处arch_call_rest_init(),然后接着在此处打上断点,stepinto, 接着调试:

然后进入到res_init(void)函数中,发现rest_init函数调用kernel_thread函数,启动了2个内核线程:根据注释可知,这里要先生成kernel_init,此为进程1 ,它是所有用户进程的祖先     

基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析_第9张图片

点开kernel_thread函数的定义,发现kernel_thread函数是通过_do_fork函数来创建进程的。

基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析_第10张图片

 再查看_do_fork()函数,发现_do_fork函数主要完成了调用copy_process()复制父进程、调用wake_up_new_task将子进程加入就绪队列等待调度执行等行为来创建进程。

基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析_第11张图片

 接下来查看kernel_init()函数:按顺序执行/init可执行文件,完成用户态初始化

基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析_第12张图片

 在linux内核中默认指定了4个用户空间的程序:/sbin/init/etc/init/bin/init/bin/sh。调用try_to_run_init_process()函数启动它们。如果其中的任意一个程序被启动了,则退出。

基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析_第13张图片

 接下来是2号进程的创建,2号进程是所有内核进程的祖先,kernel_thread创建了2号进程,同时进程执行的函数是kthreadd()。

基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析_第14张图片

 查看kthreadd()函数,此函数循环设置当前进程的状态为TASK_INTERRUPTIBLE是可以中断的,判断kthread_create_list是不是为空,如果为空则就调度出去,让出cpu,如果不为空,则从链表取出一个,然后调用kthread_create去创建一个内核线程。

基于VSCode的Linux内核调试环境搭建以及start_kernel跟踪分析_第15张图片

 

 

 

 

 

 

 

你可能感兴趣的:(ubuntu,linux,服务器)