Blog

默认真实

Content #

我们会“默认真实”:我们常用的假设是,与我们打交道的人是诚实的。只有当事件明确地变得和我们最初的假设相反时,我们才会走出“默认真实”模式。

换句话说,我们不会像头脑清醒的科学家那样,在得出结论之前,慢慢地搜集证据来证明某件事的真伪。我们的步骤恰恰相反,我们从相信开始,只有当怀疑和疑虑上升到无法被解释的程度时,我们才停止相信。

“我曾一直相信你,直到我不能再相信你。”这难道不是对“默认真实”近乎完美的表述吗?

From #

非对称洞察力错觉

Content #

普罗宁提出“非对称洞察力错觉”。她写道:

坚信自己对别人的了解超过别人对自己的了解——认为我们自己具有别人所不具有的对别人的洞察力(却不认为别人具有我们自己所不具有的对我们自己的洞察力)——导致我们在本该倾听的时候却在诉说,本该耐心地听别人说他们如何坚信自己是被误解或被不公正地判决时却心不在焉。

中情局古巴分部的官员确信他们可以评估间谍的忠诚度。法官们对于看清被告是什么样的人总是感到胜券在握,于是花一两分钟就做了“权威”的判断。内维尔·张伯伦从未质疑他为避免战争而制订的大胆计划。如果希特勒的意图不为人知,那么他作为英国首相,就有责任去德国了解清楚。

我们认为自己通过蛛丝马迹就能看透别人的内心,于是我们不放过任何机会去评判陌生人。当然,我们不会对自己这样做,因为我们深信自己是微妙的、复杂的、神秘的,但陌生人却很容易被了解。

如果我能通过这本书让你相信一件事,那我希望是:陌生人不容易被了解。

坚信自己对别人的了解超过别人对自己的了解——认为我们自己具有别人所不具有的对别人的洞察力(却不认为别人具有我们自己所不具有的对我们自己的洞察力)—导致我们在本该倾听的时候却在诉说,本该耐心地听别人说他们如何坚信自己是被误解或被不公正地判决时却心不在焉。同样的信念会使我们认为,他人不懂我们自己的想法、感受、动机和对事件的理解,从而使我们不愿意接受其他人的建议。但我们又太愿意在不了解他人的想法、感受、动机和对事件的理解的情况下,根据我们对他们既往行为的看法,为他人提供建议。事实上,有的信息交流,特别是仔细的、尊重的倾听,可以大大减少人际和群体冲突所带来的挫折感和怨恨情绪,而上述偏见可能会是这种好的信息交流的障碍。

From #

两类外部硬件中断

Intel处理器的两类外部硬件中断 #

外部硬件中断是通过两个信号线引入处理器内部的。从8086处理器开始,这两根线的名字就叫 NMI 和 INTR。

有些中断,在任何时候都必须及时处理,因为事关整个系统的安全性。比如:

  1. 在使用不间断电源的系统中,当电池电量很低的时候,不间断电源系统会发出一个中断,通知处理器快掉电了。
  2. 内存访问电路发现了一个校验错误,这意味着,从内存读取的数据是错误的,处理器再努力工作也是没有意义的。

在所有这些情况下,处理器必须针对这些中断采取必要的措施。所有的严重事件都必须无条件地加以处理,这种类型的中断是不会被阻断和屏蔽的,称为非屏蔽中断(Non Maskable Interrupt,NMI)。非屏蔽中断是通过 NMI 引脚进入处理器内部的。

可屏蔽中断和 NMI 不同,更多的时候,发往处理器的中断信号通常不会意味着灾难。比如,在一个由计算机控制的车床上,当零件快速通过铣具时,处理器应当立即处理中断,并向铣具发送信号,告诉它应当如何切削。这类中断有两个特点:

  1. 数量很多,毕竟有很多外部设备;
  2. 它们可以被屏蔽,这样处理器就像是没听见、没看见一样,不会对它们进行处理。

所以,这类硬件中断称为可屏蔽中断。尽管不处理中断就会把零件铣坏,但是否允许处理器看见该中断,是你自己的事,这是处理器赋予你的权利。可屏蔽中断是通过INTR 引脚进入处理器内部的。

From #

实模式下的中断向量表

Content #

实模式下的中断向量表所谓中断处理,归根结底就是处理器要执行一段与该中断有关的程序(指令)。处理器可以识别256 个中断,那么理论上就需要256 段程序。这些程序的位置并不重要,重要的是,在实模式下,处理器要求将它们的入口点集中存放到内存中从物理地址0x00000 开始,到0x003ff 结束,共1KB 的空间内,这就是所谓的中断向量表(Interrupt Vector Table,IVT)。 如图所示,每个中断在中断向量表中占 2 个字,分别是中断处理程序的偏移地址和段地址。中断0 的入口点位于物理地址0x00000 处,也就是逻辑地址 0x0000:0x0000;中断1 的入口点位于物理地址0x00004 处,即逻辑地址 0x0000:0x0004;其他中断以此类推,总之是按顺序的。

From #

修改IOPL位和IF位的四条指令

Content #

通过将EFLAGS 寄存器的内容压入栈,局部修改后,再弹出到EFLAGS,可以间接地改变它的各种标志位。对多数标志位的修改不会威胁到整个系统的安全,但是,如果修改了IOPL 位和IF 位,就不同了。

能够修改这两个标志的指令是 popf iret cli sti 注意没有pushf指令,保存eflags到栈中,并不会改变标志位。中断应由操作系统或者内核统一管理,但cli 和sti指令不是特权指令,低特权级的程序与能调用。

对这四条指令的调用是用 IOPL 本身来控制的。如果当前特权级CPL 高于,或者和当前I/O 特权级IOPL 相同,即,在数值上 CPL <= IOPL 则允许执行以上4条指令,也允许访问所有的硬件端口。否则,执行popf 和iret 指令时,会引发处理器异常中断;执行cli 和sti 时,不会引发异常中断,但不改变标志寄存器的IF 位。

From #

IOPL位

Content #

除了那些特权级敏感的指令外,处理器还允许对各个特权级别所能执行的I/O 操作进行控制。通常,这指的是端口访问的许可权,因为对设备的访问都是通过端口进行的。

如图所示,在处理器的标志寄存器EFLAGS 中,位13、位12 是IOPL 位,也就是输入/输出特权级(I/O Privilege Level),它代表着当前任务的I/O 特权级别。

From #

IO许可位串

Content #

EFLAGS 寄存器的IOPL 位决定了当前任务的I/O 特权级别。如果当前特权级CPL 高于,或者和任务的I/O 特权级IOPL 相同时,即,在数值上, CPL <= IOPL 时,所有I/O 操作都是允许的,针对任何硬件端口的访问都可以通过。

相反,如果当前特权级CPL 低于任务的I/O 特权级IOPL,也并不意味着所有的硬件端口都对当前任务关上了大门。事实上,处理器的意思是总体上不允许,但个别端口除外。至于个别端口是哪些端口,要找到当前任务的TSS,并检索I/O 许可位串。

如图所示,I/O 许可位串(I/O Permission Bit String)是一个比特序列,或者说是一个比特串,最多允许65536 比特,即8KB。从第1 比特开始,各比特用它在串中的位置代表一个端口号。因此,第1 个比特代表0 号端口,第2 个比特代表1 号端口,第3 个比特代表2 号端口,……,第65536 比特代表第65535 号端口。

每个比特的取值决定了相应的端口是否允许访问。为1 时,禁止访问;为0 时,允许访问。处理器检查I/O 许可位的方法是先计算它在I/O 许可位映射区的字节编号,并读取该字节,然后进行测试。比如,当执行指令 out 0x09, al 时,处理器通过计算就可以知道,该端口对应着I/O 许可位映射区第2 个字节的第2 个比特(位1)。于是,它读取该字节,并测试那一位。

From #

IO许可映射区的最后一位必须是0xFF

Content #

I/O 端口是按字节编址的。每个端口仅被设计用来读写一个字节的数据,当以字或者双字访问时,实际上是访问连续的2 个或者4 个端口。

比如,当从端口n 读取一个字时,相当于同时从端口n 和端口n+1 各读取一个字节。下面的代码

in ax, 0x3f8

相当于同时执行

in al, 0x3f8
in ah, 0x3f9  ;仅为示例,x86不允许使用ah寄存器

由于这个原因,当处理器执行一个字或者双字I/O 指令时,会检查许可位串中的 2 个,或者4个连续位,而且要求它们必须都是“0”,否则引发异常中断。

麻烦在于,这些连续的位可能是跨字节的。即,一些位于前一字节,另一些位于后一字节。为此,处理器每次都要从I/O 许可位映射区读两个连续的字节。这种操作方式直接导致了另一个问题。即,如果要检查的比特在最后一字节中,那么,这个两字节的读操作将会越界。

为防止这种情况,处理器要求I/O 许可位映射区的最后必须附加一个额外的字节,并要求它的所有比特都是“1”,即0xFF。当然,它必须位于TSS 的界限之内。

处理器不要求为每一个I/O 端口都提供位映射。对于那些没有在该区域内映射的位,处理器假定它对应的比特是“1”。例如,要是I/O 许可位映射区的长度是 11 字节,那么,除去最后一个所有比特都是“1”的字节,前10 字节映射了80 个端口,分别是端口0 到端口79,访问更高地址的端口将引发异常中断。

From #

TSS中的IO许可位映射区

Content #

同其他和任务相关的信息一样,I/O 许可位串位于任务的TSS 中。任务状态段 TSS 的最小长度是104 字节,保存着最基本的任务信息,但这并不是它的最大长度。事实上,整个TSS 还可以包括一个I/O 许可位串,它所占用的区域称为I/O 许可位映射区。

如图所示,在TSS 内偏移为102 的那个字单元,保存着I/O 许可位串(I/O 许可位映射区)的起始位置,从TSS 的起始处(0)算起。因此,如果该字单元的内容大于或者等于TSS 的段界限(在TSS 描述符中),则表明没有I/O 许可位串。在这种情况下,如果当前特权级CPL 低于当前的I/O 特权级IOPL,执行任何硬件 I/O 指令都会引发处理器异常中断。

和LDT 一样,必须在GDT 中创建TSS 的描述符,TSS 描述符中包括了TSS 的基地址和界限,该界限值包括I/O 许可位映射区在内。

From #

IO许可映射区的最后一位必须是0xFF IO许可位串

LDT的0号槽位是可用的

Content #

和GDT 不同,LDT 的0 号槽位也是可用的。原因在于,其选择子的TI 位是“1”,所以不可能会有一个全零的选择子指向LDT。这就是说,一个指向LDT 的选择子代入段寄存器时,它不可能是因程序员粗心大意而未初始化的。

From #