Pan Junyang
Original works reproduced please indicate the source
"Linux kernel Analysis" MOOC course http://mooc.study.163.com/course/USTC-1000029000
I. Preparation
Build the Environment
12345678910111213141516171819202122232425 |
cd ~
/Work/
wget https:
//www
.kernel.org
/pub/linux/kernel/v3
.x
/linux-3
.18.6.
tar
.xz
xz -d linux-3.18.6.
tar
.xz
tar -xvf linux-3.18.6.
tar
cd linux-3.18.6
make i386_defconfig
make
cd ~
/Work/
mkdir rootfs
git clone https:
//github
.com
/mengning/menu
.git
# 话说这里为什么用MenuOS 我个人觉得老师一来是节约编译时间 二来也可以做做广告
cd menu
sudo apt-get
install libc6:i386 lib32stdc++6
# 这两行安装非常有必要
sudo apt-get
install lib32readline-gplv2-dev
# 在64bit的Ubuntu环境下不能编译这个MenuOS的roofs 需要这些包来支持 即使用了-m32
gcc -o init linktable.c menu.c
test
.c -m32 -static -lpthread
cd ..
/rootfs
cp ..
/menu/init ./
find . | cpio -o -Hnewc |
gzip -9 > ..
/rootfs
.img
cd ~
/Work/
qemu -kernel linux-3.18.6
/arch/x86/boot/bzImage -initrd rootfs.img
sudo apt-get
install libncurses5-dev
# 保证make menuconfig可用
make menuconfig
kernel hacking->
copile-
time checks and compile options
[*] compile the kernel with debug info
qemu -kernel linux-3.18.6
/arch/x86/boot/bzImage -initrd rootfs.img -s -S
|
Then open another shell and execute the following command:
1234 |
gdb file linux-3.18.6 /vmlinux # 在gdb界面中targe remote之前加载符号表 target remote:1234 # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行 break start_kernel # 断点的设置可以在target remote之前,也可以在之后 |
After you set the breakpoint, you can use C to let the kernel continue to load and load to the first breakpoint Start_kernel
Two. Analysis
During the execution of Start_kernel, the period early initializes various hardware devices, such as CPU and memory, which involve the loading of a very wide variety of kernel modules.
The last initialization in Start_kernel is the initialization of kernel process management. Once this initialization is complete, the kernel is loaded successfully.
My_start_kernel, after inserting this function, our own kernel through the PCB process Management unit to manage the four simple processes we created sequentially, and through the way of time slice rotation scheduling. So what exactly is Rest_init () doing in the actual Linux kernel code that makes it necessary for us to execute my_start_kernel before it? The reason is that rest_init is actually a function of the Linux kernel initialization process. If we create our own process ourselves before it executes, and use our own scheduling algorithm to dispatch the process created after it, then Rest_init will never be executed, because our own process is not over before it executes.
Let's take a look at the kernel code rest_init of the actual Linux initialization process (delete the parts that don't care):
12345678910111213141516 |
void rest_init(
void
)
{
int pid;
………………
kernel_thread(kernel_init, NULL, CLONE_FS);
numa_default_policy();
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
rcu_read_lock();
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
rcu_read_unlock();
complete(&kthreadd_done);
init_idle_bootup_task(current);
schedule_preempt_disabled();
cpu_startup_entry(CPUHP_ONLINE);
}
|
In Rest_init's code, Kernel_thread, defined in the file arch/x86/kernel/fork.c, is used to fork a kernel thread.
1234 |
pid_t Kernel_thread ( Code class= "CPP Color1 bold" >int (*FN) ( void *), void * ARG, unsigned long flags) { |
As we can see from the above code, Kernel_thread is actually a thread that takes a fork.
When Kernel_thread is executed, Kernel_init is passed in as a function pointer that will be executed, and the process ID is set to 1. So here, the Kernel_init kernel thread is created and the process number is 1.
After the kernel process is created, the Kthreadd kernel thread is created to manage and dispatch other kernel threads.
Kernel_init is going to be executed, let's see what Kernel_init will do:
123456789101112131415161718192021222324252627282930313233 |
static int kernel_init(
void *unused)
{
int ret;
kernel_init_freeable();
async_synchronize_full();
free_initmem();
mark_rodata_ro();
system_state = SYSTEM_RUNNING;
numa_default_policy();
flush_delayed_fput();
if (ramdisk_execute_command) {
ret = run_init_process(ramdisk_execute_command);
if (!ret)
return 0;
pr_err(
"Failed to execute %s (error %d)\n"
,
ramdisk_execute_command, ret);
}
if (execute_command) {
ret = run_init_process(execute_command);
if (!ret)
return 0;
pr_err(
"Failed to execute %s (error %d). Attempting defaults...\n"
,
execute_command, ret);
}
if (!try_to_run_init_process(
"/sbin/init"
) ||
!try_to_run_init_process(
"/etc/init"
) ||
!try_to_run_init_process(
"/bin/init"
) ||
!try_to_run_init_process(
"/bin/sh"
))
return 0;
panic(
"No working init found. Try passing init= option to kernel. "
"See Linux Documentation/init.txt for guidance."
);
}
|
In fact, Kernel_init will continue to work on the final initialization of the kernel until the last line actually initializes the entire kernel.
Note that the process ID we created is actually starting at 1. The process created in Kernel_init is number 1th, and the 2nd process was created in the earlier kthreadd.
Then, in order for the system to work, the remaining three lines of code have done a very important job, which completes the CPU's scheduled initialization of the task, allowing the kernel to really start to enter the user-led phase:
123 |
init_idle_bootup_task(current); schedule_preempt_disabled(); cpu_startup_entry(CPUHP_ONLINE); |
First, Init_idle_bootup_tast () Initializes an idle (idle) process, which does nothing else but consumes time slices.
The process is then set through schedule_preempt_disabled to not be dispatched. Since the CPU is obviously better at utilization, it is not possible for the scheduler to schedule a process that consumes only the time slices.
Finally, the Cpu_startup_entry will allow the CPU to work in a loop like idle, constantly reciprocating, never returning.
12345 |
void cpu_startup_entry( enum cpuhp_state state) { arch_cpu_idle_prepare(); cpu_idle_loop(); } |
Since then, the entire kernel startup process has been completed.
Three. The experimental process
Let's step through the process of loading the Idel and process # 1th.
From the above analysis, we note that there are several more important breakpoints that need to be set:
Start_kernel, Page_address_init, Trap_init, Mm_init, Rest_init, Kernel_init, Kthreadd, Init_idle_bootup_task, cpu_ Startup_entry
Trace analysis of the boot process of the Linux kernel