跟踪分析Linux内核的启动过程

来源:互联网 发布:惠州市干部网络 编辑:程序博客网 时间:2024/05/16 09:02

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

张磊+ 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

使用实验楼虚拟机打开 shell

cd LinuxKernel/qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
运行结果如图所示:

内核启动进入 menu 程序。

下面是用 gbd 来跟踪内核的启动过程:

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

另开一个 shell 窗口

gdb(gdb)file linux-3.18.6/vmlinux # 在gdb界面中targe remote之前加载符号表(gdb)target remote:1234 # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行(gdb)break start_kernel # 断点的设置可以在target remote之前,也可以在之后
如图加 断点后程序运行停止在了 start_kernel 处。


 从某种意义上,函数start_kernel就好像一般可执行程序中的主函数main,系统进入这个函数之前已经进行了一些最低限度的初始化,再往前研究就涉及很多硬件相关及编程语言了,这里是较高层次的初始化,基本是C代码,一直想搞清楚内核的初始化流程,好对整个linux内核有更深理解。分析程序习惯性的找main函数,那么就从这个start_kernel看看。

  这个函数在init/main.c:

asmlinkage __visible void __init start_kernel(void){char *command_line;char *after_dashes;/* * Need to run as early as possible, to initialize the * lockdep hash: */lockdep_init();set_task_stack_end_magic(&init_task);smp_setup_processor_id();debug_objects_early_init();

首先是调用 lockdep_init函数 :

void lockdep_init(void){int i;/* * Some architectures have their own start_kernel() * code which calls lockdep_init(), while we also * call lockdep_init() from the start_kernel() itself, * and we want to initialize the hashes only once: */if (lockdep_initialized)return;for (i = 0; i < CLASSHASH_SIZE; i++)INIT_LIST_HEAD(classhash_table + i);for (i = 0; i < CHAINHASH_SIZE; i++)INIT_LIST_HEAD(chainhash_table + i);lockdep_initialized = 1;}

注释写得很清楚,有些体系结构有自己的start_kernel也会调用lockdep_init,这里只会调用一次,来初始化hash表。

其它函数就不在这里一一描述了,当执行到init 函数时,init是第一个调用的使用标准C库编译的程序。在此之前,还没有执行任何标准的C应用程序。在桌面Linux系统上,第一个

启动的程序通常是/sbin/init,它的进程号为1。init进程是所有进程的发起者和控制者,它有两个作用:

    (1)扮演终结父进程的角色:所有的孤儿进程都会被init进程接管。

    (2)系统初始化工作:如设置键盘、字体,装载模块,设置网络等。

 小结:

x86架构的Linux内核启动过程分为6大步,分别为:    

(1)实模式的入口函数_start():在header.S中,这里会进入众所周知的main函数,它拷贝bootloader的各个参数,执行基本硬件设置,解析命令行参数。

(2)保护模式的入口函数startup_32():在compressed/header_32.S中,这里会解压bzImage内核映像,加载vmlinux内核文件。

(3)内核入口函数startup_32():在kernel/header_32.S中,这就是所谓的进程0,它会进入体系结构无关的start_kernel()函数,即众所周知的Linux内核启动函数。start_kernel()会

做大量的内核初始化操作,解析内核启动的命令行参数,并启动一个内核线程来完成内核模块初始化的过程,然后进入空闲循环。

(4)内核模块初始化的入口函数kernel_init():在init/main.c中,这里会启动内核模块、创建基于内存的rootfs、加载initramfs文件或cpio-initrd,并启动一个内核线程来运行其中

的/init脚本,完成真正根文件系统的挂载。

(5)根文件系统挂载脚本/init:这里会挂载根文件系统、运行/sbin/init,从而启动众所周知的进程1。

(6)init进程的系统初始化过程:执行相关脚本,以完成系统初始化,如设置键盘、字体,装载模块,设置网络等,最后运行登录程序,出现登录界面。

    如果从体系结构无关的视角来看,start_kernel()可以看作时体系结构无关的Linux main函数,它是体系结构无关的代码的统一入口函数,这也是为什么文件会命名为init/main.c的

原因。这个main.c粘合剂把各种体系结构的代码“粘合”到一个统一的入口处。

0 0
原创粉丝点击