嵌入式知识点一览

语言基础

关键字

static:

1.static关键字用来修饰函数内变量 使局部变量存储在全局静态区,只初始化一次,函数结束后可一直存在,可在函数内修改。(更改存储区域&延长生命周期)

局部变量

static修改局部变量主要是延长生命周期。

我们都知道,局部变量原本就有隐藏作用,仅对当前函数可见。

同时局部变量原本是存放在内存中的栈区,每次进入函数时创建,退出函数时销毁。

使用static修改局部变量后,将局部变量从栈区移至全局静态区,其生命周期为一直存在。

所以是更改存储区域导致的生命周期延长。

那么,问题来了。为什么要这么做呢?

举个栗子: 有一个局部变量LocalVar仅被某一个函数RepeatFunc调用。该函数会重复执行很多次,每次都会基于上次的结果,对LocalVar做修改,且保留LocalVar的值到下一次RepeatFunc执行。

一种简单粗暴的方法是直接将LocalVar定义为全局变量。但是这样做有两个隐患,一个是其他函数有可能调用并修改LocalVar从而导致运行结果出错;第二个是造成全局变量泛滥,对于大型代码工程来说是不可接受的。

因此,static修改的局部变量派上用场了。可以同时具有局部变量的隐藏特性,还具有长久的生命周期来保留上次的结果

2.static关键字用来修饰模块内变量  使全局变量对外部文件不可见。(隐藏)

全局变量

全局变量本身就存在于全局静态区,static关键字的作用是隐藏,更改全局变量的作用域

为什么要隐藏呢?

有的是考虑到重名的因素,但我个人认为还有一点是比较重要的,那就是处于安全的考虑。

比如有一个c文件名为Trusty.c,里面定义了各种对于用户密码的操作,由一个名为安全小组的团队维护。

那么我们知道,用户密码肯定是存放在一个全局变量PassWord中,方便各个函数调用操作。

PassWord在这个Trusty.c可以随意被调用,因为是由安全小组内部维护的,可以保证安全性。

但是,如果另外模块得知了PassWord这个变量名,就可以随意调用。

那么缺乏保护措施的另一个模块,很容易泄漏PassWord的内容,导致最重要的密码被窃取!这是绝对绝对不可以的!

此时就可以在Trusty.c中定义一个静态全局变量来对外部隐藏PassWord变量啦。

static char PassWord[10] = "XiaoYu666";

3.static关键字用来修饰模块内函数  表明函数的作用范围,仅当前.c文件内可被调用,对外不可见。(隐藏)

函数

我们首先要知道,编程规范中,建议每一个函数只负责执行一个动作,不要一个函数完成多个动作。这叫做低耦合

那么,同修饰全局变量中的例子,用户设置、更改和读取密码的过程中,会使用一系列中间函数来完成一个大的功能如加密&解密,同时返回一些关键信息。

如果此时外部模块调用这部分中间函数,非法获取它的返回值,就会造成关键信息泄漏。

这个是不被允许的!因此我们可以使用static关键字来修饰中间函数,从而对外隐藏它们。

只留给外部模块固定的API接口函数(设置、更改和读取密码),这样就能从代码编写层面来保护啦~

volatile:

定义

volatile是一种类型修饰符,表明该变量可能会被意想不到地改变(操作系统、硬件或者其它线程等),防止编译器对代码进行优化

功能

1.使编译器每次使用变量时不从寄存器中读取,而是从内存中重新获取

2.防止编译器调整操作volatile变量的指令顺序(无法避免CPU动态调度)

背景知识

1.通常情况下,为了提高运行速度和优化代码量,编译器可能优化读取和存储相关的代码。

2.程序运行中读取某一变量时,为提高存取速度,编译器优化时会先把变量读取到一个寄存器中,后续直接从该寄存器中取值(而非从内存中重新读取)。

1)硬件相关的寄存器(如:状态寄存器)

我们都知道,嵌入式软件之所以特别,就是因为要和硬件打交道。

现在有一个反映硬件状态的变量,由于外部因素改变了硬件状态从而修改了该变量的值。

那么此时该变量在内存中的数值和其在寄存器中的数值是不一致的。如果此时CPU需要读取该值,很可能因为编译器的优化而直接读取寄存器中的值,导致读取到的是"旧值"。

2)中断服务程序中可能会修改的变量

当变量在中断服务程序(ISR)中被修改后,而编译器判断主函数里面没有修改该变量,因此可能只会从该寄存器中读取变量的"旧值"。

3)多线程间共享的变量

当某一线程读取一个变量时,编译器优化时会先把该变量读取到寄存器中;后面再取变量值时,就直接从寄存器中取值;

当另外的线程改变了该值后,该寄存器的值不会相应改变。如果此时CPU需要读取该值,很可能读取到"旧值"。

通俗地说,小明(CPU)把自己的个人资料(变量)记录在一张纸上(寄存器)。

我们知道,姓名、性别、出生年月这些基本不会改变的属性(非volatile变量),一次记录(从内存copy到寄存器)后每次都可以重复使用。

然而身高、体重等随时可能改变的数据(volatile变量),每次必须要现场测量(从内存地址读取)才可以,不然只会得到之前的"旧值"。

extern:

1.extern关键字引用同一文件内的变量

2.extern关键字引用其他文件中的变量

3.extern关键字引用其他文件中的函数

const:

1.const关键字修饰变量则定义变量为常量的作用

2.const关键字修饰参数为常量

3.修饰返回值也是为常量

sizeof与strlen:

​ 1、sizeof是运算符,strlen是函数

​ 2、sizeof可以用类型、函数作为参数,strlen只能计算char*,还必须以/0结尾

​ 3、sizeof编译的时候计算,strlen是运行期计算,表示字符串长度,不是内存大小

struct与union:

 1、联合体公用一块地址空间,联合体变量长度等于最长的成员的长度

​ 2、对不同成员赋值,会将其他成员重写

define与typedef: 

1、都是替对象去一个别名,增强程序的可读性

2、define为预处理指令,不做正确性检查,只有带入之后才能发现

3、typedef用来定义类型别名,不止包含内部类型还包含自定义类型(与机器无关),方便记忆

4、define不仅可以给类型取别名,还能定义常量、变量、编译开关。

5、define没有作用域限制,typedef有

C语言内存分配方式

1、静态储存区分配

2、栈上分配

3、堆上分配

C++内存管理是怎样的

分为代码段、数据段、BSS段、堆区、栈区、文件映射区

代码段:分为只读区和文本区,只读取储存字符串常量,文本区储存机器代码。

数据段:储存以及初始化的全局变量和静态变量

BSS段:储存未初始化的全局变量和静态变量,以及初始化为0的全局和静态。

堆区:手动分配的内存

栈:局部变量参数返回值等

映射区:储存动态链接库,mmap函数的文件映射

堆和栈的区别

1、申请方式。 栈为操作系统自动分配/释放,堆为手动

2、申请大小,栈空间有限,向低地址拓展的连续区域,堆是向高地址拓展的不连续区域,链表储存的空闲地址。

3、申请效率,栈是系统自动分配,速度快,不可控。堆是由new分配,速度比较慢,容易产生内存碎片。

栈的作用

1、储存临时变量

2、多线程编程的基础。每个线程至少有一个栈用来存储临时变量和返回的值

避免内存泄漏方法

1、分配的内存以链表管理,使用完毕后从链表删除,程序结束的时候检查链表

2、良好的编程习惯,在设计内存的程序段,检验出内存泄漏,使用了内存分配的函数,使用完毕后将使用的相应函数释放掉

3、smart pointer

为什么堆的空间是不连续的?

1、堆包含一个链表来维护已用和空闲的内存块。

2、分配的空间在逻辑地址(虚拟地址)上是连续的,但在物理地址上是不连续的

什么是用户栈和内核栈?

内核栈 :内存中属于操作系统空间的一块区域。

作用:

1、保存中断现场

2、保存调用的参数、返回值、函数局部变量

用户栈:

用户进程空间的一块区域,用于保存用户空间子程序间调用的参数,返回值以及局部变量。

为什么不能共用一个栈:

1、系统栈(内核栈)大小有限用户程序调用次数可能很多。

2、用户栈空间不能提供相应保护措施

ARM体系和架构

NAND Flash和NOR Flash

NAND Flash和NOR Flash是两种常见的闪存技术,用于存储数据的非易失性存储器。它们在结构、性能和适用场景方面存在一些差异。

  1. 结构:

    • NAND Flash:采用串行结构,其中存储单元以矩阵形式排列,通过字节或页进行读写操作。
    • NOR Flash:采用并行结构,每个存储单元都有唯一的地址,可以直接访问任何位置。
  2. 存取方式:

    • NAND Flash:以页为单位进行读写,但必须擦除整个块才能修改数据。
    • NOR Flash:可以按字节随机读写,无需特定擦除操作。
  3. 速度和性能:

    • NAND Flash:具有较高的读取速度和较低的成本,适合大容量存储和快速数据传输。
    • NOR Flash:具有较快的随机访问时间,适用于需要频繁读写小块数据的应用。
  4. 适用场景:

    • NAND Flash:广泛应用于移动设备、固态硬盘(SSD)等需要大容量存储的设备。
    • NOR Flash:通常用于嵌入式系统、微控制器、引导程序存储等对实时读取和执行要求较高的应用。

总体而言,NAND Flash和NOR Flash代表了不同的闪存技术,适用于不同的存储需求和应用场景。NAND Flash在大容量存储和数据传输方面更具优势,而NOR Flash在随机读写和实时执行方面更为出色

CPU,MPU,MCU,SOC,SOPC联系与差别?

  1. CPU(中央处理器):

    • CPU是计算机系统中的主要处理单元,负责执行指令、处理数据和控制计算机的操作。
    • 它通常由控制单元、算术逻辑单元和寄存器组成,可以执行各种计算任务。
  2. MPU(微处理器):

    • MPU是一个较小规模的集成电路芯片,集成了CPU核心以及一些周边功能,如存储器管理单元(MMU)、高速缓存等。
    • MPU通常用于嵌入式系统或对功耗和尺寸有严格要求的应用,如移动设备、无人机等。
  3. MCU(微控制器):

    • MCU是一种集成了处理器核心、存储器、输入/输出接口和其他外设的单芯片微型计算机系统。
    • 它专门设计用于控制和执行嵌入式应用,如家电、汽车电子、工业自动化等。
  4. SoC(系统级芯片):

    • SoC是一种集成了多个硬件组件和功能的单一芯片,包括处理器核心、内存控制器、外设接口、网络控制器等。
    • SoC将多个功能模块集成在一起,以提供完整的计算和通信能力,常见于智能手机、平板电脑和物联网设备等。
  5. SoPC(可编程系统级芯片):

    • SoPC是一种基于FPGA(现场可编程门阵列)技术的可编程芯片,可以根据应用需求重新配置其硬件功能。
    • SoPC结合了硬件和软件的灵活性,使得开发者可以根据特定应用的需求进行自定义设计和优化。

CPU中cache的作用?cache的基本组织结构?

Cache在CPU中起到了缓存数据的作用,以提高CPU访问主内存的效率。由于主内存的访问速度相对较慢,而CPU执行指令和访问数据的速度较快,为了减少访问延迟,引入了cache作为位于CPU和主内存之间的高速存储器。

Cache的基本组织结构通常包括以下三个层次:

  1. Level 1 (L1) Cache(一级缓存):

    • L1 Cache是最靠近CPU核心的缓存层级,位于CPU芯片上。
    • 它通常分为指令缓存(Instruction Cache)和数据缓存(Data Cache),用于分别缓存指令和数据。
    • L1 Cache的容量相对较小,但访问速度非常快,可以在一个或几个CPU周期内响应CPU的请求。
  2. Level 2 (L2) Cache(二级缓存):

    • L2 Cache位于L1 Cache之后,通常位于CPU芯片上,但容量比L1 Cache大。
    • 它扩展了缓存容量,提供更多的存储空间,并且仍然具有较快的访问速度。
    • L2 Cache通常由多个缓存块组成,每个缓存块包含多个缓存行,用于存储数据和指令。
  3. Level 3 (L3) Cache(三级缓存):

    • L3 Cache位于L2 Cache之后,通常集成在CPU芯片的外部或作为共享缓存。
    • 它的容量更大,但相对于L1和L2 Cache,访问速度可能稍慢一些。
    • L3 Cache用于进一步扩展缓存容量,并提供更高级别的数据共享。

Cache的基本组织结构遵循缓存行、索引和标记的概念。

描述一下嵌入式基于ROM的运行方式和基于RAM的运行方式有什么区别?

嵌入式系统可以基于ROM(只读存储器)或RAM(随机存储器)运行。下面是基于ROM和基于RAM的运行方式之间的区别:

  1. 基于ROM的运行方式:

    • ROM是一种非易失性存储器,其中存储的数据在通电或重启后仍然保持。
    • 在基于ROM的运行方式中,程序代码和常量数据被预先存储在ROM中,无法被修改。
    • 由于ROM的特性,它提供了较快的启动时间,因为系统可以立即从ROM中开始执行代码。
    • ROM的容量相对较小,但它具有低功耗、高可靠性和抗干扰能力。
  2. 基于RAM的运行方式:

    • RAM是一种易失性存储器,其中存储的数据在断电时会丢失。
    • 在基于RAM的运行方式中,程序代码和数据被加载到RAM中进行执行。
    • RAM提供了更大的存储容量,允许灵活地存储和修改程序代码、变量和临时数据。
    • 由于RAM的读写速度较快,基于RAM的运行方式可以提供更高的灵活性和实时性。

主要区别:

  • 可编程性:基于ROM的运行方式受到存储在ROM中的固定程序代码和数据的限制,不可随意修改。而基于RAM的运行方式可以动态加载和修改程序代码和数据,提供更大的灵活性。
  • 存储容量:ROM的容量通常较小,适合存储固定的程序代码和数据。相比之下,RAM提供了较大的存储容量,允许存储更多的程序代码、变量和临时数据。
  • 可靠性:由于ROM是非易失性存储器,其数据在断电后仍然保持,因此具有较高的可靠性。而RAM是易失性存储器,数据需要定期保存到非易失性存储器中以防止丢失。

中断与异常区别

1、中断是指外部硬件产生的一个电信号从CPU的中断引脚进入,打断CPU的运行,异常是指软件运行过程中发生了一些必须作出处理的事件,CPU自动产生一个陷入来打断CPU的运行。

2、异常处理的时候要考虑与处理器的时钟同步,异常被称为同步中断

中断的响应执行流程是什么?

cpu接受中断->保存中断上下文跳转到中断处理历程->执行中断上半部->执行中断下半 部->恢复中断上下文

stm32的中断以及中断优先级

STM32是一系列由STMicroelectronics开发的32位ARM Cortex-M微控制器。它们在中断处理方面提供了灵活且强大的功能。下面我将简要介绍STM32中断的概念和中断优先级。

  1. 中断概念: STM32中断是一种机制,用于处理特定事件或外部信号的异步响应。当一个中断被触发时,CPU会立即暂停当前任务并转而执行与中断相关的中断服务程序(ISR)。中断可以来自多个来源,例如定时器溢出、外部GPIO信号变化、串口数据接收等。

  2. 中断优先级: STM32中断具有可编程的中断优先级。每个中断都有一个唯一的优先级值,范围从0到最大优先级值,通常为15。较低的数值表示较高的优先级。当多个中断同时发生时,具有最高优先级的中断将首先得到服务。

    STM32中的中断优先级分为两个级别:抢占优先级和子优先级。抢占优先级用于决定哪个中断能打断正在执行的中断,而子优先级用于决定在已打断的情况下,同一优先级的多个中断之间的执行顺序。

    在STM32中,每个中断都有一个独立的优先级寄存器,通过设置寄存器的值来配置中断的优先级。较低数值的优先级将具有较高的优先级。可以使用CMSIS(Cortex Microcontroller Software Interface Standard)提供的API,例如NVIC_SetPriority()函数来配置中断优先级。 

         

Linux驱动

Linux指令

查看当前进程 ps;

执行退出 exit;

查看当前路径 pwd;

查看目录 ls -a显示所有文件及目录,-l详细列出

创建目录 mkdir;

创建文件 vi 、 touch;

查看文件内容 vi,cat ;

屏幕输出 echo;

常用的GCC命令

gcc -E test.c -o test.i #把预处理的结果导出到test.i文件

gcc -S test.i -o test.s #编译器将test.i翻译成汇编语言,并将结果存储在test.s文件中。

gcc -c test.s -o test.o #将汇编代码编译为目标文件(.o)但不链接

gcc test.o -o test #将生成的目标文件test.o生成最终的可执行文件test

gcc test.c -o test #将源文件test.c编译链接为可执行文件test

gcc test1.c test2.c -o test 多文件编译

常用的GDB调试指令

gcc -g test.c -o test #编译时生成debug有关的程序信 就是说正常编译不能使用GDB

list 查看源码

next #单步调试(逐过程,函数直接执行),简写n
step #单步调试(逐语句:跳入自定义函数内部执行),简写s

run #运行程序

break + num #设置第num行 为断点

continue #继续运行到下一个断点。

display 追踪具体变量值

delete breakpoints num #删除第num个断点

常用的驱动开发指令

insmod\modprobe 加载驱动

rmmod #卸载驱动

lsmod #查看已有的字符设备信息

cat /proc/interrupt #查看已有的中断号

uboot启动流程

u-boot系统启动流程 ,大多数bootloader都分为stage1和stage2两部分, u-boot也不例外。

依赖于CPU体系结构的代码(如设备初始化代码等)通常都放在stage1且可以用汇编语言来实现,而stage2则通常用C语言来实现,这样可以实现复杂的功能,而且有更好的可读性和移植性。

1、Stage1 start.S代码结构 u-boot的stage1代码通常放在start.S文件中,他用汇编语言写成,其主要代码部分如下

(1) 定义入口,该工作通过修改连接器脚本来完成。

(2)设置异常向量(Exception Vector)。

(3)设置CPU的速度、时钟频率及终端控制寄存器。

(4)初始化内存控制器。

(5)将ROM中的程序复制到RAM中。

(6)初始化堆栈。

(7)转到RAM中执行,该工作可使用指令ldr pc来完成

2、Stage2

C语言代码部分 lib_arm/board.c中的start arm boot是C语言开始的函数也是整个启动代码中C语言的主函数,同时还是整个u-boot(armboot)的主函数,该函数只要完成如下操作:

(1)调用一系列的初始化函数。

(2)初始化Flash设备。

(3)初始化系统内存分配函数。

(4)如果目标系统拥有NAND设备,则初始化NAND设备。

(5)如果目标系统有显示设备,则初始化该类设备。

(6)初始化相关网络设备,填写IP、MAC地址等。

(7)进去命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后进行相应的工           作

uboot启动过程中做了那些事?

1、初始化时钟,关闭看门狗,关中断,启动ICACHE,关闭DCACHE和TLB,关闭MMU,初始化SDRAM,初始化NAND FLASH,重定位。

2、初始化一个串口,检测系统内存映射,将内核映象和根文件系统映象从 Flash上读到SDRAM空间中,为内核设置启动参数,调用内核

详细解释:

  1. 初始化时钟:设置系统主时钟,确保系统以正确的频率运行。

  2. 关闭看门狗:如果系统有看门狗(Watchdog)功能,需要在启动过程中关闭它,以避免不必要的重启。

  3. 关中断:在启动过程中可能需要临时关闭中断,以避免干扰和冲突。

  4. 启动ICACHE:启用指令缓存(Instruction Cache),以加快代码执行速度。

  5. 关闭DCACHE和TLB:禁用数据缓存(Data Cache)和转换后备缓冲器(Translation Lookaside Buffer),以确保启动过程中的内存操作的一致性。

  6. 关闭MMU:内存管理单元(Memory Management Unit)控制虚拟内存到物理内存的映射,关闭MMU会将系统切换到物理地址模式。

  7. 初始化SDRAM:初始化系统中的SDRAM(Synchronous Dynamic Random-Access Memory),包括配置时序和参数等,以便正常使用内存。

  8. 初始化NAND FLASH:如果系统使用了NAND Flash作为存储介质,需要进行NAND Flash的初始化,包括配置和检测。

  9. 重定位:将加载的引导程序或其他代码从其加载地址重定位到正确的内存地址,以便后续执行。

  10. 初始化一个串口:配置和初始化一个串口接口,以便与外部设备进行通信,如调试信息输出或用户交互。

  11. 检测系统内存映射:检测系统的内存布局和映射情况,确保正确访问系统内存。

  12. 读取内核镜像和根文件系统镜像:从Flash等存储介质上读取内核映像和根文件系统映像,并将它们加载到SDRAM空间中。

  13. 设置内核启动参数:为内核设置必要的启动参数,例如根文件系统的位置、内核命令行参数等。

  14. 调用内核:通过设置CPU寄存器等方式将控制权传递给加载的内核,从而启动操作系统的启动流程。

uboot和内核如何完成参数传递?

  1. uboot设置CPU寄存器:在启动过程中,uboot会进行一系列的初始化操作。在这个阶段,uboot会设置CPU的寄存器。具体来说,uboot会将R0设置为0,R1设置为机器类型ID,并将R2设置为启动参数标记列表在RAM中的起始基地址。此外,uboot还会禁止中断并将系统模式设置为SVC模式(超级用户模式),这有利于硬件的初始化。同时,MMU(内存管理单元)也会关闭。

  2. uboot通过寄存器传递参数给内核:uboot会通过CPU的寄存器将参数传递给内核。具体而言,uboot将机器类型ID存储在R1中,将块内存的基地址存储在R2中。这块内存主要用于存放uboot传递给Linux内核的其他参数。由于参数数量较多,需要按照特定规定进行存放。这些参数通常被称为"标记",是一种特定的数据结构。

  3. 参数标记的数据结构:参数标记使用一个称为"tag"的数据结构来表示。它由一个tag_header结构和一个联合(union)组成。tag_header结构包含了有关标记的元信息,如标记类型和长度等。联合则提供了不同类型的标记数据存储方式,以适应不同的参数类型

为什么uboot要关掉caches?

在启动过程中,uboot关闭caches的主要原因是为了避免不一致性和预取异常。

Caches是CPU内部的高速缓存,用于提高数据和指令的访问速度。然而,在刚上电时,CPU还不能正确管理和同步caches。如果在这个阶段不关闭数据cache,当执行代码从内存中读取数据时,由于数据还没有被正确缓存到caches中,就会导致数据的不一致性。

具体来说,在刚开始的代码中,如果数据cache是开启的,CPU可能会尝试从cache中获取数据,而此时RAM中的数据还没有被正确地加载到cache中,导致获取的数据与期望值不一致。这种不一致性会引发各种问题,包括错误的计算结果、不可预测的行为和系统崩溃等。

另外,关闭caches还可以避免预取异常。预取是一种优化技术,用于提前将数据从内存中加载到cache中,以提高后续访问的速度。然而,在启动过程中,由于内存的初始化和其他操作,预取可能会引发异常或不正确的数据加载,进而导致系统运行出现问题。通过关闭caches,可以避免这些预取异常带来的负面影响

什么是根文件系统?

1、内核启动时所挂载(mount)的第一个文件系统,内核代码的映像文件保存在根文件系统中。

2、挂载之后会把一些初始化脚本和服务加载到内存中去运行。

根文件系统为啥这么重要?

1、根文件系统包含系统启动时所必须的目录和关键性的文件,以及使其他文件系统得以挂载(mount)所必要的文件。比如shell命令程序必须运行在根文件系统上,譬如ls、cd等命令。

2、一套linux体系,只有内核本身是不能工作的,必须要rootfs(上的etc目录下的配置文件、/bin
/sbin等目录下的shell命令,还有/lib目录下的库文件等)相配合才能工作

硬中断 / 软中断是什么?有什么区别?

1、硬中断是由硬件产生的,软中断是执行中断指令产生的。

2、硬中断可以直接中断CPU,软中断并不会直接中断CPU。也只有当前正在运行的代码(或进程)才会产生软中断。

3、硬中断可屏蔽、软中断不可屏蔽

4、硬中断又称上半部,要快速完成任务

中断为什么要区分上半部和下半部?

1、调用过程:外部中断产生->发送中断信号到中断控制器->通知处理器产生中断的中断号

2、为了能被新的中断打断。将中断处理一分为二,上半部登记新的中断,处理快速简单的任务,复杂耗时的任务给下半段处理,所以下半段可以被打断。

3、中断下半部一般使用tasklet或工作队列实现

linux中断的响应执行流程?

  1. CPU接收中断信号:当硬件设备触发一个硬中断时,CPU会接收到中断信号。

  2. 保存中断上下文并切换到中断处理过程:在接收到中断信号后,CPU会首先保存当前正在执行的进程或代码的上下文(包括寄存器值、程序计数器等),然后切换到中断处理过程。

  3. 执行中断上半部:中断处理过程分为上半部和下半部。在中断处理过程的上半部,主要进行一些紧急的、必要的、时间敏感的操作,例如快速响应硬件事件、保存关键数据等。上半部的执行通常需要尽量快速完成,以确保系统的响应性能。

  4. 执行中断下半部(可选):在某些情况下,一部分中断处理任务可能无法在中断上半部完成,或者需要延迟执行以减少中断上半部的执行时间。这些任务可以在中断下半部或者叫做延迟处理过程中执行。中断下半部的执行可以在中断处理过程结束后、在合适的时机、在离开中断上下文之前执行。

  5. 恢复中断上下文:在中断处理过程完成后,CPU会从中断处理过程返回到之前被中断的进程或代码,并恢复之前保存的上下文。这样,被中断的进程或代码可以继续执行。

Linux驱动模型

Linux驱动模型基于字符设备驱动和块设备驱动两种主要类型的设备驱动。下面是关于这两种驱动类型的简要介绍:

  1. 字符设备驱动:字符设备驱动用于管理处理数据流的设备,其中数据按字节流进行传输,没有固定的块结构。例如,串口、键盘和鼠标等设备被视为字符设备。字符设备驱动通过文件接口(如/dev/tty)向用户空间提供访问设备的能力。

  2. 块设备驱动:块设备驱动用于管理以块为单位的设备,例如硬盘驱动器。块设备支持随机存取,可以对数据进行分块读写。块设备驱动通过块设备接口(如/dev/sda)向用户空间提供访问设备的能力

Linux设备中字符设备和块设备有什么主要区别?

字符设备:提供连续的数据流,应用程序可以顺序读取,通常不支持随机存取

块设备:应用程序可以随机访问设备数据,程序可自行确定读取数据的位置

驱动中操作物理绝对地址为什么要先ioremap?

1、ioremap是将io地址空间映射到虚拟地址空间上去,便于操作。

2、因为保护模式下的cpu只认虚拟地址,不认物理地址,所以你要操作外设上的寄存器必须先映射到虚拟内存空间,拿着虚拟地址去跟cpu对接,从而操作寄存器

Linux移植ARM的基本步骤和完成的任务

1)首先是准备工作,包括下载源码、建立交叉编译环境等;
2)然后是配置和编译内核,必要时还要对源码做一定的修改;
3)第三步就是需要制作文件系统(如RAM disk)来挂接根文件系统;
4)最后是下载、调试内核并在fs中添加自己的应用程序。

  1. 了解目标硬件:首先需要详细了解目标ARM硬件平台的技术规格和特性,包括处理器型号、内存布局、外设接口等。这将有助于后续的移植工作。

  2. 准备交叉编译工具链:ARM架构上的Linux移植通常需要使用交叉编译工具链,以在主机系统上生成适用于目标ARM平台的可执行文件和库。选择合适的交叉编译工具链,并进行配置和安装。

  3. 获取内核源代码:获取适用于ARM架构的Linux内核源代码,可以从官方内核网站或其他开放源代码项目中获取。确保选择与目标硬件兼容的内核版本。

  4. 配置内核:根据目标硬件的特性,进行内核的配置。这包括启用适当的内核选项、设备驱动程序和功能,禁用不需要的组件,以及对内存布局和硬件接口进行配置。

  5. 进行交叉编译:使用之前准备好的交叉编译工具链,对内核源代码进行交叉编译,生成针对ARM目标平台的内核映像(zImage或uImage)和模块。

  6. 准备引导加载程序:根据目标硬件平台的要求,准备适当的引导加载程序(例如U-Boot)。引导加载程序负责加载内核映像,设置必要的初始化参数,并将控制权传递给内核。

  7. 测试和调试:将生成的内核映像和引导加载程序烧录到目标ARM设备上,并进行测试和调试。确保系统能够正确启动并正常运行,验证硬件驱动程序、网络连接、存储设备等功能是否正常工作。

  8. 文件系统:选择适当的文件系统类型(如ext4、YAFFS2等),并进行配置和编译。为目标设备提供根文件系统镜像,以支持用户空间程序的运行。

  9. 用户空间程序:根据需求,在根文件系统中添加所需的用户空间程序和库。这些程序可以是基本的shell、命令行工具,或者更复杂的应用程序。

  10. 完善移植:根据实际需求,进一步完善移植工作,可能包括优化性能、添加额外的设备驱动程序、定制启动脚本等。

ARM-linux启动分几部分,简述流程:

  1. 引导加载程序(Bootloader):在系统加电后,引导加载程序是第一个被执行的代码。它位于ROM或Flash存储器中,负责初始化硬件、加载内核映像并将控制权传递给内核。常见的ARM平台引导加载程序有U-Boot和Das U-Boot等。

  2. 引导加载程序初始化:引导加载程序负责初始化设备树、设置内存映射、配置时钟、初始化串口等硬件操作,并加载内核映像到内存中准备启动。

  3. 内核加载与启动:引导加载程序将内核映像从存储介质(如SD卡、NAND闪存等)加载到内存中的指定地址。然后,引导加载程序通过设置寄存器和堆栈,将控制权转移到内核的入口点。

  4. 内核初始化:内核开始执行后,首先会进行一系列的初始化操作,包括初始化调度器、内存管理子系统、设备驱动程序和文件系统等。

  5. 根文件系统挂载:内核初始化后,它会尝试挂载根文件系统。根文件系统通常位于存储介质上,可以是RAM disk、NFS网络文件系统、SD卡等。挂载成功后,用户空间程序就可以通过根文件系统进行访问。

  6. 用户空间初始化:一旦根文件系统成功挂载,内核会启动第一个用户空间进程——init进程。init进程是用户空间的第一个进程,负责加载其他用户空间进程和服务。

总结起来,ARM-Linux启动流程大致为引导加载程序初始化 -> 内核加载与启动 -> 内核初始化 -> 根文件系统挂载 -> 用户空间初始化。每个步骤都涉及到硬件初始化、内存管理、设备驱动加载和用户空间启动等关键操作,以确保系统能够正确启动并运行。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

网友小浩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值