嵌入式软件工程师养成计划(Eembedded Sodtware Engineer Culture Program)
0. 大纲
夫计先定而后动者,胜;动而后计者,败
- 大纲
- 总论
- 从上下文切换出发学处理器架构
2.1. 何谓上下文切换
2.2. 去哪里获取上下文切换的参考源码
2.1.1. 何处会用到上下文切换
2.2.2. 从指令集架构手册中获取
2.2.3. 从 uboot 等 boot 软件目录中获取
2.2.4. 从 freeRTOS 等 RTOS 目录中获取
2.3. 如何从参考源码获知不同处理器架构的差异
2.3.1. 指令集架构的组成
2.3.2. 寄存器文件
2.4. 怎样深入学习特定处理器架构及某个实现
2.4.1. 架构(Architecture)和实现(Implementation)的关系
2.4.2. 获取指令集说明/编程指南/技术参考手册
2.4.3. 把握异常处理机制
2.4.4. 了解访问控制机制
2.4.5. 明确中断控制机制
2.4.6. 实操高速缓存和内存管理机制
2.4.7. 关注 64 位架构对 32 位指令集的兼容
2.5. 实战:轻瞥 ARMv8 架构 Cortex-a53 处理器芳容 - 从应用软件抽象出发学操作系统
3.1. 操作系统即对硬件的抽象
3.1.1. 调度器即对处理核的抽象
3.1.2. 时间管理模块即对定时器的抽象
3.1.3. 内存管理模块即对内存的抽象
3.1.4. 文件系统即对存储器/磁盘的抽象
3.1.5. 驱动框架即对设备的抽象
3.2. 操作系统分类
3.2.1. 术语辨析
3.2.2. 桌面操作系统和实时操作系统的关联
3.3. 操作系统是一种软件编程范式
3.3.1. 精简开发是使用操作系统的初心使命
3.3.2. 借助调度器实现多任务协同
3.3.3. 借助文件系统进行数据存取
3.3.4. 了解设备驱动和设备树
3.4. 实战:一招吃遍多任务编程 - 从需求分析出发学协同软件设计
4.1. 搞清软硬件协同设计的原因和目的
4.2. 拆解需求
4.3. 软硬件任务划分
4.4. 实时性分析
4.5. 实战:录音笔方案臆想
1. 总论
2. 从上下文切换学处理器架构
2.1. 何谓上下文切换
上下文切换是计算机操作系统中的一种机制,用于在多任务环境中实现进程或线程之间的切换。当操作系统决定将 CPU 时间从当前运行的进程或线程转移到另一个进程或线程时,需要保存当前进程或线程的状态信息(包括程序计数器、寄存器、栈指针等)并加载下一个进程或线程的状态信息,这个过程就是上下文切换
以下是上下文切换的详细步骤:
保存当前进程/线程的上下文信息:操作系统需要保存当前进程/线程的寄存器、程序计数器和堆栈指针等关键状态信息,以便在之后执行此进程/线程时能够恢复它的执行现场。
加载新进程/线程的上下文信息:操作系统需要加载即将执行的新进程/线程的关键状态信息,包括寄存器、程序计数器和堆栈指针等信息,以准备该进程/线程的执行。
更新内存管理数据结构:由于进程/线程需要使用内存,因此操作系统需要更新相应的内存管理数据结构,以确保每个进程/线程都可以访问其所需的内存资源。
将CPU控制权转移给新进程/线程:操作系统最后需要将CPU控制权转移到新的进程/线程,使其开始执行。
这些步骤必须严格按照顺序执行,以确保正确地切换进程/线程,并尽可能地减少上下文切换的开销。
2.2. 去哪里获取上下文切换的参考源码
2.2.1 何处会用到上下文切换
上下文切换通常发生在以下情况中:
当操作系统决定将 CPU 时间从当前进程或线程转移到另一个进程或线程时。
当当前进程或线程让出 CPU 时间,例如因为它正在等待 I/O 操作完成而被阻塞。
当多个进程或线程共享同一 CPU 时,操作系统需要周期性地切换它们的执行顺序,以保证所有进程或线程都能得到足够的 CPU 时间。
上下文切换是指计算机操作系统在多任务处理中,由于需要进行进程间的切换而进行的一种机制。因此,操作系统本身就是一个使用上下文切换的软件。当有多个进程同时运行时,操作系统需要通过上下文切换来分配CPU时间片,保证每个进程都能得到执行的机会。
除了操作系统,以下是一些常见的使用上下文切换的软件:
数据库管理系统(DBMS):DBMS通常需要处理大量的并发请求,因此也需要使用上下文切换来在多个线程之间进行切换。
网络服务器:网络服务器需要同时处理多个客户端请求,这也需要使用上下文切换来实现多任务处理。
图形用户界面(GUI)应用程序:GUI应用程序通常需要同时响应用户的多个操作请求,例如鼠标、键盘输入等,因此也需要使用上下文切换来实现多任务处理。
2.2.2 从指令集架构手册中获取
要从指令集架构手册中获取上下文切换的说明,您需要遵循以下步骤:
确定所使用的处理器架构和版本。例如,如果您正在使用英特尔 x86 处理器,则需要查找该处理器的相关文档。
在供应商的网站上搜索指令集架构手册或开发人员手册。这些文档通常包含有关处理器架构和其功能的详细信息。
找到关于上下文切换的章节或部分。这可能会在“操作系统支持”、“中断和异常处理”或类似部分中找到。
阅读有关上下文切换的描述和相关指令。这些章节经常包括有关如何保存和恢复处理器寄存器状态的信息,以及如何通过指令触发上下文切换。
查看示例代码或操作系统实现的参考资料,以更好地了解如何在实践中进行上下文切换。
主流的指令集架构供应商包括:
Intel Corporation:Intel是全球最大的微处理器制造商之一,其x86架构是PC市场和服务器市场的主要指令集。Intel官网为www.intel.com。
Advanced Micro Devices, Inc. (AMD):AMD也是一家微处理器制造商,其官网为www.amd.com。AMD的x86-64架构在近年来已经成为了主流。
ARM Holdings plc:ARM是英国公司,提供基于ARM架构的低功耗处理器设计。ARM架构广泛用于移动设备、网络设备等领域。ARM的官网为www.arm.com。
IBM Corporation:IBM是一家综合性科技公司,其POWER架构是商业级别和高性能计算市场的主要指令集。IBM的官网为www.ibm.com。
NVIDIA Corporation:NVIDIA是一家图形处理器制造商,其GPU架构有CUDA和OpenCL两种编程模型,常用于科学计算和人工智能领域。NVIDIA的官网为www.nvidia.com。
Qualcomm Technologies, Inc.:Qualcomm是一家移动通信芯片制造商,其Snapdragon系列芯片使用基于ARM的指令集架构。Qualcomm的官网为www.qualcomm.com。
2.2.3 从 uboot 等 boot 软件目录中获取
U-Boot是一种开源的、嵌入式系统中常用的引导加载程序(bootloader),它主要负责在系统启动时初始化硬件设备、加载操作系统内核及根文件系统,并提供一些调试、配置和维护功能。U-Boot最初是为ARM架构的嵌入式系统设计的,但后来逐渐扩展到支持多种处理器架构,如x86、MIPS、PowerPC等。
上下文切换通常是由操作系统负责实现。因此,大多数情况下,U-boot仅提供了有限的支持,例如在启动操作系统时设置堆栈指针和程序计数器(PC)等寄存器。
在一些特殊的场景下,如在U-boot中实现虚拟化或操作系统引导加载器(bootloader)时,上下文切换可能会更为重要。在这种情况下,U-boot会提供一些接口用于保存和恢复CPU上下文,并且需要仔细处理中断和异常处理机制。
在u-boot源码中,上下文切换的代码通常分散在多个目录和文件中,因为不同的处理器架构和操作系统可能有不同的上下文结构和切换方式。但是一般来说,跟上下文切换相关的代码可以在以下目录下找到:
arch/: 各种处理器架构相关的代码,包括上下文切换和寄存器保存/恢复等
include/: 头文件目录,包含各种定义和宏,例如上下文结构体定义
common/: 通用代码目录,包含一些与上下文切换相关的通用代码实现,例如栈操作、异常处理等。
具体来说,对于ARM架构的u-boot,上下文切换的代码可以在以下文件中找到:
arch/arm/cpu/armv8/start.S: 包括启动代码和上下文切换相关汇编代码,例如EL1和EL0模式之间的切换
arch/arm/include/asm/reg.h: 定义了各种寄存器和状态位的宏,用于保存和恢复上下文
common/board_r.c: 实现了board_init_r()函数,在其中进行了一些初始化,例如设置栈指针,保存当前上下文等。
需要注意的是,不同版本的u-boot可能会有细微的差别,因此具体的代码位置和实现可能会略有不同。
U-Boot的官网是http://www.denx.de/wiki/U-Boot/,也可以通过http://www.uboot.org/访问到U-Boot的官方网站。该网站提供了U-Boot的最新版本下载、文档、邮件列表等资源和支持。Denx公司是U-Boot的原始开发者之一,因此它维护了一个U-Boot的Wiki页面,提供关于U-Boot的详细信息、常见问题解答以及各种应用场景的示例。除此之外,U-Boot在Github上也有一个官方代码仓库(https://github.com/u-boot/u-boot )。
2.2.4 从 freeRTOS 等 RTOS 目录中获取
FreeRTOS是一个开源的实时操作系统(RTOS),旨在为嵌入式系统提供轻量级、可移植和可扩展的解决方案。它由一组C语言库函数组成,可以在各种微处理器架构和编译器上运行。
在FreeRTOS的目录结构中,上下文切换的代码通常位于内核端口(kernel/portable)目录下。具体来说,不同的处理器架构有不同的内核端口文件夹,例如:
port.c和portmacro.h文件包含了用于ARM Cortex-M处理器的上下文切换代码。
在x86架构上,可以在port.c和portmacro.h文件中找到基于软件的上下文切换代码。
FreeRTOS的官网是http://www.freertos.org。该网站提供了关于FreeRTOS实时操作系统的详细信息,包括下载、文档、示例代码、支持论坛、社区贡献和商业支持等内容。
其他开源的实时操作系统可以从下面这些网址找到:
Zephyr:一个轻量级、可扩展的实时操作系统,适用于多种硬件架构和平台。其主页为https://www.zephyrproject.org/。
RT-Thread:一个快速、可靠的实时操作系统,支持多种处理器架构和实时调度算法。其主页为https://www.rt-thread.org/。
NuttX:一个可移植的实时操作系统,使用ANSI C编写,支持多种处理器和架构。其主页为http://nuttx.org/。
eCos:一个可裁剪的实时操作系统,使用C和C++编写,支持多种处理器和平台。其主页为http://ecos.sourceware.org/。
2.3 如何从参考源码获知不同处理器架构的差异
2.3.1 指令集架构的组成
指令集架构是计算机系统中的一个重要概念,包含以下几个方面:
指令集:指令集是一组定义了计算机操作的指令。它告诉计算机如何执行基本的运算、存储和传输数据等操作。指令集通常包括操作码(opcode)、操作数(operand)和寻址模式(addressing mode)等部分。
寄存器文件:寄存器文件是用来存储CPU内部数据的高速存储器。它们可以被CPU直接访问,因此能够提高计算机指令的执行速度。寄存器文件包括通用寄存器、特定功能寄存器等。
存储器地址空间:存储器地址空间是指计算机系统中所有可寻址的存储单元的集合。每个存储单元都有一个唯一的地址。这个地址空间可以被分为不同的区域,例如程序区、数据区和堆栈区等。
处理器模式:处理器模式指的是CPU在不同的状态下的工作模式。不同的处理器模式可以让CPU执行不同的操作或者访问不同的资源。常见的处理器模式包括用户模式、内核模式和超级用户模式等。
异常处理:异常处理是指当计算机系统中出现错误、中断或其他不正常情况时,系统如何响应和处理。异常处理包括中断处理、错误处理和系统调用等操作。
2.3.2 从寄存器文件出发看差异
寄存器文件是计算机处理器中的一种硬件组件,用于存储和管理处理器中的寄存器。寄存器是一种非常快速的内存单元,用于在处理器中存储数据和指令。寄存器文件通常包含多个独立的寄存器,每个寄存器可以存储一个特定类型的数据,如整数、浮点数、地址等。
寄存器文件的主要作用是提供高速缓存,以便处理器能够更快地访问常用的数据和指令。寄存器文件还可以通过直接对寄存器进行读写操作来执行一些简单的算术和逻辑操作,从而减少处理器需要执行的指令数量,提高计算机的性能。
ARMv7架构的寄存器文件由16个32位通用寄存器(R0-R15)、1个程序计数器(PC)、1个当前程序状态寄存器(CPSR)和1个保存上一个程序状态寄存器的备份(SPSR)组成。具体来说,这些寄存器包括:
R0-R12:共有13个通用寄存器,用于存储数据或者地址。R13(SP):栈指针寄存器,用于指向当前栈的栈顶。R14(LR):链接寄存器,用于存储函数调用后返回地址。R15(PC):程序计数器,存储下一条即将执行的指令的地址。CPSR:当前程序状态寄存器,用于存储当前处理器的状态信息,如标志位(比如进位标志、零标志等)以及当前CPU的模式(用户模式、特权模式等)。SPSR:保存上一个程序状态寄存器的备份,用于在异常(如中断或者系统调用)发生时保存当前状态。
在armv7架构中,上下文切换需要保存和恢复以下寄存器:
通用寄存器 r0-r12:这些寄存器用于保存程序状态和数据。在上下文切换时,需要将当前任务的这些寄存器的值保存到堆栈中,并从堆栈中恢复新任务先前保存的寄存器值。栈指针寄存器 sp:这个寄存器指向当前任务使用的栈的顶部。在上下文切换时,需要将当前任务的栈指针值保存到堆栈中,并从堆栈中恢复新任务先前保存的栈指针值。程序计数器寄存器 pc:这个寄存器保存了当前任务正在执行的指令地址。在上下文切换时,需要将当前任务的程序计数器值保存到堆栈中,并从堆栈中恢复新任务先前保存的程序计数器值。状态寄存器 cpsr:这个寄存器保存了当前任务的处理器状态,例如条件码、中断使能等。在上下文切换时,需要将当前任务的状态寄存器值保存到堆栈中,并从堆栈中恢复新任务先前保存的状态寄存器值。
x86架构的寄存器文件包含16个32位通用寄存器、6个段寄存器、2个指令指针寄存器和标志寄存器。
其中,16个通用寄存器可以分为以下三类:
数据寄存器:EAX、EBX、ECX和EDX。它们可以被用来存储整数值。指针寄存器:ESP和EBP。ESP(堆栈指针)指向当前堆栈顶部,EBP(基址指针)指向当前堆栈帧的基地址。变址寄存器:ESI和EDI。它们可以被用来存储内存访问的源和目的地址。
6个段寄存器包括:
CS、DS、SS、ES、FS和GS。它们存储了内存访问时的段基址,用于在物理内存地址和逻辑内存地址之间进行转换。
另外,x86还有两个指令指针寄存器:
EIP(指令指针寄存器)存储了当前正在执行的指令的地址。EFLAGS(标志寄存器)存储了处理器状态的各种标志,例如进位标志、零标志、符号标志等。
在x86架构下,上下文切换(Context Switch)涉及到的寄存器有以下几个:
EAX:通用寄存器,用于保存函数返回值。EBX、ECX、EDX、ESI、EDI、EBP:通用寄存器,用于保存函数参数、局部变量和临时数据。ESP:堆栈指针寄存器,用于指向当前进程的堆栈顶部。EIP:指令指针寄存器,用于指向将要执行的下一条指令。EFLAGS:标志寄存器,用于保存处理器状态标志,如进位标志、零标志等。
RISC-V的寄存器文件包含32个32位寄存器,其中x0恒等于零。这些寄存器可以分为以下几类:
整数寄存器:x1~x31是通用整数寄存器,用于存储整数数据和指针。浮点寄存器:f0~f31是浮点寄存器,用于存储单精度浮点数或双精度浮点数。特殊寄存器:pc(程序计数器)存储着指令的内存地址;csr(控制状态寄存器)用于控制CPU的状态和行为,例如异常处理、中断和时钟等。零寄存器:x0始终为零,不能被写入。它在某些指令中有特定的含义,例如与立即数相加时结果为立即数本身。
在 RISC-V 中,上下文切换需要保存和恢复以下寄存器:
x1 线程指针寄存器(Thread Pointer Register),用于指向当前线程的控制块。x5 至 x7 寄存器,用于存储函数调用相关的参数和返回值。x8 到 x9 寄存器,用于存储异常处理程序的参数。x10 到 x11 寄存器,用于存储正常的函数调用中的临时变量。x12 到 x17 寄存器,用于存储不被调用者保存的寄存器。x18 到 x27 寄存器,用于存储被调用者保存的寄存器。x28 到 x31 寄存器,用于存储平台特定的寄存器。
2.4 怎样深入学习特定处理器架构及某个实现
2.4.1 架构(Architecture)和实现(Implementation)的关系
先做个辨析,ARM(Advanced RISC Machines)是一种基于精简指令集(RISC)架构的处理器设计,其架构(Architecture)定义了处理器的指令集和运行方式,而实现(Implementation)则指的是将这些指令集和运行方式转化为硬件电路的过程。
这里以前面我们提到的armv8指令集架构和处理器cortex-a53为例说明。
ARMv8是一种指令集架构,ARM Cortex-A53是基于ARMv8架构的一个处理器核心。ARMv8-A则是一种扩展的ARMv8架构,包含了与虚拟化、安全性和大型系统相关的特性。
具体来说,ARMv8是一种32位和64位处理器架构,支持Aarch32和Aarch64指令集,并提供了虚拟化和安全性扩展特性。ARM Cortex-A53则是一种基于ARMv8架构的低功耗、高效能的应用处理器核心。而ARMv8-A则在ARMv8的基础上增加了诸如AArch64状态下的EL3异常级别、提高的安全性、可选的大型系统内存管理单元等特性。
因此,可以说ARM Cortex-A53 是基于ARMv8-A架构的一个处理器核心,支持ARMv8-A特性。
2.4.2 获取指令集说明/编程指南/技术参考手册
架构参考手册 (ARM):此类手册提供系统架构的高级概述,包括其设计理念、目标和功能。它通常包含有关系统指令集、内存组织、I/O 接口以及影响其整体操作的其他设计特征的信息。
程序员指南 (PG) :程序员指南比 ARM 更详细,它通常提供有关如何使用其指令集、API 和其他软件工具对特定系统进行编程的信息。它可能包括代码示例、编程技巧和性能优化技术。
技术参考手册 (TRM):TRM 包含有关系统组件的详细技术信息,例如寄存器、总线、中断控制器和其他硬件模块。这种类型的手册通常由需要了解系统内部工作的硬件工程师、固件开发人员和软件设计人员使用。
总之,ARM 用于提供对系统设计和功能的高级理解,而 PG 则提供有关如何使用系统软件接口的更多详细信息。TRM 用于提供有关系统内部工作的低级技术详细信息,包括其硬件和固件组件。这些文档通常是一起创建的,并且可以相互依赖,它们之间有交叉引用。
ARM 官方网站的文档中心网址为 https://developer.arm.com/documentation, 可以找到包括处理器架构、软件开发工具和系统设计等各种类型的文档。
2.4.3 把握异常处理机制
处理器的异常处理机制是指在执行计算机指令过程中,如果发生了某些意外情况(如除零、内存访问越界等),就会触发一个异常信号。此时处理器会暂停当前正在执行的指令,转而执行异常处理程序来处理这个异常。
异常处理程序可以是操作系统内核提供的默认程序,也可以是应用程序自己提供的处理程序。当处理器接收到异常信号后,会根据预先定义的异常向量表(Exception Vector Table)跳转到对应的异常处理程序。在处理程序中,系统会根据异常类型进行相应的错误处理,如输出错误信息、恢复现场、终止程序等。
处理器的异常处理机制能够保证系统的稳定性和可靠性。它可以防止由于程序错误或硬件故障导致系统崩溃,同时也为开发者提供了一种灵活的方式来处理各种异常情况。
2.4.4 了解访问控制机制
处理器的访问控制机制是一种硬件和软件结合的安全措施,用于保护处理器对系统资源的访问权限。其目的是确保只有被授权的程序或者用户才能够访问受保护的资源,从而防止未经授权的访问、恶意代码攻击和其他安全威胁。
处理器的访问控制机制包括以下几个方面:
权限级别:处理器通过分配不同的权限级别来限制访问受保护的资源。通常,处理器将系统划分为内核态和用户态两种权限级别,并为每种权限级别提供了不同的指令集和特权级别。
特权模式:处理器支持不同的特权模式,如监管模式、虚拟模式、保护模式等。这些模式可以在硬件和软件层面上管理系统资源的访问权限。
中断和异常处理:处理器可以检测到各种中断和异常事件,并调用相应的异常处理程序来执行必要的操作。这些异常处理程序可以在内核态下运行,从而保护系统资源免受非法访问。
存储保护:处理器可以通过使用存储保护机制,如地址空间隔离、分页机制、内存保护键等来保护系统资源。这些机制可以将内存划分为多个区域,并为每个区域分配不同的访问权限。
安全扩展:处理器可以支持安全扩展,如硬件加密、安全引导和可信执行环境等,以提高系统的安全性。
2.4.5 明确中断控制机制
处理器的中断控制机制是一种硬件和软件的协同机制,用于处理处理器接收到外部设备或程序请求中断时的行为。该机制包括以下四个主要组成部分:
中断请求 (IRQ):外部设备或程序通过向处理器发送中断请求信号来请求处理器执行中断服务程序。
中断向量表 (IVT):当处理器接收到中断请求时,它会查询中断向量表,找到对应的中断服务程序的地址。
中断服务程序:当处理器确定了中断服务程序的地址后,它会跳转到相应的中断服务程序开始执行,并将当前的上下文信息保存到堆栈中。
中断返回 (IRET):中断服务程序完成后,处理器使用IRET指令从堆栈中恢复上下文信息,回到原来的程序继续执行。
处理器的中断控制机制可以提高计算机系统的通用性和响应能力,使处理器能够及时响应外部事件和异常,从而保证计算机系统的稳定性和安全性。
2.4.6 实操高速缓存和内存管理机制
处理器的高速缓存和内存管理机制是计算机系统中非常重要的组成部分,它们对系统性能和响应时间有着直接的影响。下面是对这两个机制的详细介绍:
高速缓存(Cache):高速缓存是一种快速访问且容量相对较小的存储设备,通常由SRAM(静态随机存储器)组成。其作用是在处理器需要从内存中读取数据时,首先查找高速缓存中是否存在该数据,如果存在则可以直接从高速缓存中获取数据而不必再次从内存中读取,从而提高了系统的运行效率。高速缓存通常分为三级,L1缓存距离处理器最近,容量最小,访问速度最快;L2缓存容量稍大,访问速度比L1慢;L3缓存位于CPU与主内存之间,容量最大、访问速度最慢。
内存管理机制:内存管理机制是指处理器如何将物理内存映射到虚拟地址空间中,并管理进程对内存的访问。在现代操作系统中,通常采用虚拟内存技术,将物理内存划分为若干个等大小的页面,每个进程都拥有自己的虚拟地址空间,其中的页被映射到物理内存中的页框上。内存管理机制还负责处理页面置换(将不常用的数据从内存中替换出去,以腾出空间供其他数据使用)和页面保护(限制进程对某些特定内存区域的访问权限)等问题。
总之,高速缓存和内存管理机制是现代计算机系统中至关重要的组成部分,它们可以提高系统运行效率、加速数据访问、提高系统响应速度,并且有效地管理进程对内存的访问,确保系统稳定性和安全性。
2.4.7 关注 64 位架构对 32 位指令集的兼容
不同的指令集架构之间存在着很大的差异,因此在处理器设计中需要考虑到兼容性问题。下面是关于64位指令集架构和32位指令集架构的兼容性的详细解释:
x86-64(AMD64)指令集架构对x86指令集架构的兼容性:
x86-64指令集架构是一种扩展了x86指令集架构的64位指令集架构。在x86-64架构中,可以运行原生的32位x86代码,并且采用了一些新的指令来提高性能和安全性。这就意味着,支持x86-64架构的处理器可以同时运行32位和64位操作系统,以及32位和64位应用程序。
ARMv8-A指令集架构对ARMv7-A指令集架构的兼容性:
ARMv8-A指令集架构是一种64位指令集架构,可以运行在支持ARMv7-A指令集架构的处理器上。这种兼容性是由于ARMv8-A架构包含了一个称为AArch32的子集,它可以运行原生的32位ARM代码。但是,ARMv7-A架构无法直接运行ARMv8-A的64位代码。
MIPS64指令集架构对MIPS32指令集架构的兼容性:
MIPS64指令集架构是一种64位指令集架构,可以在支持MIPS32指令集架构的处理器上运行。然而,MIPS64和MIPS32之间的兼容性不如其他两种指令集架构的兼容性那么好。因为MIPS64使用一些新的寄存器和指令,这意味着在某些情况下需要重新编译软件才能在MIPS64处理器上运行。
总之,不同指令集架构之间的兼容性各有差异,x86-64架构对x86架构的兼容性最好,ARMv8-A架构对ARMv7-A架构的兼容性次之,而MIPS64架构对MIPS32架构的兼容性最差
以arm为例,AARCH64和AARCH64都是ARM架构的指令集,由ARM公司开发。它们是针对不同的CPU架构设计的。
AARCH64是传统的32位ARM指令集架构,支持32位寻址空间和32位数据处理。在AARCH64中,寄存器被设计成32位宽度。
AARCH64是ARMv8-A架构下的64位指令集架构,支持64位寻址空间和64位数据处理。在AARCH64中,寄存器被设计成64位宽度,并且有更多的寄存器可用于更快的代码执行。
在ARMv8-A架构中,AARCH64和AARCH64可以共存并相互操作。这意味着AARCH64处理器可以运行AARCH64代码,而AARCH64处理器也可以运行AARCH64代码(如果支持)。这种特性被称为“AARCH64/AARCH64混合模式”。
2.5. 实战:轻瞥 ARMv8 架构 Cortex-a53 处理器芳容
第1篇 我们到底在学什么
第2篇 ARMv8 AArch64异常处理机制概览
第3篇 ARMv8高速缓存(Cache)和内存管理单元(MMU)
第4篇 怎样编写裸片启动程序-ARMv8的Boot Code和ROM程序
3. 从应用软件抽象学操作系统
3.1. 操作系统即对硬件的抽象
操作系统是对硬件的抽象。嵌入式系统中的硬件不外乎处理器(CORES)、内部存储(RAM/DDR等片上存储器)、存储器(FLASH/EMMC等外部大容量存储设备)、普通外部设备和网络接口。因而可以将操作系统内核分解为
- 任务管理(对处理器的抽象)
- 内存管理(对内部存储的抽象)
- 文件管理(对存储器的抽象)
- 设备管理(对外设的抽象)
- 网络管理(对网络接口的抽象)
等五个模块。
鉴于实时操作系统对时间约束的强调,
- 时间管理
应当作为一个单独的功能模块被讨论。
3.1.1. 调度器即对处理核的抽象
既然任务管理是对处理器的抽象,而处理器分给任务的资源即任务占用处理器的时间,那么管理多任务/多线程可以理解为管理CPU占用时间。
运行、就绪、阻塞、挂起,或者初始、运行、关闭,这些都是常见的任务状态,见表格1-1。但不论各个操作系统内核对任务的状态做了何种细致的划分,本质上的区别始终不变————任务是否占用CPU。
表格 3-1-1-1 | |
---|---|
参考 | 论述 |
freertos-task-states | – |
rt-thread-task-states | 从运行的过程上划分,线程有多种不同的运行状态,如初始状态、挂起状态、就绪状态等 |
所以学习系统内核的任务管理模块,主要关注两个方面:
- 创建/销毁任务
- 分配CPU时间
3.1.2. 时间管理模块即对定时器的抽象
(基于硬件定时器)提供多个软件定时器。
RT Thread是通过硬件定时器中断维护一个系统心跳计数SysTic,同时提供硬件中断和软件中断API。特性见表格 3-1-1-1。
表格 3-1-2-1 | |
---|---|
参考 | 论述 |
时钟管理 (rt-thread.org) | 中断之间的时间间隔取决于不同的应用,一般是 1ms–100ms,时钟节拍率越快,系统的实时响应越快,但是系统的额外开销就越大。 |
FreeRTOS的软件定时器完全由内核代理,用户不能直接使用硬件定时器中断。当且仅当软件定时器超时时从中断上下文执行计时器回调函数,此外不会消耗任何CPU处理时间,不会向SYSTIC中断增加任何处理开销。在禁用中断时不会遍历(Walk)任何链接列表结构。特性见表格3-1-2-2。
表格 3-1-2-2 | |
---|---|
参考 | 论述 |
161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf | Software timers are implemented by, and are under the control of, the FreeRTOS kernel. They do not require hardware support, and are not related to hardware timers or hardware counters. |
参考 | 论述 |
FreeRTOS – RTOS software timer functionality and features description | The FreeRTOS implementation does not execute timer callback functions from an interrupt context, does not consume any processing time unless a timer has actually expired, does not add any processing overhead to the tick interrupt, and does not walk any link list structures while interrupts are disabled. |
3.1.2.1 软件定时器回调函数
软件定时器回调函数(Software Timer Callback Functions)在软件定时器超时事件发生被调用。
FreeRTOS的软件定时器回调函数在设计上固定函数原型为void类型返回值,仅有一个TimerHandler_t类型的参数,调用过程中不允许进入阻塞态(也即不允许调用可能造成当前任务被挂起的API)。
表格 1-3-2-1-1 | |
---|---|
参考 | 论述 |
161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf/p150 section 5.2 | Software timer callback functions execute from start to finish, and exit in the normal way. They should be kept short, and must not enter the Blocked state. |
参考 | 论述 |
161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf/p154 section 5.4 | Software timer callback functions must not call FreeRTOS API functions that will result in the calling task entering the Blocked state, as to do so will result in the daemon task entering the Blocked state. |
3.1.2.2 软件延时
FreeRTOS延时会向定时器命令队列发送时间戳,超时时间基于发送的时间戳计算,而不依赖于延时命令何时被daemon task处理。
表格 3-1-2-2-1 | |
---|---|
参考 | 论述 |
161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf/p158 section 5.4 | For example, if a ‘start a timer’ command is sent to start a timer that has a period of 10 ticks, the time stamp is used to ensure the timer being started expires 10 ticks after the command was sent, not 10 ticks after the command was processed by the daemon task. |
RT Thread的内核文档中则没有提及这一点。
表格 3-1-2-2-2 | |
---|---|
参考 | 论述 |
时钟管理 (rt-thread.org)/定时器管理 | – |
3.1.2.3 软件定时器服务线程(任务)
管理软件定时器的专用线程(或者进程,在这里我们把所有任务视作线程),守护线程(daemon thread)的前身。
3.1.3. 内存管理模块即对内存的抽象
划定RAM中某一区域用作动态内存区域,交由内存管理器管理分配和释放。
对RTOS而言,较短的建立时间是值得追求的,所以内存管理并非必要的,FreeRTOS自9.0.0版本开始就能完全静态创建各种对象。见表格3-1-3-1。
表格 3-1-3-1 | |
---|---|
参考 | 论述 |
161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf/p25 chapter 2 | From FreeRTOS V9.0.0 FreeRTOS applications can be completely statically allocated, removing the need to include a heap memory manager |
3.1.3.1 动态内存管理的弊端
动态内存管理(Dynamic Memory Allocation)的弊端本质上是管理算法与嵌入式系统的适配问题。常规的管理算法本就不是面向资源有限的嵌入式系统设计的,通常是臃肿而复杂的、非线程安全的(rarely thread-safe)、实时性不确定的(not deterministic,即每次调用耗费的时间不能确定),贸然引入这这类内存管理算法将造成软件调试和维护的困难。
FreeRTOS的设计者认为不同的嵌入式系统需要不同的管理算法,因此在发布系统时预备了五种可选的管理方案。见表格 3-1-2-1-1
表格 3-1-2-1-1 | |
---|---|
参考 | 论述 |
161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf/p27 chapter 2-1 | This is in recognition of the fact that different embedded systems have varying dynamic memory allocation and timing requirements |
3.1.4. 文件系统即对存储器/磁盘的抽象
在操作系统中,文件系统是对存储设备的抽象。文件系统提供了一种标准的接口,使得应用程序可以通过这个接口来访问存储设备中的文件。文件系统将存储设备中的数据组织成一个层次结构,每个文件都有一个唯一的文件名和路径。应用程序可以通过文件名和路径来访问文件,而不需要知道文件在存储设备中的具体位置。文件系统还提供了一些高级功能,如文件的读写、创建、删除、重命名等,使得文件的使用更加方便和灵活。
3.1.5. 驱动框架即对设备的抽象
操作系统的驱动框架是对设备的抽象,它提供了一种标准的接口,使得应用程序可以通过这个接口来访问设备,而不需要了解设备的具体细节。这种抽象的好处在于,它可以使得应用程序的开发变得更加简单和高效,因为应用程序不需要了解设备的底层细节,只需要调用驱动框架提供的接口即可。
驱动框架的实现通常包括两个部分:设备驱动和设备管理器。设备驱动是一个软件模块,它负责与硬件设备进行通信,并将设备的功能暴露给操作系统。设备管理器则负责管理所有的设备驱动,并提供一个标准的接口,使得应用程序可以通过这个接口来访问设备。
通过驱动框架的抽象,操作系统可以将不同类型的设备统一管理,从而提高了系统的可扩展性和可维护性。
3.2. 操作系统分类
3.2.1. 术语辨析
3.2.2. 桌面操作系统和实时操作系统的关联
3.3. 操作系统是一种软件编程范式
编程范式是一种编程思想或方法论,它描述了如何组织和设计程序的结构和行为。编程范式包括多种不同的方法,如面向对象编程、函数式编程、过程式编程等。每种编程范式都有其独特的特点和优势,可以用于不同类型的应用程序开发。编程范式可以帮助开发人员更好地组织和管理程序的结构和行为,提高程序的可读性、可维护性和可扩展性。同时,编程范式也可以帮助开发人员更好地理解和解决问题,提高程序的质量和效率。不同的编程范式可以相互结合,形成更加灵活和强大的编程方法。
所谓操作系统是一种编程范式,即是说操作系统是一种用于编写软件的方法论。
操作系统提供了一些基本的概念和机制,如进程、线程、内存管理、文件系统等,开发人员可以利用这些概念和机制来编写应用程序。操作系统的编程范式与传统的编程范式有所不同,它更加注重系统级别的问题,如资源管理、并发控制、安全性等。
操作系统这种编程范式强调可扩展性和可扩展性。
可扩展性指的是软件支持不同类型的硬件设备和应用程序。它通过设备驱动程序和应用程序接口,使得新的硬件设备和应用程序可以方便地集成到系统中。
可移植性指的是软件可以在不同的硬件平台和软件环境中运行。它通过抽象硬件和软件接口,使得操作系统可以适应不同的环境和需求。
3.3.1. 精简开发是使用操作系统的初心使命
恰如人类社会中普遍存在的重复劳动,众多软件的开发过程中也存在着重复造轮子的行为,远古开发者在这方面的体会尤为深刻————如果想屏幕上显示字符,还得自己打码表,纯纯的牛马开发策略。
操作系统在降低重复性开发方面的作用主要体现在以下几个方面:
提供标准接口:操作系统提供了标准的接口和API,使得应用程序可以方便地访问和使用系统资源,如文件、网络、设备等。这些接口和API是经过充分测试和验证的,可以保证其稳定性和可靠性。应用程序可以直接使用这些接口和API,而不需要重复开发和测试相同的功能,从而降低了开发的重复性和成本。
提供共享资源:操作系统负责管理计算机的各种资源,如CPU、内存、磁盘、网络等。它通过分配和调度资源,使得多个应用程序可以共享计算机的资源,提高计算机的利用率。应用程序可以利用这些共享资源,而不需要重复开发和管理相同的资源,从而降低了开发的重复性和成本。
提供通用功能:操作系统提供了许多通用的功能和服务,如进程管理、内存管理、文件系统、安全性管理等。这些功能和服务是经过充分测试和验证的,可以保证其稳定性和可靠性。应用程序可以直接使用这些功能和服务,而不需要重复开发和测试相同的功能,从而降低了开发的重复性和成本。
3.3.2. 借助调度器实现多任务协同
操作系统可以通过多任务调度来实现多任务协同。具体来说,操作系统会将CPU时间分配给多个任务,让它们交替执行,从而实现多任务协同。
操作系统通常会采用抢占式调度或协作式调度来实现多任务协同。抢占式调度是指操作系统可以在任何时候中断当前任务,将CPU时间分配给其他任务,而协作式调度则是指任务需要主动让出CPU时间,才能让其他任务执行。
在实现多任务协同时,操作系统还需要提供一些机制来保证任务之间的通信和同步。例如,操作系统可以提供进程间通信(IPC)机制,让不同的任务之间可以进行数据交换和协作。操作系统还可以提供锁、信号量等同步机制,来保证多个任务之间的同步和互斥。
总之,操作系统可以通过多任务调度、进程间通信和同步机制等手段来实现多任务协同,从而提高系统的并发性和效率。
3.3.3. 借助文件系统进行数据存取
文件系统是操作系统提供的一种数据存储和管理方式,可以通过文件系统来进行数据存取。具体来说,可以通过以下步骤来借助文件系统进行数据存取:
打开文件:首先需要打开要进行数据存取的文件,可以使用操作系统提供的文件打开函数来打开文件。
读取数据:打开文件后,可以使用文件读取函数来读取文件中的数据。读取函数可以指定读取的数据长度和读取的位置等参数。
写入数据:如果需要向文件中写入数据,可以使用文件写入函数来写入数据。写入函数可以指定写入的数据长度和写入的位置等参数。
关闭文件:在完成数据存取后,需要关闭文件,释放文件资源。可以使用文件关闭函数来关闭文件。
需要注意的是,在进行文件存取时,需要考虑文件的权限和安全性等问题。例如,需要确保只有具有访问权限的用户才能进行文件存取,避免数据泄露和损坏等问题。此外,还需要注意文件的并发访问问题,避免多个进程同时对同一个文件进行读写操作,导致数据不一致等问题。
C标准库是C语言的标准库,提供了一系列常用的函数和数据类型,包括文件操作、字符串处理、数学计算、内存管理等功能。其中,文件操作相关的函数包括文件打开、读写、关闭等操作,例如fopen、fread、fwrite、fclose等函数。C标准库的相关文档可以在C语言标准的官方网站https://www.iso.org/standard/74528.html上获取。
3.3.4. 了解设备驱动和设备树
Linux操作系统的设备驱动是用于控制硬件设备的软件模块,它可以让操作系统与硬件设备进行通信和控制。设备驱动通常由内核模块和用户空间程序组成,内核模块负责与硬件设备进行交互,用户空间程序则负责提供用户接口和应用程序接口。
设备树是一种用于描述硬件设备的数据结构,它可以让操作系统在启动时自动识别硬件设备,并加载相应的设备驱动。设备树通常由设备树源文件和设备树二进制文件组成,设备树源文件是一种文本文件,用于描述硬件设备的属性和连接关系,设备树二进制文件则是一种编译后的二进制文件,用于在启动时加载设备树。
在Linux操作系统中,设备驱动和设备树通常是紧密相关的,设备驱动需要根据设备树中描述的硬件设备信息来进行初始化和控制。因此,了解设备树的结构和使用方法对于开发Linux设备驱动非常重要。同时,设备树也可以让硬件设备的描述更加灵活和可扩展,方便硬件设备的开发和维护。
设备树的规范由Device Tree Specification(DTS)组织制定和维护,可以在其官方网站https://www.devicetree.org/上获取最新的规范文档和相关资源。DTS组织还提供了一些工具和库,用于生成、解析和操作设备树文件,例如Device Tree Compiler(dtc)和libfdt等。此外,一些Linux发行版和开发社区也提供了相关的文档和教程,可以帮助开发者更好地理解和使用设备树。在学习设备树规范时,需要注意不同版本的规范可能存在差异,需要根据具体情况选择相应的文档和工具。
3.4. 实战:一招吃遍多任务编程
请看操演:怎样快速上手实时操作系统(以RT Thread为例)
4. 从需求分析出发学协同软件设计
4.1. 搞清软硬件协同设计的原因和目的
软硬件协同设计是指在设计过程中,软件和硬件的开发人员共同参与,协同完成系统的设计和开发。软硬件协同设计的目的是为了提高系统的性能、可靠性和可维护性,同时缩短开发周期和降低开发成本。
在软硬件协同设计中,软件和硬件的开发人员可以共同制定系统的架构、接口和测试计划,以确保系统的功能和性能得到充分的测试和验证。此外,软硬件协同设计还可以提高系统的可重用性和可扩展性,使得系统可以更加容易地进行升级和维护。总之,软硬件协同设计是一种有效的设计方法,可以提高系统的质量和效率,同时降低开发成本和风险。
软硬件协同设计需要软件和硬件的开发人员共同参与,协同完成系统的设计和开发。具体步骤如下:
确定系统需求:软硬件协同设计的第一步是确定系统的需求,包括功能、性能、接口等方面的要求。软件和硬件的开发人员需要共同制定系统的需求规格说明书,以确保系统的设计和开发符合用户的需求。
制定系统架构:在确定系统需求后,软硬件的开发人员需要共同制定系统的架构,包括硬件和软件的分工、接口设计、通信协议等方面的内容。在制定系统架构时,需要考虑系统的可扩展性、可重用性和可维护性等方面的要求。
进行模块设计:在确定系统架构后,软硬件的开发人员需要分别进行模块设计,包括硬件模块和软件模块的设计。在进行模块设计时,需要考虑模块的功能、接口、测试等方面的要求。
进行集成测试:在完成模块设计后,软硬件的开发人员需要进行集成测试,以确保系统的各个模块能够正常协同工作。在进行集成测试时,需要考虑系统的稳定性、可靠性和性能等方面的要求。
进行系统测试:在完成集成测试后,软硬件的开发人员需要进行系统测试,以确保系统能够满足用户的需求。在进行系统测试时,需要考虑系统的功能、性能、安全等方面的要求。
4.2. 拆解需求
软硬件协同设计的需求可以分为以下几个方面:
功能需求:确定系统需要实现的功能,包括软件和硬件的功能需求。在确定功能需求时,需要考虑系统的实际应用场景和用户需求。
性能需求:确定系统需要达到的性能指标,包括响应时间、吞吐量、可靠性等方面的要求。在确定性能需求时,需要考虑系统的实际应用场景和用户需求。
接口需求:确定系统各个模块之间的接口,包括硬件和软件的接口设计。在确定接口需求时,需要考虑系统的可扩展性、可重用性和可维护性等方面的要求。
通信协议需求:确定系统需要使用的通信协议,包括硬件和软件的通信协议。在确定通信协议需求时,需要考虑系统的实际应用场景和用户需求。
安全需求:确定系统需要满足的安全要求,包括数据安全、系统安全等方面的要求。在确定安全需求时,需要考虑系统的实际应用场景和用户需求。
4.3. 软硬件任务划分
把具体的任务划分到软硬件设计人员名下需要考虑以下几个方面:
技能和专业领域:根据软硬件设计人员的技能和专业领域,将任务分配到相应的人员名下。例如,硬件设计人员可以负责硬件模块的设计和测试,而软件设计人员可以负责软件模块的设计和测试。
工作量和时间:根据任务的工作量和时间要求,将任务分配到相应的人员名下。例如,工作量较大的任务可以分配给有经验的人员,而时间紧迫的任务可以分配给有较高工作效率的人员。
任务的复杂度和难度:根据任务的复杂度和难度,将任务分配到相应的人员名下。例如,复杂度较高的任务可以分配给有较高技能水平和经验的人员,而难度较大的任务可以分配给有较高解决问题能力的人员。
团队协作和沟通:在分配任务时,需要考虑团队协作和沟通的问题,以确保任务能够顺利完成。例如,需要将任务分配给能够良好协作和沟通的人员,以确保团队的合作效率和质量。
4.4. 实时性分析
对产品设计方案做实时性分析需要考虑以下几个方面:
系统架构:首先需要确定系统的架构,包括硬件和软件的架构。在确定系统架构时,需要考虑系统的实际应用场景和用户需求。
任务分配:根据系统架构,将系统的任务分配到相应的硬件和软件模块中。在任务分配时,需要考虑任务的优先级和时间要求。
任务调度:确定任务的调度方式,包括硬件和软件的任务调度。在任务调度时,需要考虑任务的优先级、时间要求和系统资源的利用率。
实时性分析:对系统的任务进行实时性分析,包括任务的响应时间、处理时间和完成时间等方面的分析。在实时性分析时,需要考虑系统的实际应用场景和用户需求。
优化设计:根据实时性分析的结果,对系统的设计进行优化,包括硬件和软件的优化。在优化设计时,需要考虑系统的实际应用场景和用户需求。
关键在于,将系统级的计算软件和调度软件拆解为多个相对独立的子模块。
在处理器上使用系统时钟测量各计算模块的耗时,在通过系统级联调度联合测试评估硬件加速器的性能。综合软硬件耗时和调度耗时形成设计方案的实时性评估报告。
形成报告后,应当组织软硬件人员参会评估,对设计方案的性能上下界做出确认,同时考虑优化空间,为后续同系列产品的升级提供预备参考。