Linux内核学习第三周 Linux启动过程分析

一.知识点回顾

1.计算机的“三个法宝”:存储程序计算机,中断,函数调用堆栈;

2.操作系统的“两把宝剑”:中断上下文的切换(保存现场和恢复现场),进程上下文的切换。

二.实验内容

1.启动menuos

执行命令   cd LinuxKernel/
           qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img

menuos启动成功。

2.利用gdb跟踪调试内核

 

  1. qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S # 关于-s和-S选项的说明:
  2. # -S freeze CPU at startup (use ’c’ to start execution)
  3. # -s shorthand for -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项

另打开一个shell窗口

 

 

 

  1. gdb
  2. (gdb)file linux-3.18.6/vmlinux # 在gdb界面中targe remote之前加载符号表
  3. (gdb)target remote:1234 # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行
  4. (gdb)break start_kernel # 断点的设置可以在target remote之前,也可以在之后

 

设置断点rest_init()

三.总结

1.start_kernel函数

start_kernel函数完成linux内核的初始化。几乎每个内核部件都是由这个函数进行初始化的。

调用sched_init函数初始化调度程序;

电泳trap_init函数和init_IRQ函数来完成IDT初始化;

调用time_init()函数来初始化系统日期和时间;

调用kernel_thread()函数为进程1创建内核线程,这个内核线程又会创建其他的内核线程并执行。

2init_task进程和idle进程

init_task进程在Linux中属于一个比较特殊的进程,它是内核开发者人为制造出来的,而不是其他进程通过do_fork来完成。Linux在无进程概念的情况下将一直从初始化部分的代码执行到start_kernel,然后再到其最后一个函数调用rest_init。从rest_init开始,Linux开始产生进程,因为init_task是静态制造出来的,pid=0,它试图将从最早的汇编代码一直到start_kernel的执行都纳入到init_task进程上下文中。在rest_init函数中,内核将通过下面的代码产生第一个真正的进程(pid=1):

 

  1. static noinline void __init_refok rest_init(void)
  2. {
  3.     ...
  4.     kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
  5.     ...
  6.     cpu_idle();
  7. }

kernel_init函数最有意思的地方在于它会通过调用kernel_execve来执行根文件系统下的/sbin/init文件(所以此前系统根文件系统必须已经就绪),kernel_execve对用户空间程序/sbin/init的调用发起自int $0x80,这是个从内核空间发起的系统调用,与call_usermodehelper函数本质上是完全一样的。

而此时init_task的任务基本上已经完全结束了,它将沦落为一个idle task,事实上在更早前的sched_init()函数中,通过init_idle(current, smp_processor_id())函数的调用就已经把init_task初始化成了一个idle task,init_idle函数的第一个参数current就是&init_task,在init_idle中将会把init_task加入到cpu的运行队列中,这样当运行队列中没有别的就绪进程时,init_task(也就是idle task)将会被调用,它的核心是一个while(1)循环,在循环中它将会调用schedule函数以便在运行队列中有新进程加入时切换到该新进程上。

 

posted @ 2016-03-13 21:56  小剑灬  阅读(192)  评论(0编辑  收藏  举报