假期中抽时间学习了一下linux内核的启动过程,在此做一下学习总结。

Linux启动过程描述:

1、启动BootLoader

2、Linux系统的初始化

3、Linux的应用程序的初始化

通用寄存器的作用

  • r0 :在函数开始时使用

  • r1 :存放堆栈指针,相当于ia32架构中的esp寄存器

  • r2 :存放当前进程的描述符的地址

  • r3 :存放第一个参数和返回地址

  • r4-r10 :存放函数的参数

  • r11 :用在指针的调用和当前一些语言的环境指针

  • r12 :用于存放异常处理

  • r13 :保留做为系统线程ID

  • r14-r31 :作为本地变量,具有非易失性

Linux启动过程描述:

第一步:使用BootLoader(一般是U-boot)加载Linux内核映像到内存,并负责目标系统的基本初始化过程,并搜集这个系统的基本信息,比如内存大小、处理器主频、外设的使用情况等一系列信息。然后把这些信息传递给linux内核。然后Boot loader把linux内核复制到从0x0000 0000 开始的物理内存处(虚拟地址一般为0xc000 0000处)开始执行。

从文件\arch\powerpc\boot\zImage.lds中可以看出,bootstraploader的入口为_zimage_start。在代码arch\powerpc\boot\crt0.S中

D:\virtual_machine\share_folder\linux-2.6.23\arch\powerpc\boot\zImage.lds中定义的入口地址为4MB,见下面:

SECTIONS{. = (410241024);_start = .;.text:

进入linux内核:从vmlinux.lds看到,内核入口为_stext,通过段.text.head 将代码定位到0xc0000000处。

在代码arch/powerpc/kernel/head_32.S中_stext之后紧接着是_start,他们之间没有代码,他们表示相同的地址。在vmlinux.lds中将.text.head规划为.text的第一个字段(保证了地址定位到0xc0000000)。

第二步:Linux系统的初始化。

1、 bootstraploader 过程

注意:需要知道从uboot跳到此处时,r3寄存器的内容,以及其他register的内容。如果运行地址和链接地址不同,则修正got表中各个函数的指针,清零BSS段调用platform_init(),保存bd到__res,初始化ppc_md(ppc module)中的各个函数。调用arch\powerpc\boot\main.c中的start()。

在start()中:

  • 1.1将命令行拷贝到cmdline中
  • 1.2调用open函数打开串口
  • 1.3解压缩kernel代码
  • 1.4解压缩ramdisk image
  • 1.5最终初始化设备树
  • 1.6跳到内核代码中执行

有两个调用语句,应该是运行了语句:kentry(ft_addr, 0, NULL); need confirm

2、 进入linux内核入口:arch/powerpc/kernel/head_32.S中的_start。

  • 2.1 early_init() ,arch/powerpc/kernel/setup_32.c中

       计算运行地址和链接地址的差值。根据cpu型号调用do_feature_fixups函数来对__ftr_fixup段进行修复处理。例如若HIDO寄存器的HIGH_BAT_EN位置位,另外的4组寄存器 IBATs (4–7) 和 4组 DBATs (4-7) 将会被激活,ftr_fixup段中对这8组寄存器进行初始化的代码就会生效;否则__ftr_fixup中的这段代码就会被nop指令所替!early_init()函数调用identify_cpu()函数,通过cpu中的pvr寄存器存放的CPU核的版本号在全局数组cpu_specs中寻找到当前cpu的详细信息,identify_cpu()函数在找到之后,会调用setup_cpu_spec()函数把上述cpu的信息所在的链接地址赋值给cur_cpu_spec变量。

  • 2.2 mmu_off() 关闭mmu
  • 2.3 flush_tlbs() 从TLB中移除页表
  • 2.4 call_setup_cpu():call_setup_cpu()位于misc_32.S文件中
  • 2.5 relocate_kernel():把内核代码拷贝到链接地址指向的位置
  • 2.6 turn_on_mmu():映射了256MB内存,就可以避免调用reloc_offset()函数来显式得把虚拟地址映射到物理地址!
  • 2.7跳到start_here()函数中运行,可以认为是真正内核开始运行。。。

3、start_kernel

本阶段也是有0号线程init_task中调用的,将完成Linux内核核心数据结构的初始化,最终创建1号线程kernel_init,最后由1号内核线程启动1号用户进程。需要后续确认。

4、 rest_init()函数。

  • 4.1 调用kernel_thread()创建1号内核线程。

  • 4.2 调用kernel_thread()创建kthreadd内核线程。尚不明作用。

  • 4.3 init_idle_bootup_task():当前0号进程init_task最终会退化成idle进程,所以这里调用init_idle_bootup_task()函数,让init_task进程隶属到idle调度类中。即选择idle的调度相关函数。

  • 4.4 调用schedule()函数切换当前进程,在调用该函数之前,Linux系统中只有两个进程,即0号进程init_task和1号进程kernel_init,其中kernel_init进程也是刚刚被创建的。调用该函数后,1号进程kernel_init将会运行!

  • 4.5 调用cpu_idle(),0号线程进入idle函数的循环,在该循环中会周期性地检查。

5、kernel_init 1号线程初始化

主要包括三方面:

第一:引导SMP系统中的其它CPU(即AP(Aplication Processor))

第二:调用do_basic_setup()函数,完成Linux系统其它模块的初始化;

第三:更换核心进程kernel_init为普通进程之后,完成对Linux系统的二次引导,即对Linux系统应用程序的引导!

  • 5.1设置当前1号线程所允许的BSP(即core 0)在cpu_all_mask中的对应bit位

  • 5.2 init_pid_ns.child_reaper = current; 设置1号线程回收orphan 线程。1号进程在Linux系统中相当于一个收容所,专门用于处理那些孤儿进程。

  • 5.3 smp_prepare_cpus(setup_max_cpus)该函数的作用是首先探测我们的目标系统中有多少个CPU,该函数为每个CPU创建一个idle进程。

  • 5.4 smp_init()是我们的linux-smp映像中启动另外一个核的最重要的代码,该函数主要是引导SMP系统中的AP,该函数会依次调用:smp_init()-> cpu_up()-> _cpu_up()-> __cpu_up()-> smp_ops-> kick_cpu(kick_cpu是一个函数指针,指向smp_86xx_kick_cpu,故会执行smp_86xx_kick_cpu()函数)smp_86xx_kick_cpu()-> smp_86xx_release_core()-> __secondary_start_mpc86xx()-> __secondary_start()来启动core 1,并调用set_cpu_online()函数将次CPU加入到cpu_online_map变量中,用以向主CPU通知该次CPU已经被激活。

  • 5.5 sched_init_smp()该函数的作用,有待遇进一步的分析!

  • 5.6 do_basic_setup():到目前为止,内核已经初始化了,memory管理和process scheduler 已经开始运行。但尚未注册设备。在本函数中将初始化workqueue,初始化device drivers,初始化中断处理,最后调用do_initcalls()进行静态安装所有模块,其中包括驱动人员最关心的用device_initcall声明的设备模块的安装。

  • 5.7 调用init_post()创建用户模式1号进程。

第三步:Linux的应用程序的初始化。

       1号kernel_init进程完成linux的各项配置(包括启动AP)后,就会在/sbin,/etc,/bin寻找init程序来运行。该init程序会替换kernel_init进程(注意:并不是创建一个新的进程来运行init程序,而是一次变身,使用sys_execve函数改变核心进程的正文段,将核心进程kernel_init转换成用户进程init),此时处于内核态的1号kernel_init进程将会转换为用户空间内的1号进程init。户进程init将根据/etc/inittab中提供的信息完成应用程序的初始化调用。然后init进程会执行/bin/sh产生shell界面提供给用户来与Linux系统进行交互。

调用init_post()创建用户模式1号进程。

在init_post()中最终调用下面的任何一个入口(按顺序,第一个执行成功后将不返回)

  1. if (execute_command) {
  2. run_init_process(execute_command);
  3. printk(KERN_WARNING "Failed to execute %s. Attempting "
  4. "defaults...\n", execute_command);
  5. }
  6. run_init_process("/sbin/init");
  7. run_init_process("/etc/init");
  8. run_init_process("/bin/init");
  9. run_init_process("/bin/sh");

20169211《Linux内核原理与分析》第三周作业的更多相关文章

  1. 2019-2020-1 20199329《Linux内核原理与分析》第九周作业

    <Linux内核原理与分析>第九周作业 一.本周内容概述: 阐释linux操作系统的整体构架 理解linux系统的一般执行过程和进程调度的时机 理解linux系统的中断和进程上下文切换 二 ...

  2. 2019-2020-1 20199329《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 一.上周问题总结: 未能及时整理笔记 Linux还需要多用 markdown格式不熟练 发布博客时间超过规定期限 二.本周学习内容: <庖丁解 ...

  3. 2019-2020-1 20209313《Linux内核原理与分析》第二周作业

    2019-2020-1 20209313<Linux内核原理与分析>第二周作业 零.总结 阐明自己对"计算机是如何工作的"理解. 一.myod 步骤 复习c文件处理内容 ...

  4. 20169212《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 这一周学习了MOOCLinux内核分析的第一讲,计算机是如何工作的?由于本科对相关知识的不熟悉,所以感觉有的知识理解起来了有一定的难度,不过多查查资 ...

  5. 20169210《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 本周作业分为两部分:第一部分为观看学习视频并完成实验楼实验一:第二部分为看<Linux内核设计与实现>1.2.18章并安装配置内核. 第 ...

  6. 2018-2019-1 20189221 《Linux内核原理与分析》第九周作业

    2018-2019-1 20189221 <Linux内核原理与分析>第九周作业 实验八 理理解进程调度时机跟踪分析进程调度与进程切换的过程 进程调度 进度调度时机: 1.中断处理过程(包 ...

  7. 2017-2018-1 20179215《Linux内核原理与分析》第二周作业

    20179215<Linux内核原理与分析>第二周作业 这一周主要了解了计算机是如何工作的,包括现在存储程序计算机的工作模型.X86汇编指令包括几种内存地址的寻址方式和push.pop.c ...

  8. 2018-2019-1 20189221《Linux内核原理与分析》第一周作业

    Linux内核原理与分析 - 第一周作业 实验1 Linux系统简介 Linux历史 1991 年 10 月,Linus Torvalds想在自己的电脑上运行UNIX,可是 UNIX 的商业版本非常昂 ...

  9. 《Linux内核原理与分析》第一周作业 20189210

    实验一 Linux系统简介 这一节主要学习了Linux的历史,Linux有关的重要人物以及学习Linux的方法,Linux和Windows的区别.其中学到了LInux中的应用程序大都为开源自由的软件, ...

  10. 2020-2021-1 20209307 《Linux内核原理与分析》第九周作业

    这个作业属于哪个课程 <2020-2021-1Linux内核原理与分析)> 这个作业要求在哪里 <2020-2021-1Linux内核原理与分析第九周作业> 这个作业的目标 & ...

随机推荐

  1. 软件测试(三)—— 参数化测试用例(Nextday.java)

    import static org.junit.Assert.*; import java.lang.reflect.Array; import java.util.Arrays; import ja ...

  2. arguments.length

    本文地址:http://www.cnblogs.com/veinyin/p/7607083.html  arguments.length是实参的个数,与形参个数无关.

  3. final关键字详解

    java中,final关键字可以用来修饰类.方法和变量(包括成员变量和局部变量).下面就从这三个方面来了解一下final关键字的基本用法. 1.修饰类 当用final修饰一个类时,表明这个类不能被继承 ...

  4. lintcode 66.67.68 二叉树遍历(前序、中序、后序)

    AC代码: /** * Definition of TreeNode: * public class TreeNode { * public int val; * public TreeNode le ...

  5. Neuroph studio 入门教程

    PERCEPTRON Perceptron is a simple two layer neural network with several neurons in input layer, and ...

  6. Postgres中tuple的组装与插入

    1.相关的数据类型 我们先看相关的数据类型: HeapTupleData(src/include/access/htup.h) typedef struct HeapTupleData { uint3 ...

  7. 码源中国.gitignore忽略文件配置

    码源中国.gitignore忽略文件配置 ## Ignore Visual Studio temporary files, build results, and ## files generated ...

  8. 修改 firefox accesskey 的快捷键

    Chrome中,如果设置了 accesskey 的话,可以通过 Alt + 快捷键 来之直接跳转的.但在Firefox 中,可能是为了防止于菜单的快捷键冲突,所以设置了 Shift + Alt + 快 ...

  9. Python模块之pxssh

    pxssh模块用于在python中ssh远程连接,执行命令,返回结果,但注意不支持Windows系统 #!/usr/bin/env python #-*- coding:utf-8 -*- from ...

  10. Nginx源码分析--epoll模块

    Nginx采用epoll模块实现高并发的网络编程,现在对Nginx的epoll模块进行分析. 定义在src/event/modules/ngx_epoll_module.c中 1. epoll_cre ...