一、比喻
Trampoline 指的是“蹦床”,也就是底下这个东西:
他是一种很有趣的比喻,人们不能直接跳到高空中,而是需要先往下跳,然后借助蹦床的弹力跳到高空中。
这个比喻出现在用户态和内核态的相互转变中,转变不能直接发生,而是必须要借助一个“蹦床”机制。
一致性(consistency)这个概念应该是来自于分布式理论的 CAP 理论
我觉得用人话来说就是对于多个观察者观察同一个事物,每个观察者观察到的现象是“相同的”,那么我们就认为这个设计具有“一致性”。“盲人摸象”就是一个非一致性的例子,对于同一个事物,盲人们分别给出了“柱子、山、石头”等描述,就是不具有一致性的案例。
一致性和并行编程联系紧密,因为一致性保证了多个并行的实体,他们看到的共享资源是相同的。只有这样才能保证并行实体间的协作,如果实体看到的资源都是不一样的,比如说线程 A 给资源加了个锁,但是 B 没有看到这个锁,那么就会导致数据竞争的发生。盲人摸象的结局不正是因为盲人们对于大象的看法各执己见,最终大打出手吗?
转义字符的意思是,原本这个字符表达一个意思,而经过转义后,就会表达另一个意思。原本的意思没有什么特别之处(比如说必须是一个普通字符,一个通配符,一个格式控制符),表达出来的另一个意思也同样没有什么特别之处。
那么为什么要存在转义字符?我们可以用另一个字符来表达更多的意思,而不是让一个已经有一个意思的字符,经过转义后来表达另一个意思。但是可悲的是,这并不是现实的,现实是,我们仅有数量不多的可打印字符,而我们却希望表达更多的意思(这点在正则中尤其明显)。事实就是,每一个可打印字符都有了自己的含义,我们所能做到的,就只是复用他们。
上面的说法有些绝对,却是有方法可以缓解这种方式,比如用 Java 中数字编码转义字符,如 \uF890
这种,可以避免复用的尴尬,不过这样又有些不直观,毕竟谁都喜欢写 \n
而不是 \10
。
函数被调用的过程中会使用寄存器,这就可能给函数的调用者造成困扰,原本在调用前的寄存器可能会被调用的函数污染。如果希望寄存器不被污染,可以将寄存器保存在栈上,在调用结束后再将栈上的值重新拷贝回对应的寄存器。
我们将寄存器分为两组,其中一组由调用者保存,被称为 caller-saved ,这是说调用者可以按照自己的需要来保存这些寄存器,如果自己使用了,那么就保存,如果没有使用,就不需要保存。另一组由被调用者保存,被称为 callee-saved ,被调用者在执行正式的功能前前需要将这些寄存器压栈,在返回前需要将这些寄存器弹栈。
不过这种标准的定义其实并不容易让人理解其中的原因。我们可以换一个角度去看这件事。当一个函数调用发生时,只有 caller-saved 的寄存器有可能被污染(在 caller 并不保存的情况下),这是因为所有的 callee-saved 寄存器都会本着“谁污染,谁治理”的策略被保护起来。所以 caller-saved register 其实核心特性是 unsafe ,也就是“调用前后并能保证寄存器中的值不发生变化”,所以这种寄存器也被称为临时寄存器。而 callee-saved register 则正好相反,是 safe 的,调用者不用担心调用一个函数后,自己的寄存器值被改写。
所以我建议使用更好的名字:
在 Armv8 的 A32 架构或者 Armv7 及以前的指令集中,每条指令都可以“条件执行”,也就是根据 CPSR 的条件码来确定这条指令是否真的具有执行效果。
比如说如下指令,只有在 CPSR 中 Z
位为 1 的时候,addz
这条指令才有实际效果,这依赖于前一条指令 cmp
对 CPSR 的修改(ARM 同样可以指定是否修改 CPSR,这是通过在指令位中的 S
确定的)。
cmp r4, r5
addz r1, r2, r3
MIPS 或者 RISCV 都有条件指令,不过这些条件指令一般是条件指令,这样可以实现编程思想中的分支结构。Arm 的特殊之处在于每条 32 位指令的高 4 位都是条件码,这就会导致每条指令都是条件指令。