一、实验过程

使用实验楼虚拟机打开shell

cd LinuxKernel/
qemu -kernel linux-3.18./arch/x86/boot/bzImage -initrd rootfs.img

内核启动进入 menu 程序。下面是用 gbd 来跟踪内核的启动过程:

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

这里的 -s和-S选项分别代表:
 -S freeze CPU at startup (use ’c’ to start execution)。

-s shorthand for -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项。

另开一个shell窗口,输入gdb。然后输入file linux-3.18.6/vmlinux,在gdb界面中targe remote之前加载符号表;
target remote:1234,建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行。

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

可以看到,内核启动在start_kernel进程处停住了,在这之前从Power On开始是很长的初始化过程。x86 CPU启动的第一个动作CS:EIP=FFFF:0000H(换算为物理地址为000FFFF0H,因为16位CPU有20根地址线),即BIOS程序的位置,在这个过程计算机只是单纯地执行二进制指令,此时并不存在真正的进程。然后是运行到第二个断点rest_init,这是start_kernel调用的最后一个函数。设置相关的断点,这里我们将断定设在函数break start_kernel以及rest_init处。继续程序并观察断点处代码。

二、start_kernel函数的执行过程

     系统进入start_kernel这个函数之前已经进行了一些最低限度的初始化,再往前研究就涉及很多硬件相关及编程语言了。内核即进入了C语言部分,它完成了内核的大部分初始化工作。实际上,可以将start_kernel函数看做内核的main函数。 这个函数在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 = ; i < CLASSHASH_SIZE; i++)
INIT_LIST_HEAD(classhash_table + i); for (i = ; i < CHAINHASH_SIZE; i++)
INIT_LIST_HEAD(chainhash_table + i); lockdep_initialized = ;
}

在start_kernel函数的最后调用了rest_init函数进行后续的初始化,生成了一号的进程。

static noinline void __init_refok rest_init(void)
{
int pid; rcu_scheduler_starting();
/*
* We need to spawn init first so that it obtains pid 1, however
* the init task will end up wanting to create kthreads, which, if
* we schedule it before we create kthreadd, will OOPS.
*/
kernel_thread(kernel_init, NULL, CLONE_FS);
numa_default_policy();
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);

pid= kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); 这行代码是创建一个干净内核线程,以便以后其它所有内核线程全部拷贝它,并由它来创建,这样达到更方便创建线程。

rcu_read_lock();
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
rcu_read_unlock();
complete(&kthreadd_done);
/*
* The boot idle thread must execute schedule()
* at least once to get things moving:
*/
init_idle_bootup_task(current);

init_idle_bootup_task(current); 这行代码是初始化空闲进程的调度器,以便让空闲进程知道怎么样调度任务列表里的进程。current是指向当前IDLE任务的结构。

schedule_preempt_disabled();
/* Call into cpu_idle with preempt disabled */
cpu_startup_entry(CPUHP_ONLINE);

这行代码是调用进程调度函数schedule,主要初始化调度器可以切换回到空闲任务。并增加内核抢先计数。

三、总结

在本实验中,我分析了Linux系统的启动过程。最初执行的进程即是0号进程init_task,它是被静态产生的,内存栈的位置固定,执行一些初始化的工作。一直到start_kernel开始调用执行sched_init(),0号进程被init_idle(current, smp_processor_id())进程初始化成为一个idle task,变成上一次实验中的进程一样的,通过一个while循环不断执行,只要运行栈里没有别的进程它就执行,循环中不断检测运行栈里是否有其他进程并通过schedule函数进行调度。
     其中idle进程的产生为:idle是一个进程,其pid号为 0。其前身是系统创建的第一个进程,也是唯一一个没有通过fork()产生的进程。它在本实验中,具体是由init/main.c中start_kernel函数的set_task_stack_end_magic(&init_task)这一行开始实现的。其中的init_task就是手工创建的PCB,pid=0的进程,也就是最终的idle进程。
     而1号进程的产生为:而到了kernel_thread(kernel_init, NULL, CLONE_FS);则通过fork()建立了pid=1的1号进程,也叫init进程,它是第一个用户态进程,它会继续完成剩下的初始化工作,成为系统中的其他所有进程的祖先。而创建了1号进程后,随着init_idle_bootup_task(current);等函数的调用,0号进程就演变成了idle进程。而idle进程就是当系统没有进程需要执行的时候来调度用的。所以start_kernel里、rest_init里创建了0号进程,该进程在系统初始化时候建立,并在系统运行过程中一直存在;而由0号进程,生成了1号进程,以及之后的许许多多的进程。最后进入了cpu_startup_entry。这个其实就是调用了cpu_idle。其实里面就是在while循环里调用了0号进程。

虽然在实验中我出不体会了Linux系统的启动过程,但是对Linux系统的理解还不深入,需要进一步加强。

刘帅

原创作品转载请注明出处

《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

Linux内核分析之跟踪分析Linux内核的启动过程的更多相关文章

  1. linux内核学习之三 跟踪分析内核的启动过程

    一   前期准备工作       1 搭建环境 1.1下载内核源代码并编译内核 创建目录,并进入该目录: 下载源码: 解压缩,并进入该目录:xz -d linux-3.18.6.tar.xz tar ...

  2. Linux第三周——跟踪分析内核的启动过程

    跟踪分析内核的启动过程实验 张潇月<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 这周主要学习的是对内核 ...

  3. 走进Linux之systemd启动过程

    Linux系统的启动方式有点复杂,而且总是有需要优化的地方.传统的Linux系统启动过程主要由著名的init进程(也被称为SysV init启动系统)处理,而基于init的启动系统被认为有效率不足的问 ...

  4. Hive源码分析(1)——HiveServer2启动过程

    1.想了解HiveServer2的启动过程,则需要找到启动HiveServer2的入口,hive服务的启动命令为hive --service HiveServer2,通过分析$HIVE_HOME/bi ...

  5. Linux内核分析--理解进程调度时机、跟踪分析进程调度和进程切换的过程

    ID:fuchen1994 姓名:江军 作业要求: 理解Linux系统中进程调度的时机,可以在内核代码中搜索schedule()函数,看都是哪里调用了schedule(),判断我们课程内容中的总结是否 ...

  6. 跟踪分析Linux内核的启动过程--实验报告 分析 及知识重点

    跟踪分析Linux内核的启动过程 攥写人:杨光  学号:20135233 ( *原创作品转载请注明出处*) ( 学习课程:<Linux内核分析>MOOC课程http://mooc.stud ...

  7. 20135202闫佳歆--week3 跟踪分析Linux内核的启动过程--实验及总结

    实验三:跟踪分析Linux内核的启动过程 一.调试步骤如下: 使用gdb跟踪调试内核 qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd r ...

  8. Linux内核分析 实验三:跟踪分析Linux内核的启动过程

    贺邦 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 一. 实验过程 ...

  9. 跟踪分析Linux内核的启动过程小解

    跟踪分析Linux内核的启动过程 “20135224陈实  + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029 ...

随机推荐

  1. Clustering with the ArcGIS API for Flex

    Clustering is an excellent technique for visualizing lotss of point data. We've all seen application ...

  2. sencha touch 入门系列 (二)sencha touch 开发准备

    这是本人第一次写博客教程,没什么经验,文笔也不是很好,写这教程一方面为了巩固自己这段时间的学习成果,一方面帮助大家解决问题,欢迎大家多提建议,指出问题.接下来我们就开始我们的sencha touch开 ...

  3. JS的join函数用法

    无意中在网上看到一个关于join比for循环更有效率的说法.虽然不明白为什么,先记一笔. join函数用于数组.返回值为一个字符串.实现的效果就是将数组连成自己想要的字符串,当然是有规律可循的字符串. ...

  4. 在silverlight中通过WCF连接ORACLE DB数据库(转)

    转自 http://hi.baidu.com/qianlihanse/item/458aa7c8d93d4e0cac092ff4 这不是我的原创,我也是上网学习的~ How to get data f ...

  5. js获取浏览器语言(ie、ff、chrome)、contextpath

    /js获取浏览器语言(ie.ff.chrome) var language_en_us = "en-us"; var language_zh_cn = "zh-cn&qu ...

  6. AS

    1.修改注释模板: “File“->“Settings”    File and Code Templates     然后选中Includes tab下面的File Header. 2.代码提 ...

  7. 自定义控件TextView

    public class defineTextView extends TextView { Context context; public defineTextView(Context contex ...

  8. ios category,protocol理解

    category: 向现有的类中增加方法,同时提供方法的实现,现有类不需要做任何改动. protocol:(相当于Java或C#中的接口interface,当很多类都要需要类似的方法,但是方法具体实现 ...

  9. 【转载】OpenGL ES 三种类型修饰 uniform attribute varying

    其实attribute varying已经被in和out代替了,但是有些工程代码里面仍然还在,所以权当笔记好了. 1.uniform变量uniform变量是外部application程序传递给(ver ...

  10. 编程范式 episode 6 实现stack 栈功能_ to do

    //既然在这里开始,那就在这里结束. 实现stack 功能 ____coding _using subfunction to focus on the main aim of current func ...