《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
使用gdb跟踪调试内核
另开一个shell窗口
Linux内核启动过程相关的参考资料
计算机的启动过程概述
x86 CPU启动的第一个动作CS:EIP=FFFF:0000H(换算为物理地址为000FFFF0H,因为16位CPU有20根地址线),即BIOS程序的位置
BIOS例行程序检测完硬件并完成相应的初始化之后就会寻找可引导介质,找到后把引导程序加载到指定内存区域后,就把控制权交给了引导程序。这里一般是把硬盘的第一个扇区MBR和活动分区的引导程序加载到内存(即加载BootLoader),加载完整后把控制权交给BootLoader。
引导程序BootLoader开始负责操作系统初始化,然后起动操作系统。启动操作系统时一般会指定kernel、initrd和root所在的分区和目录,比如root (hd0,0),kernel (hd0,0)/bzImage root=/dev/ram init=/bin/ash,initrd (hd0,0)/myinitrd4M.img
内核启动过程包括start_kernel之前和之后,之前全部是做初始化的汇编指令,之后开始C代码的操作系统初始化,最后执行第一个用户态进程init。
一般分两阶段启动,先是利用initrd的内存文件系统,然后切换到硬盘文件系统继续启动。initrd文件的功能主要有两个:1、提供开机必需的但kernel文件(即vmlinuz)没有提供的驱动模块(modules) 2、负责加载硬盘上的根文件系统并执行其中的/sbin/init程序进而将开机过程持续下去
在kernel/fork.c里面实现了kernel_thread函数
do_fork就开始fork进程了!
在调用rest_init之前,会有很多初始化的工作,这个工作都由进程0来完成!
直到rest_init被调用,创建进程1.
由于初始化的操作太多, 水平有限,只能适当的选取一些函数进行分析.
注意到,调用了这个函数
函数定义如下: 经调试发现smp_processor_id()的返回值为0.即,用于启动的CPU是0号CPU
我们定位函数,发现smp_processor_id()实质上是一个宏定义,然后调用的别人的函数.
这里一直到rest_init()都是由init_task进程控制的.而init_task是内核程序员人为创建的进程.
以下是rest_init()的实现.
通过kernel_thread()函数将创建1号(而不是0号, 0 号就是我们之前一直处于的进程 :-) . 2号进程为kthreadd/
这里1号进程将进入kernel_init()函数
啊哈!我们来看看1号进程里面都是些啥
static int __ref kernel_init(void *unused) { int ret; kernel_init_freeable(); /* need to finish all async __init code before freeing the memory */ 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); } /* * We try each of these until one succeeds. * * The Bourne shell can be used instead of init if we are * trying to recover a really broken machine. */ if (execute_command) { //注意!(我们这里就可以动点"手脚"了)如果我们指定了init程序就可以跑我们的程序,如果没有,这里就进不去if. 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."); }
我只想吐槽,这编译时间,简直了,受不了,读者可以自行去修改config文件,然后别去编译什么鬼module,用不到,这里快点进入我们的init进程,即自己写的menu程序就可以啦
如下图运行结果.
千万千万记得,make之前要做make i386_deconfig
我之前就是缺了这步,然后可能是用的64位的虚拟的,然后就panic了 : )
前车之鉴
制作跟文件系统
首先在内核目录树根目录下面创建rootfs目录.
参考文献:
http://blog.chinaunix.net/uid-23769728-id-3127671.html
http://blog.csdn.net/larryliuqing/article/details/10077785