linux内核启动跟踪,Linux内核分析------跟踪分析Linux内核的启动过程
1、基础知识
(1)Linux内核中经常使用的目录
1.arch/:与体系结构相关的代码,(内容庞大,支持不一样cpu的源代码)。
2.Documentation/:文档文件
3.fs/:file system 文件系统
4.drivers/:与驱动相关的代码
5.lib/:连接库文件
6.ipc/:与进程间通讯相关的文件
7.mm/:memory managment 内存管理
8.kernel/:Linux内核的核心代码
9.init/:与内核启动相关的代码,其中有main.c,main.c中的start_kernel()函数是初始化Linux的起点
(2)使用gdb调试工具的基本指令
1.gdb 可执行文件名:进入gdb调试
2.(gdb)l:至关于list,从第一行开始列出源码
3.(gdb)回车:重复上一次指令
4.(gdb)break 函数名/行数:在某一行或者某个函数处设置断点
5.(gdb)info break:查看断点信息
6.在断点内能够使用:r(run运行),n(next单句执行),c(contiune继续执行)
7.(gdb)bt:查看函数堆栈
8.(gdb)finis:退出函数
9.(gdb)q:退出gdb
(3)Linux内核启动过程
Linux系统的启动分为4个部分:引导加载程序(bootloader),Linux内核,文件系统,应用程序,其中,在Linux内核启动时咱们完成了系统的重要初始化,并建立了init进程。
内核启动主要能够分为两个阶段:第一阶段主要是与硬件相关的初始化工做,此次咱们讨论的主要是第二部分。
在内核源码的init/main.c中,定义了内核启动的入口函数start_kernel(),在start _kernel()中调用相关处理程序函数,
建立了0号进程(set _task _stack _end _magic(&init _task)),
设置体系结构的相关环境(setup _arch),
初始化内存结构(bootmem _init),
开启mmu,
创建页表(paging _init),
初始化串口(console _init),
0号进程是全部进程的父进程,0号进程建立了1号进程init,又建立了其余服务例程。
2、实验执行——经过gdb调试,分析Linux的内核启动过程
(1)实验步骤:
使用实验楼已配置好的环境,web
cd LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
进入了菜单内核:
能够执行help、version和quit命令
进入gdb调试:
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
其中:
-S 是指在初始化cpu以前将cpu冻结
-s 是指在-gdb tcp ::1234,建立一个gdb server
已将cpu冻结
另开一个shell窗口:
(gdb)file linux-3.18.6/vmlinux
(gdb)target remote:1234
(gdb)break start_kernel
(gdb)c
在start_kernel处设置断点,执行c调试,冻结的系统开始启动,断点执行到start _kernel
若设置断点在rest_init,
利用list命令查看,发现rest_ init在start _kernel的尾部,几乎全部重要的内核模块初始化都在start _kernel完成,这是内核启动的关键。
(2)实验分析
进入并分析start_kernel:
1.init_task全局变量:即手工建立的PCB,0号进程即最终的idle进程.
在start_ kernel()中set _ task_ stack_ end_ magic (&init_task)中进行设置和初始化。0号进程是全部进程的父进程
2.trap_init():初始化中断向量
在trap_ init()中set_ intr_ gate(…)设置了不少中断门,执行不一样的硬件中断。set _ system _ trap _ gate(SYSCALL_ VECTOR,&system_call),设置系统陷阱门,就是系统调用!
3.mm_init()内存管理的初始化,sched _ init()调度模块的初始化
4.在start_ kernel的最后执行rest_init():
在rest_ init()中调用了kernel_thread(kernel _init,NULL,CLONE _FS),在kernel _init中,调用了run _init _process (),默认的init是根目录下的init,若是在根目录下没有,则找/sbin/init,/etc/init,/bin/init,/bin/sh做为1号进程,则kernel _init建立了一个1号进程,
执行kernel _ thread(kernel _ threaddd,NULL,CLONE _ FS|CLONE _ FILES),建立内核线程管理系统的运行资源,在rest init()尾部,进入了cpu startup _ entry(…),进入cpu _ idle,执行cpu _ idle _ loop(),在cpu idle loop()进行while(1)的循环处理,这就是0号进程。当系统没有进程须要执行时就调度到idle进程
start_kernel()启动时rest _init就一直存在,也就是0号进程,0号进程建立了1号进程,还建立了其余的服务线程,这样内核就启动起来了。
3、实验总结
本次实验主要分析了在内核启动过程当中的第一个函数:start_kernel的执行。
start_kernel()是init/main.c第一个启动的函数,主要完成了不少重要的与硬件平台相关和内核相关的初始化,并建立了init进程。
在init的start_ kernel()中调用了setup_ arch()进行与体系结构相关的第一个初始化,
经过bottom_init()函数根据系统定义的meminfo结构进行内存初始化,
最后paging_init()开启mmu,建立内核页表,映射全部的物理内存和I/O空间,
完成了建立异常向量表和初始化中断处理函数,初始化系统核心进程调度器和时钟中断处理机制,初始化串口控制台(serial_console)等等,
当全部的相关操做结束后,start_ kernel()会调用rest_ init()进行最后初始化,包括建立系统的第一个进程——init进程,init进程首先进行一系列的硬件初始化,而后经过命令行传递过来的参数挂在根文件系统,init进程会执行用户传递过来的参数执行用户指定的命令或者执行/sbin/init,/etc/init,/bin/init,/bin/sh 之一完成初始化工做,cpu_idle会被调用使系统处于闲置状态并等待用户输入。
到此,Linux内核启动完毕。
注意:
(1)内核启动过程包括start_kernel以前和以后,以前所有是作初始化的汇编指令,以后开始C代码的操做系统初始化,最后执行第一个用户态进程init
(2)从rest_ init开始,Linux开始产生进程,由于init_ task是静态制造出来的,pid=0,它试图将从最先的汇编代码一直到start_ kernel的执行都归入到init_ task进程上下文中。在rest _init函数中,内核将经过下面的代码产生第一个真正的进程(pid=1):
道生一(start_ kernel….cpu_ idle),一辈子二(kernel_init和kthreadd),二生三(即前面0、1和2三个进程),三生万物(1号进程是全部用户态进程的祖先,2号进程是全部内核线程的祖先)
经过此次实验,要重点理解内核启动时各个函数的功能以及调用顺序等,Linux启动过程很复杂,有不少硬件软件等初始化,应好好理解!shell
还没有评论,来说两句吧...