ie传递给系统调用的数据区域太小_嵌入式linux系统基础

  1. 嵌入式linux的开机顺序

dab6d308162dd1ffc7c5ebbb67fb2b9a.png

上图是一个嵌入式Linux系统的典型结构,划分成了4个区:

  • uboot区域存储的是uboot类似于windows的BIOS,做的东西就是 嵌入式系统最基础的硬件初始化,驱动,内核加载,参数传递.

  • 参数传递就是uboot将内核启动的基本参数传递给内核,最常见的就是bootargs ,console=ttyS0 ,就是传递的内核的终端是从串口0输出的,这个参数会在printk.c里面获取,这个比较简单,需要自己去跟一下代码,之后会具体讲解一下bootargs传递参数代表的意思是什么。

  • 内核镜像区域存放的是内核的镜像的压缩,当他被解压后放到内存里面,作为嵌入式设备的系统。

  • 文件系统区域存放的是经过压缩的文件系统,他会被linux内核解压后并且挂载,并且做为各种应用程序,文件的载体。

uboot的启动流程

6bdb8c0d89b9071100789675a89fe1e1.png

  • uboot的stage1完成的主要任务

    • 基本的硬件初始化,这是uboot一开始就应该执行的操作,主要谜底是为了stage2的执行以及随后的kernel的执行准备给好一些基本的硬件唤醒,它通常包括以下步骤:

      • 禁用中断(因为中断服务通常是设备驱动程序的责任)

      • 设置时钟,CPU的速度

      • RAM初始化

      • 关闭CPU的Cache(因为通常情况下CPU对数据的读取和写入都是通过CR15在通过CACHE对数据读取,但是这个是后DDR或者外部SRAM并没有初始化,所以这个时候去cache里面读出来的数据就错误的,并不是你想要的。)

    • 复制stage2到RAM中

      • 复制的时候需要确定两点:stage2的可执行镜像在固态存储设备的存放起始和结束地址,以及RAM空间的起始地址

    • 设置堆栈指针SP,堆栈指针的设置是为了执行C语言代码做好准备

    • 跳转到stage2的C入口地址。一般都是bl ...

  • uboot的stage2完成的主要任务

    stage2的基本上都是C语言实现的,原因是C语言更加的好阅读,更容易实现复杂的功能,但是并没有是用glibc库中的任何函数,哪些比较常见的字符串操作和内存操作函数都是需要自己实现的,但是uboot都做好了。

    • 初始化现在需要使用的硬件设备,比如串口,usb,网络,主要是用来下载和打印log的,比如输出一些版本号,和调试信息。

    • 加载内核镜像,从mmc或者flash中读取镜像到RAM中。

    • 设置内核的启动参数,具体之后单独写一下。

    • 调用内核,一般都是直接跳转到内核的第一条指令处,在跳转的时候,一定要满足以下几个条件:

      • CPU寄存器的设置:

        • R0 = 0

        • R1=机器类型ID;我们直接看linux/arch/arm/tools/mach-types就可以了

        • R2=启动参数标记列表在RAM中起始及地址;

      • CPU模式设置:

        • 必须禁用中断

        • 必须处于SVC模式

      • Cache和mmu的设置:

        • MMU必须关闭

        • CACHE也要关闭

关闭为啥大家看一下詹荣开写的《嵌入式系统Bootloader技术内幕》这本书。

linux内核初始化

  1. 内核自解压

  2. 设置工作模式,以及使能MMU,设置一级页表啥的

    1. 内核自检,一般来说找到你的体系架构下的arch/arm(或者你的体系架构)/kernel 里面有一个head-xxx.S文件中

    2. 主要作用是检查CPU ID, Architecture Type,初始化BSS等操作,并跳到start_kernel函数,在执行前,处理器应满足以下状态:

      1.       r0 - should be 0

      2.   r1 - unique architecture number

      3.   MMU - off

      4.   I-cache - on or off

      5.   D-cache – off

  3. 运行C代码,内核启动的全部工作。

 Linux内核启动的第二阶段从start_kernel函数开始。start_kernel是所有Linux平台进入系统内核初始化后的入口函数,它主要完成剩余的与 硬件平台相关的初始化工作,在进行一系列与内核相关的初始化后,调用第一个用户进程- init 进程并等待用户进程的执行,这样整个 Linux内核便启动完毕。该函数位于init/main.c文件中,主要工作流程如图所示:

0fdb46a3a46bc80f007ab73752353ebf.png该函数所做的具体工作包括以下几项:

  • 调用setup_arch()函数进行与体系结构相关的第一个初始化工作,具体函数在/arch/xxx/kernel/setup-xxx.c里面,做的工作基本上都是一个套路,根据系统定义的meminfo结构进行内存结构的初始化,最后调用 paging_init()开启MMU,创建内核页表,映射所有的物理内存和IO空间。

  • 创建异常向量表和初始化中断处理函数

  • 初始化系统核心进程调度器和时钟中断处理机制;

  • 初始化串口控制台(console_init),但是不是说在上面这些操作中就不能够打印log了,linux内核的打印模块实际上也是很有意思的,他有一个缓存buf,会将这些log信息放到这里面然后等到串口初始化后从这个buf中取出来打印。

  • 创建和初始化系统cache,为各种内存调用机制提供缓存,包括;动态内存分配,虚拟文件系统(VirtualFile System)及页缓存

  • 初始化内存管理,检测内存大小及被内核占用的内存情况;初始化系统的进程间通信机制(IPC);当以上所有的初始化工作结束后,start_kernel()函数会调用rest_init()函数来进行最后的初始化,包括创建系统的第一个进程-init进程来结束内核的启动。

  • 挂载根文件系统并启动init

  • Linux内核启动的下一过程是启动第一个进程init,但必须以根文件系统为载体,所以在启动init之前,还要挂载根文件系统;

文件系统引导

  1. 实际上就是kernel将一个存储介质,U盘或者flash等等,挂载为根文件系统,就像我们mount一个设备为一个文件夹一样。

  2. 那kernel在执行init之后做的是什么,如图

    5ebdff9d56161f800ef354fc80cff33e.png

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值