Linux内核分析:实验三--内核启动过程分析

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

概述

本次实验阅读Linux-3.18.6版本的部分内核源码,并通过调试跟踪简化版本的Linux内核–MenuOS,来分析内核启动的过程。MenuOS内核启动后,只有三个命令:help、version、quit。如下图所示:
这里写图片描述

内核源码目录解读

本次实验阅读的源码是Linux-3.18.6版本,大致包含如下几个目录:
- arch : 计算机的体系结构–Linux系统能跑在不同类型CPU的关键
- block : 部分块设备驱动程序
- crypto : 加密、压缩、CRC校验算法
- documentation : 内核的文档
- drivers : 设备驱动程序
- fs : 存放各种文件系统的实现代码
- include : 内核所需要的头文件
- init : 内核初始化代码
- ipc : 进程间通信代码
- kernel : Linux大多数关键的核心功能都在此目录实现的
- lib : 库文件的代码
- mm : 实现内存管理
- net : 网络协议的实现代码
- samples : 一些内核编程的范例
- scripts: 配置内核的脚本
- security : SElinux的模块
- sound : 音频设备的驱动程序
- usr : cpio命令实现
-virt : 内核虚拟机

以上资料来源于:Linux内核目录解读

内核启动过程分析

在init目录下存放着内核初始化的代码,在这个目录下有一个main.c文件,它完成了整个内核的初始化,其中我们主要关注的是start_kernel函数。首先我们先进行设断点调试,然后再分析源码。

GDB调试内核

为了能让内核启动前,在start_kernel位置处设下断点,我们需要做2个工作。
第一:我们编译时需要开启编译器的调试信息选项,GCC的 -g 选项
第二:让内核程序启动之前,让CPU停住,不然等我们打开GDB的时候,内核已经完成启动了。
首先在终端输入

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

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

然后新开一个终端,输入gdb -q 对内核进行调试,如下所示:
在start_kernel处设置断点
这里写图片描述
在rest_init处设置断点
这里写图片描述
一直运行到内核启动完成
这里写图片描述
它是一个while(1)死循环,在cpu_idle_loop中。

start_kernel启动过程分析

内核的初始化程序在start_kernel这个函数中,可以在线查看这些代码: start_kernel。通过阅读start_kernel代码,可以大致了解到内核在初始化的时候,做了以下工作:
1)lockdep_init():初始化内核依赖关系表,初始化hash表
2)boot_init_stack_canary():为栈增加保护机制,预防一些缓冲区溢出之类的攻击
3)tick_init():初始化内核时钟系统
4)boot_cpu_init():激活当前CPU
5)setup_arch():对不同体系结构的CPU设置不同的参数、选项等
6)trap_init():初始化硬件中断,函数中设置了很多中断门
7)mm_init():建立内核的内存分配器
8)sched_init():初始化任务调度
9)init_IRQ():中断向量的初始化
….
很多初始化工作。。
n)rest_init():剩下的初始化工作,这里面其实做了很多工作

rest_init过程分析

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

kernel_thread():使用do_fork()创建一个内核线程,实际上就是内核进程。其参数kernel_init是一个函数,kernel_init中会调用try_to_run_init_process执行/sbin/init程序,如图所示:
这里写图片描述
init进程是Linux系统的第一个用户态进程,为1号进程,没有父进程,由Linux内核直接启动。

此时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函数以便在运行队列中有新进程加入时切换到该新进程上。参考[简书-那只大象]的文章

总结

Linux内核是一个非常庞大的工程项目,其中包含了各种各样的初始化和回调。start_kernel函数中包含了Linux内核启动过程的大部分代码,内核启动过程包括start_kernel之前和之后,之前全部是做初始化的汇编指令(硬件平台相关),之后开始C代码的操作系统初始化(硬件平台无关),最后执行第一个用户态进程init。

1号进程init进程是所有用户进程的祖先,2号进程是所有内核线程的祖先。

参考

[1] 简书-那只大象
[2] CSDN-qing-ping

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值