1. Configure VSCode
In order to prevent the problem of slow downloading through the virtual machine, choose to download the vscode installation package and upload the virtual machine through finalshell.
Execute the following installation command to install
sudo apt install ./code_1.76.2-1678817801_amd64.deb
Also install the VSCode plugins C/C++ Intellisense and C/C++ Themes. Since the plug-in C/C++ Intellisense requires GUN Global, the following command is also required to install GUN Global.
sudo apt install global
Then install the above plugin
2. Install development tools
sudo apt install build-essential sudo apt install qemu # install QEMU# as a virtual machine sudo apt install libncurses5-dev bison flex libssl-dev libelf-dev #Dependencies required to compile the kernel --- some basic libraries
3. Download kernel source code
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 #download xz -d linux-5.4.34.tar.xz tar -xvf linux-5.4.34.tar#Decompression cd linux-5.4.34
4. Configure kernel options
make defconfig # Default configuration is based on 'x86_64_defconfig' make menuconfig# open debug related options
Open the picture as shown below:
Change the following configuration:
Kernel hacking —>
Compile-time checks and compiler options —>
[*] Compile the kernel with debug info
[*] Provide GDB scripts for kernel debugging
[*] Kernel debugging
# Turn off KASLR (random address), otherwise the breakpoint will fail. In this way, the debugger can trace the source code. The reason why the random address is set is to prevent hacker attacks:
Processor type and features —->
[ ] Randomize the address of the kernel image (KASLR)
5. Compile and run the kernel
make -j$(nproc) # nproc gives the number of CPU cores/threads available # Test whether the kernel can be loaded and run normally, because there will be a kernel panic without a file system qemu-system-x86_64-kernel arch/x86/boot/bzImage
6. Create a root file system
When the computer is powered on, the kernel is first loaded by the bootloader, and then the kernel needs to mount the memory root file system, which contains the necessary device drivers and tools. The bootloader loads the root file system into the memory, and the kernel will mount it to the root directory/download , and then run the init script in the root file system to perform some startup tasks, and finally mount the real disk root file system.
In order to simplify the experimental environment, we only make the memory root file system here. Here, BusyBox is used to build a minimal memory root file system and provide basic user mode executable programs.
First download the busybox source code from https://www.busybox.net and decompress it. After the decompression is complete, configure and compile it like the kernel, and install it
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 #Remember to compile it into a static link instead of a dynamic link library.
Settings —>
[*] Build static binary (no shared libs) static compilation
Then compile and install, and it will be installed in the _install directory under the source code directory by default.
make -j$(nproc) & amp; & amp; make install
Then make a memory root file system image, the general process is as follows:
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/
Next, prepare the init script file and place it in the root file system and directory (rootfs/init), and add the following content to the init file:
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo “Wellcome TinyyOS!”
echo “——————–”
cd home
/bin/sh
Next, add executable permissions to the init script:
chmod + x init
Packaged into a memory root file system image
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz
Test to mount the root file system to see if the init script is executed after the kernel boot is complete
qemu-system-x86_64 -kernel ./arch/x86/boot/bzImage -initrd rootfs.cpio.gz
7. Debug the Linux kernel
Since the Linux kernel is highly customized, there is no way to make Intellisense prompt normally by configuring includePath, etc. Here, a Python script is used to generate the compile_commands.json file to help Intellisense prompt normally (including header files and macro definitions, etc.). Run the following command directly in the Linux source code directory to generate compile_commands.json. To open the previously prepared linux-5.4.34 folder in vscode, you need to do two things:
(1) Command line input:
python ./scripts/gen_compile_commands.py
(2) You need to manually place the configuration file in the linux-5.4.34 folder before this: create a new .vscode folder, and put all the files in the configuration file into the .vscode folder
Modify the value of command in task.json to be consistent with the current file path
8. Track the boot process of the Linux kernel
The starting point of the Linux kernel is the start_kernel function, so first break the point at start_kernel (click the run and debug icon, add a function breakpoint in the breakpoint: start_kernel)
Start debugging, the program is suspended at the breakpoint, and trace analysis is started from start_kernel.
Click the single point to skip, here we see that process 0 init_task is set as the first process of the whole system (process 0 is created manually, other processes are created by process 0) when the kernel boots, init_task will be created and start, which is the starting point for all other processes. Continue to skip, start_kernel will continue to perform some initialization operations.
At the end of the start_kernel() function, the arch_call_rest_init() function body is the rest_init() function, so set a rest_init function breakpoint and enter the rest_init function body, which is executed by process 0.
The kernel_thread function creates kernel_init, which corresponds to process No. 1 and is the ancestor of all user processes. Then the kernel_thread function creates kthreadd, which corresponds to process No. 2 and is the ancestor of all kernel processes. Enter the kernel_thread function to view, which creates a process through the _do_fork function. So it can be seen that process No. 1 and process No. 2 are finally created through _do_fork, and the process created by the user state through the system call fork is finally completed through _do_fork.
The principle of do_fork is as follows:
Next, run the kernel_init() function in the new thread (process), click on the kernel_init() function, you can see that the run_init_process function is called in this, and the run_init_process function uses the do_execve function. do_execve loads the executable file, runs the init program, and executes the exec system call, so that process 1 completes the user mode initialization.
See kthreadd function definition. Process 2 is created and initialized.
The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge Cloud native entry skill treeHomepageOverview 11124 people are learning systematically