text段:
代码段(codesegment/textsegment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等
bss段:
bss是指那些没有初始化的和初始化为0的全局变量。bss类型的全局变量只占运行时的内存空间,而不占文件空间。另外,大多数操作系统,在加载程序时,会把所有的bss全局变量全部清零,无需要你手工去清零。但为保证程序的可移植性,手工把这些变量初始化为0也是一个好习惯。
data段:
与bss相比,data就容易明白多了,它的名字就暗示着里面存放着数据。当然,如果数据全是零,为了优化考虑,编译器把它当作bss处理。通俗的说,data指那些初始化过(非零)的非const的全局变量。data类型的全局变量是即占文件空间,又占用运行时内存空间的。
rodata段:
rodata的意义同样明显,ro代表read only,即只读数据(const)。关于rodata类型的数据,要注意以下几点:
1、常量不一定就放在rodata里,有的立即数直接编码在指令里,存放在代码段(.text)中。
2、对于字符串常量,编译器会自动去掉重复的字符串,保证一个字符串在一个可执行文件(EXE/SO)中只存在一份拷贝。
3、 rodata是在多个进程间是共享的,这可以提高空间利用率。
4、在有的嵌入式系统中,rodata放在ROM(如norflash)里,运行时直接读取ROM内存,无需要加载到RAM内存中。
5、在嵌入式linux系统中,通过一种叫作XIP(就地执行)的技术,也可以直接读取,而无需要加载到RAM内存中。
由此可见,把在运行过程中不会改变的数据设为rodata类型的,是有很多好处的:在多个进程间共享,可以大大提高空间利用率,甚至不占用RAM空间。同时由于rodata在只读的内存页面(page)中,是受保护的,任何试图对它的修改都会被及时发现,这可以帮助提高程序的稳定性。
当U-Boot在NORFLASH、MMC、SPI FLASH等作为启动设备的存储介质中时,SoC内部的RAM空间通常无法满足U-Boot的需求,此时,需要U-Boot自身通过重定向功能实现程序的搬移。
链接地址、运行地址、加载地址,在重定向的过程中起着非常重要的作用。
链接地址是U-Boot重定向后的执行地址,若重定向后在DDR上运行,那么链接地址是DDR内存中的地址。
运行地址是U-Boot当前执行时的地址PC。若U-Boot需要重定向,那么重定向前的运行地址和加载地址无关。
加载地址同样需要考虑是否需要重定向,若需要重定向,那么加载地址一般是存储地址。
U-Boot重定向之后运行地址会发生变化,通常重定向之后代码运行于外部DDR空间,代码运行速度、资源空间大小都会得到极大的提高。在board_init_f函数中完成DDR初始化以及其他重定向使用到的模块。
CISC”与“RISC”。 Intel和ARM处理器的第一个区别是,前者使用复杂指令集(CISC),而后者使用精简指令集(RISC)。属于这两种类中的各种架构之间最大的区别。
1)CISC的指令能力强,单多数指令使用率低却增加了CPU的复杂度,指令是可变长格式;RISC的指令大部分为单周期指令,指令长度固定,操作寄存器,只有Load/Store操作内存
2)CISC支持多种寻址方式;RISC支持方式少
3)CISC通过微程序控制技术实现;RISC增加了通用寄存器,硬布线逻辑控制为主,是和采用流水线
4)CISC的研制周期长
5)RISC优化编译,有效支持高级语言
检测到中断事件后:


module_param()
module_param_array()
register
这个关键字命令编译器尽可能的将变量存在CPU内部寄存器中而不是通过内存寻址访问以提高效率。
static
指定为静态变量,分配在静态变量区,修饰函数时,指定函数作用域为文件内部
常见的两种用途:
1>统计函数被调用的次数;
2>减少局部数组建立和赋值的开销.变量的建立和赋值是需要一定的处理器开销的,特别是数组等含有较多元素的存储类型。在一些含有较多的变量并且被经常调用的函数中,可以将一些数组声明为static类型,以减少建立或者初始化这些变量的开销.
详细说明:
1>、变量会被放在程序的全局存储区中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。
2>、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。
3>当static用来修饰全局变量时,它就改变了全局变量的作用域,使其不能被别的程序extern,限制在了当前文件里,但是没有改变其存放位置,还是在全局静态储存区。
使用注意:
1>若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;
2>若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
3>设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题(只要输入数据相同就应产生相同的输出)。
volatile
表明某个变量的值可能在外部被改变,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
现在的硬件会帮我们维护多核Cache一致性,并且对软件是透明的。
当CPU0修改自己私有的Cache时,硬件就会广播通知到总线上其他所有的CPU。对于每个CPU来说会有特殊的硬件监听广播事件,并检查是否有相同的数据被缓存在自己的CPU,这里是指CPU1。如果CPU1私有Cache已经缓存即将修改的数据,那么CPU1的私有Cache也需要更新对应的cache line。这个过程就称作bus snooping。
MESI是现在一种使用广泛的协议,用来维护多核Cache一致性。我们可以将MESI看做是状态机。我们将每一个cache line标记状态,并且维护状态的切换。cache line的状态可以像tag,modify等类似存储。
嵌入式系统的启动的基本流程是先运行 bootloader ,然后由 bootloader 引导启动 kernel,这里无论启动的是 rt-thread 或者是 linux 原理都是一样的。
上电后所有的 CPU 都会从 bootrom 里面开始执行代码,为了防止并发造成的一些问题,需要将除了 primary cpu 以外的 cpu 拦截下来,这样才能保证启动的顺序是可控的。
在启动的过程中,bootloader 中有一道栅栏,它拦住了除了 cpu0 外的其他 cpu。cpu0 直接往下运行,进行设备初始化以及运行 Kernel。其他 cpu0 则在栅栏外进入睡眠状态。
cpu0 在初始化 smp 的时候,会在 cpu-release-addr 里面填入一个地址并唤醒其他 cpu。这时睡眠的 cpu 接受到信号,醒来的时候会先检查 cpu-release-addr 这个地址里面的数据是不是有效。如果该地址是有效的(非 0 ),意味着自己需要真正开始启动了,接下来他会跳转到。

一般是有一个主核(有时叫core 0)先去完成上述的操作。主核的启动工作完成后,再去唤醒其它的处理器核(可以称为从核)。从核负责初始化私有的TLB和cache等资源,启动之后进入空闲(Idle)状态,直至进入操作系统再由主核进行调度。多核处理器的内核间通信一般会通过信箱(Mailbox)机制或者核间中断机制。