姓名:何伟钦
学号:20135223
( *原创作品转载请注明出处*)
( 学习课程:《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
学习内容:构造一个简单的Linux系统MenuOS
第一部分:网上课程笔记
一、Linux内核源码主要结构简介
(一)内核源代码页面
1、arch目录:在Linux内核源代码里占有的比重很大,因为Linux内核支持很多的体系结构,而arch目录是支持不同的CPU的源代码.arch/x86目录下的代码是重点
2、Documentation目录:文档目录
3、fs目录:文件系统
4、init目录:内核启动相关的代码基本都在init目录下,其中的main.c代码里的start_kernel函数相当于普通C程序的main函数
5、kernel/ 存放linux内核最核心的代码,用于实现系统的核心模块,包括进程管理、进程调度器、中断处理、系统时钟管理、同步机制等。 - 该目录中的代码实现这些核心模块的主体框架,独立于具体的平台和系统架构。核心模块与平台相关的代码放在arch/中。
(二)常用:Readme
介绍了什么是Linux,Linux能够在哪些硬件上运行,如何安装内核源代码;提供内核的各种编译方法、生成文件的查看方法。例如:
- INSTALLING 如何安装内核源代码
- make mrproper 清理安装时生成的中间代码
第二部分:构造一个简单的Linux系统MenuOS
(一) 运行MenuOS系统
1.在实验楼的虚拟机环境里,打击打开shell,使用下面的命令
cd LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
2.命令说明:
(1)qemu相当于打开一个虚拟机 (2)kernel启动一个Linux内核,位置由其后的文件名指定。如果在当前目录下,可以直接输入文件名,如果不是,则需要输入该内核的全路径。
(3)Linux-3.18.6是最新版本的Linux系统。rootfs是含有init函数的文件,生成的是rootfs.img
(4)initrd指令是挂了一个ramdisk虚拟硬盘,是内核的重要补充(指明一个根文件系统rootfs)
(5)rootfs是含有init函数的文件,生成的是rootfs.img
rootfs.img相当于虚拟硬盘,内有分区,然后启动的其实是其中的init文件
3.输入命令help和quit,可以看到有关help和quit命令的参数及说明
(二)使用gdb跟踪调试内核
1.使用带参数命令启动MenuOS
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S
-s和-S参数说明:
# -S 在CPU初始化之前,冻结CPU
# -s 1234端口上创建一个tcp接口。若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项
2.用水平分割另外打开一个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 # 在start_kernel函数入口处设置断点 (gdb)c # 使得系统运行到start_kernel处停住 (gdb)list # 显示当前行所在位置上下的代码
(gdb)break rest_init
(gdb)c # 当前系统执行到rest_init
(gdb)list # 可以看到rest_init是在start_kernel的尾部调用的实验截图:
-
3.分析start_kernel(init目录下main.c的start_kernel函数)
void __init start_kernel(void)
该函数是Linux内核的入口,start_kernel()是内核的汇编与C语言的交接点,在该函数以前,内核的代码都是用汇编写的,完成一些最基本的初始化与环境设置工作。在start_kernel()中Linux将完成整个系统的内核初始化。内核初始化的最后一步就是启动init进程,这个所有进程的开始。不管分析内核那一部分都会涉及到start_kernel,因为几乎所有模块在初始化时都会调用它。
start_kernel是所有 Linux平台进入系统内核初始化后的入口函数,它主要完成剩余的与硬件平台相关的初始化工作在进行一系列与内核相关的初始化后,调用第一个用户进程-init进程并等待用户进程的执行,这样整个 Linux 内核便启动完毕。
4. init函数分析
(1)init_task:手工创建的PCB
全局变量init_task,即手工创建的PCB,0号进程初始化,0号进程就是最终的idle
(2)trap_init():初始化一些中断向量
中断向量表的初始化函数,设置了很多中断门,如set_intr_gate:设置中断门
(3)内存管理模块初始化 mm_init()
(4)调度模块初始化 sched_init()
(5)其它模块初始化 rest_init()
kernel_thread函数中的kernel_init包含一个run_init_process,创建了一号进程,即第一个用户态进程;
之后创建了kthreadd,一个内核线程来管理系统的资源。
(三)Linux内核启动过程相关的参考资料
1.
计算机的启动过程概述
(1)x86CPU启动的第一个动作CS:EIP=FFFF:0000H(换算为物理地址为000FFFF0H,因为16位CPU有20根地址线),即BIOS程序的位置。http://wenku.baidu.com/view/4e5c49eb172ded630b1cb699.html
(2)BIOS例行程序检测完硬件并完成相应的初始化之后就会寻找可引导介质,找到后把引导程序加载到指定内存区域后,就把控制权交给了引导程序。这里一般是把硬盘的第一个扇区MBR和活动分区的引导程序加载到内存(即加载BootLoader),加载完整后把控制权交给BootLoader。
(3)引导程序BootLoader开始负责操作系统初始化,然后起动操作系统。启动操作系统时一般会指定kernel、initrd和root所在的分区和目录,比如root (hd0,0),kernel (hd0,0)/bzImage root=/dev/ram init=/bin/ash,initrd (hd0,0)/myinitrd4M.img
(4)内核启动过程包括start_kernel之前和之后,之前全部是做初始化的汇编指令,之后开始C代码的操作系统初始化,最后执行第一个用户态进程init。一般分两阶段启动,先是利用initrd的内存文件系统,然后切换到硬盘文件系统继续启动
initrd文件的功能主要有两个:
1、提供开机必需的但kernel文件(即vmlinuz)没有提供的驱动模块(modules)
2、负责加载硬盘上的根文件系统并执行其中的/sbin/init程序进而将开机过程持续下去
符合中国传统文化精神的内核核心代码:
道生一(start_kernel....cpu_idle),
一生二(kernel_init和kthreadd)
二生三(即前面0、1和2三个进程)
三生万物(1号进程是所有用户态进程的祖先,2号进程是所有内核线程的祖先)
学习体会:
Linux 内核是一个非常庞大的工程,清晰地了解它执行的每一个过程是件非常困难的事。但是在嵌入式开发过程中,我们并不需要十分清楚 linux的内部工作机制。通过对 linux的启动过程的分 析,我们可以看出哪些是和硬件相关的,哪些是 linux 内核内部已实现的功能。尽管对Linux有了一定的了解,但是通过这几周对Linux的学习,我仍然觉得深入了解Linux是件很有挑战性的事。。
参考资料:
(1)《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
Linux内核源代码简介;构造一个简单的Linux系统;跟踪调试Linux内核的启动Linux内核分析
(2)init进程详解
(3)Linux内核中的init_task进程和idle进程
(4)http://www.cnblogs.com/20135202yjx/p/5252102.html