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

配置VSCode环境与跟踪start_kernel函数 开发环境:Ubuntu 18.04(更高版本如22.04会在编译源码时发生错误)

  • 安装开发工具
  • 下载内核源码
  • 配置内核选项
  • 编译和运行内核
  • 制作根文件系统
  • gdb调试
  • 安装VSCode进行linux内核调试
  • 跟踪分析

安装开发工具

sudo apt install build_essential

sudo apt install qemu

sudo apt install libncurses5-dev bison flex libssl-dev libelf-dev//编译依赖

下载内核源码

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

配置内核选项

make deconfig //Default configuration is based on 'x86_64_defconfig'
//在这里若使用的ubuntu版本不是18,则默认配置失败
make menuconfig//打开debug相关选项

显示界面如下
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第1张图片
按Enter回车进入“—>”和Space空格键改动“[ ] [*]”来分别进行下面设置:

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
# 编译过程较久
# 测试一下内核能不能正常加载运行,因为没有文件系统最终会kernel panic
qemu-system-x86_64 -kernel arch/x86/boot/bzImage

编译的过程会比较久,需要耐心等候
在这里插入图片描述

编译完成后进行测试,测试结果如下:
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第2张图片
出现了Kernel Panic

制作根文件系统

电脑加电启动首先由bootloader加载内核,内核紧接着需要挂载内存根文件系统,其中包含必要的设备驱动和工具,bootloader加载根文件系统到内存中,内核会将其挂载到根目录/下,然后运行根文件系统中init脚本执行一些启动任务,最后才挂载真正的磁盘根文件系统。

首先从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
#记得要编译成静态链接,不用动态链接库。

基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第3张图片
Settings —>
[*] Build static binary (no shared libs)静态编译

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

make -j$(nproc) && make install

回到linux-5.4.34文件夹,然后制作内存根文件系统镜像,大致过程如下:

cd ../linux-5.4.34

mkdir 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文件:(使用touch init 创建init脚本文件,再“vim init”打开init文件,再按i进入插入模式,粘贴进去,再按ESC进入命令模式,:wq保存并退出即可。)

#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo "Wellcome LHYOS!"
echo "--------------------"
cd home
/bin/sh

其中mount命令是对proc和sys进行挂载。

接下来对脚本添加可执行权限:

chmod +x init

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

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

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

qemu-system-x86_64 -kernel ./arch/x86/boot/bzImage -initrd rootfs.cpio.gz 

基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第4张图片
可以观察到init脚本执行成功,可以进行gdb调试。

gdb调试

执行以下语句运行init脚本

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跟踪分析_第5张图片
观察到内核运行停止,此时打开另一个终端,输入命令:

gdb vmlinux

基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第6张图片
此时可以使用gdb进行跟踪

安装VSCode进行linux内核调试

首先在linux系统下载vscode安装包,打开ubuntu自带的火狐浏览器下载:官方下载地址,选择.deb下载选项,这里我下载的是:
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第7张图片
然后执行安装命令 sudo apt install ./.deb,我的文件对应的安装命令是:

sudo apt install ./vscode.deb
    还要安装VSCode插件C/C++ Intellisense和C/C++ Themes。由于插件C/C++ Intellisense需要GNU Global,还需要使用如下命令安装GNU Global。
sudo apt install global

在vscode中安装这几个扩展
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第8张图片
准备在vscode中打开前面准备好的linux-5.4.34文件夹,需要做两件事情

  1. 命令行输入以下
python ./scripts/gen_compile_commands.py

这里的解释是:由于 Linux 内核高度定制化,所以没有办法直接通过配置 includePath 等让 Intellisense 正常提示,这里借助一个 Python 脚本来生成 compile_commands.json 文件帮助 Intellisense 正常提示(包括头文件和宏定义等)。在Linux源代码目录下直接运行如下命令就可以生成 compile_commands.json 了。

  1. 需要在linux-5.4.34文件夹下在此之前直接先手动放好配置文件:新建一个.vscode文件夹,(需要点击ctrl+h显示隐藏文件)把配置文件里的文件全部放入.vscode文件夹内:

基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第9张图片
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第10张图片
接下来vscode中的隐藏.vscode文件夹出现
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第11张图片
同时对task.json中的command进行更改
在这里插入图片描述
还有setting.json里最后这个"Disabled"按照我的vscode提示改成了"disabled"
在这里插入图片描述

跟踪分析

因为linux内核的起点是"start_kernel"函数,因此先在start_kernel处打断点:点击运行和调试图标,在断点里增加函数断点:start_kernel
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第12张图片

点击运行–启动调试,从start_kernel开始进行跟踪分析。
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第13张图片
可以看到程序执行停在了start_kernel处
点击单点跳过,这里我们看到了0号进程init_task被设置整个系统的第一个进程(0进程是手工创建的,其他进程都是0号进程创建的)在内核引导时,init_task会被创建并启动,它是所有其他进程的起点。

继续跳过,start_kernel会继续执行一些初始化操作,包括初始化各种重要的数据结构、驱动程序、中断处理程序等。在这个阶段,内核会建立好一些必要的核心数据结构,如物理内存管理器、虚拟内存管理器,以及进程调度器等。
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第14张图片
最后来到start_kernel的结尾arch_call_reset_init(),这个点开这个函数的定义是执行了reset_init()函数,因此我们再设置一个函数断点"reset_init",继续点击单点跳过发现就进入了reset_init函数内部。这个函数是由0号进程执行的。
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第15张图片
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第16张图片
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第17张图片
继续执行,这时候遇到了kernel_init,即1号进程,它是所有用户进程的祖先,由kernel_thread函数创建,kernel_thread函数创建一个新的内核线程(实际linux不支持线程所以是一个内核进程),该线程的入口地址是kernel_init()函数。

点开kernel_thread函数的定义
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第18张图片
发现kernel_thread函数是通过_do_fork函数来创建进程的。
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第19张图片
_do_fork函数主要完成了调用copy_process()复制父进程、调用wake_up_new_task将子进程加入就绪队列等待调度执行等。
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第20张图片
这里copy_process点进去还有调用dup_task_struct函数等过程,放上do_fork的过程,借用老师ppt的图一目了然:
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第21张图片
接下来在新线程(进程)中运行kernel_init()函数, 点开kernel_init()函数,可以看出,在这个里面调用了run_init_process函数:
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第22张图片

ramdisk_execute_command 可以通过 uboot 在环境变量中指定。execute_command也是一个全局的 char 指针变量,值也可以通过u-boot 传递。

所以这两个没有值就按顺序再尝试跳转到/sbin/init、/etc/init、/bin/init、/bin/sh去启动用户空间的init程序。而run_init_process函数则使用了do_execve函数:
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第23张图片

do_execve加载可执行文件,运行init程序,执行exec系统调用,

-> do_execve() –> do_execveat_common() -> __do_execve_file -> exec_binprm()-> search_binary_handler() -> load_elf_binary() -> start_thread()

基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第24张图片
这样1号进程就完成了用户态初始化。

接下来是2号进程的创建,2号进程是所有内核进程的祖先,kernel_thread创建了2号进程,同时进程执行的函数是kthreadd:
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第25张图片
查看kthreadd函数,做完一些初始化设置后,发现2号进程在不停地查看是否有需要创建的内核进程,如果kthread_create_list不空即有需要创建的内核进程,则就创建否则就执行schedule函数
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第26张图片
基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第27张图片
reset_init函数继续执行,最后调用了cpu_startup_entry,设置断点查看这个函数,发现它最后执行了while(1)无限循环,循环内执行do_idle函数,总结就是:

  • 在do_idle()中,代码会不断地轮询,判断当前系统是否需要调度,如果系统当前不需要调度,则进入到idle状态。

基于VSCode的Linux内核搭建以及start_kernel跟踪分析_第28张图片
这样循环的意义就是:调度系统负责考评系统中所有的进程。只要有哪个需要被运行,调度系统就会终止cpu_idle死循环进程(空闲进程)转而去执行有意义的干活的进程。

以上start_kernel分析完毕

你可能感兴趣的:(linux内核,linux,vscode,ubuntu)