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

当Power on PC时,BIOS的代码开始执行,然后是Linux初始化的代码,这其中大约很长一段时间Linux都没有进程这一概念,但是这不影响CPU执行它的二进制代码。如果不是多任务以及进程调度的需要,Linux内核可以一直这样走下去

但是因为多任务的需求,Linux必须能支持任务这一特性,任务即进程,或者更简单地说由task_struct对象实例所代表的一段代码的集合,用以完成特定的任务。所以Linux内核初始化过程中必须为进程以及进程调度做准备


追踪操作系统 内核态 的初始化过程,从 init/main.c 中的 start_kernel() 开始



使用gdb跟踪调试内核

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -S -s

# 关于-S和-s选项的说明:
# -S freeze CPU at startup (use ’c’ to start execution)
# -s shorthand for -gdb tcp::1234
# 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项

使用gdb跟踪调试内核

另开一个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之前,也可以在之后

另开一个shell窗口


详细分析从start_kernel到init进程启动的过程


start_kernel()      /linux-3.18.6/init/main.c(500行)

start_kernel()是内核的汇编与C语言的交接点,在该函数以前,内核的代码都是用汇编写的,完成一些最基本的初始化与环境设置工作,比如内核代码载入内存并解压缩(现在的内核一般都经过压缩),CPU的最基本初始化,为C代码的运行设置环境(C代码的运行是有一定环境要求的,比如stack的设置等,具体见 C语言函数调用堆栈框架 )

start_kernel()

全局变量init_task,即手工创建的(0号进程的)PCB,0号进程即最终的idle进程

全局变量init_task

init_task进程在Linux中属于一个比较特殊的进程,它是内核开发者人为制造出来的,而不是其他进程通过do_fork来完成,init_task进程的内核栈通过静态方式分配

所有的模块在初始化的时候都是通过调用 start_kernel() 进行初始化,例如中断模块(trap_init)、内存管理模块(mm_init)、调度模块(sched_init)等等,研究特定的内核的模块,都需要了解 main.c 中的 start_kernel(),不管分析内核的哪一部分都会涉及到 start_kernel()


trap_init()      /linux-3.18.6/arch/x86/kernel/traps.c(792行)

涉及一些中断,初始化一些中断向量

trap_init()

set_intr_gate,设置了很多中断门

set_intr_gate,设置了很多中断门

set_system_trap_gate,设置系统陷阱门,系统调用

set_system_trap_gate,设置系统陷阱门,系统调用

分析中断的时候也主要是分析系统调用,因为硬件中断不好模拟,而系统调用也是一种中断,和中断的机制是一样的,它只是用指令的方式来触发一个中断

Linux在无进程概念的情况下将一直从初始化部分的代码执行到start_kernel(),在start_kernel()中Linux将完成整个系统的内核初始化。内核初始化的最后一步就是调用rest_init(),启动init进程这个所有进程的祖先


rest_init():Linux内核初始化的尾声

从rest_init开始,Linux开始产生进程,因为init_task是静态制造出来的,pid=0,它试图将从最早的汇编代码一直到start_kernel的执行都纳入到init_task进程上下文中。在rest_init函数中,内核将通过下面的代码产生第一个真正的进程(pid=1):

rest_init():Linux内核初始化的尾声

kernel_thread():创建一个内核线程,实际上就是内核进程,Linux内核是不支持类似Windows NT一样的线程概念的。Linux本质上只支持进程。这里的kernel_init只是一个函数

kernel_init():会通过调用do_execve来执行根文件系统下的/sbin/init文件(所以此前根文件系统必须已经就绪),do_execve对用户空间程序/sbin/init的调用发起自int $0x80,这是个从内核空间发起的系统调用

kernel_init()
运行init程序
run_init_process()

run_init_process():实际上是通过嵌入汇编构建一个类似用户态代码一样的do_execve()调用,其参数就是要执行的可执行文件名,也就是这里的init进程在磁盘上的文件

这里的run_init_process就是通过execve()来运行init程序。这里首先运行“/sbin/init”,如果失败再运行“/etc/init”,然后是 “/bin/init”,然后是“/bin/sh”(也就是说,init可执行文件可以放在上面代码中寻找的4个目录中都可以),如果都失败,则可以通过在系统启动时再添加的启动参数来指定init,比如init=/home/rootfs/init。这里是内核初始化结束并开始用户态初始化的阴阳界

init进程是Linux系统的第一个用户态进程,为1号进程,没有父进程,由Linux内核直接启动

接下来还创建了一个kthreadd内核线程,来管理系统的资源

创建了一个kthreadd内核线程

此时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函数以便在运行队列中有新进程加入时切换到该新进程上


Summary

内核启动过程包括start_kernel之前和之后,之前全部是做初始化的汇编指令(硬件平台相关),之后开始C代码的操作系统初始化(硬件平台无关),最后执行第一个用户态进程init

结合中国传统文化的角度看,道生一(start_kernel....cpu_idle),一生二(kernel_init和kthreadd),二生三(即前面0、1和2三个进程),三生万物(1号进程是所有用户态进程的祖先,2号进程是所有内核线程的祖先)


(完)


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容