Blog

一切都好吗

Content #

在谈判中,我最常用的开场白是“一切都好吗?”这看似一个普普通通的问题,但这个问题中至少包含了4种谈判技巧。

  1. 这个问题有助于你和对方建立一种良好的人际关系—你一开始就表现得亲切健谈。
  2. 这是一个问句—提问是一种搜集信息的极佳方式。
  3. 这个问题首先关注对方以及他们的情绪和感受,而不是“谈判”本身。
  4. 这个问题是随意的闲聊,有助于为双方营造一个轻松舒适的氛围。

From #

沃顿商学院最受欢迎的谈判课

文质彬彬,然后君子

Content #

子曰:“质胜文则野,文胜质则史。文质彬彬,然后君子。”

《论语·雍也》孔子说:“如果质朴多于文采,就会显得很粗野;文采多于质朴,又会显得很浮夸。只有文采和质朴相互配合,才是个君子。”

From #

譬如为山,未成一篑

Content #

子曰:“譬如为山,未成一篑,止,吾止也;譬如平地,虽覆一篑,进,吾往也。”

《论语·子罕》孔子说:“好比堆土成山,只要再加一筐土就成功了,如果停下来,那是自己停止的;又好比在平地上堆土成山,虽然只倒下一筐土,如果决心努力前进,还是要自己坚持。”

From #

精益创业的核心概念

Content #

精益创业的一个核心概念是“构建-衡量-学习”,这贯穿于每项工作中,从确立愿景到构建产品特性再到开发渠道和市场战略,如图所示。

在这一循环中,重点是衡量阶段。你的组织通过这个循环迭代的速度越快,就能越快地找到合适的产品和市场。如果你能更好地进行衡量,你就更有可能获得成功。

管理学大师、作家彼得·德鲁克有一句名言:“你无法衡量的东西,你也无法管理。”用这句话来描述精益创业模式再合适不过了。在精益创业模式下,成功的创业者可以在同一时间开发产品,确定市场策略,并建立一套获知用户需求的数据统计系统。

From #

特权级别与栈

Content #

通过调用门实施特权级之间的控制转移时,可以使用jmp far 指令,也可以使用 call far 指令。如果是后者,会改变当前特权级CPL。因为栈段的特权级必须同当前特权级保持一致,因此,还要切换栈,即,从低特权级的栈切换到高特权级的栈。

比如,一个特权级为3 的程序必须使用自己的3 特权级栈工作。当它通过调用门进入0 特权级的代码段执行时,当前特权级由3 变为0。此时,栈也要跟着切换,从3 特权级的栈切换到0 特权级的栈。这主要是为了防止因栈空间不足而产生不可预料的问题,同时也是为了防止栈数据的交叉引用。

为了切换栈,每个任务除了自己固有的栈之外,还必须额外定义几套栈,具体数量取决于任务的特权级别。

  1. 0 特权级任务不需要额外的栈,它自己固有的栈就足够使用,因为除了调用返回外,不可能将控制转移到低特权级的段;
  2. 1 特权级的任务需要额外定义一个描述符特权级DPL 为0 的栈,以便将控制转移到0 特权级时使用;
  3. 2 特权级的任务则需要额外定义两个栈,描述符特权级DPL 分别是0 和1,在控制转移到0 特权级和1 特权级时使用;
  4. 3 特权级的任务最多额外定义3 个栈,描述符特权级分别是0、1 和2,在控制转移到0、1 和2 特权级时使用。

这些额外的栈,一般会由操作系统加载程序时自动创建。其描述符位于任务自己的LDT 中。同时,还要在任务的TSS 中登记,原因是,栈切换是由处理器固件自动完成的,处理器需要根据TSS 中的信息来完成这一过程。

任务状态段TSS的格式图所示,在TSS 内,从偏移4~24 处登记有特权级 0 到2 的栈段选择子,以及相应的ESP 初始值。任务自己固有的栈信息则位于偏移量为56(ESP)和80(SS)的地方。任务寄存器TR 总是指向当前任务的任务状态段TSS,其内容为该TSS 的基地址和界限。在切换栈时,处理器可以用TR 找到当前任务的TSS,并从TSS 中获取新栈的信息。

From #

任务状态段TSS的格式

Content #

在一个多任务的环境中,当任务切换发生时,必须保护旧任务的运行状态,或者说是保护现场,保护的内容包括通用寄存器、段寄存器、栈指针寄存器ESP、指令指针寄存器EIP、状态寄存器EFLAGS,等等。否则的话,等下次该任务又恢复执行时,一切都会变得茫然而毫无头绪。

为了保存任务的状态,并在下次重新执行时恢复它们,每个任务都应当用一个额外的内存区域保存相关信息,这叫做任务状态段(Task State Segment:TSS)。任务状态段TSS 具有固定的格式,最小尺寸是104 字节,下图中所标注的偏移量是十进制的。

处理器用TR 寄存器来指向当前任务的TSS。

当任务切换发生的时候,TR 寄存器的内容也会跟着指向新任务的TSS。这个过程是这样的:

  1. 处理器将当前任务的现场信息保存到由TR 寄存器指向的TSS;

2.再使TR 寄存器指向新任务的TSS,并从新任务的TSS 中恢复现场。

比较奇怪的是,为什么这个寄存器叫TR,而不是TSSR。原因很简单,TSS 是一个任务存在的标志,用于区别一个任务和其他任务。所以,这个寄存器叫做任务寄存器(Task Register:TR)。

当特权级变化时,CPU从TSS中加载对应DPL的栈指针(用户态使用ss1+esp1,内核态使用ss0+esp0).

From #

TSS描述符

调用门描述符

Content #

调用门(Call-Gate)用于在不同特权级的程序之间进行控制转移。本质上,它只是一个描述符,一个不同于代码段和数据段的描述符,可以安装在GDT 或者 LDT 中。该描述符的格式如下图所示,下面是低32 位,上面是高32 位。 调用门描述符的格式

调用门描述符给出了例程所在代码段的选择子,而不是32 位线性地址。有了段选择子,就能访问描述符表得到代码段的基地址,这样做间接了一点,但却可以在通过调用门进行控制转移时,实施代码段描述符有效性、段界限和特权级的检查。

例程在代码段中的偏移量也是在描述符中直接指定的,只是被分成了两个16 位的部分。很显然,在通过调用门调用例程时,不使用指令中给出的偏移量。

描述符中的TYPE 字段用于标识门的类型,共4 比特,值“1100”表示调用门。

描述符中的P 位是有效位,通常应该是“1”。当它为“0”时,调用这样的门会导致处理器产生异常中断。对于操作系统来说,这个机关可能会很有用。比如,为了统计调用门的使用频率,可以将它置“0”。然后,每当因调用该门而产生异常中断时,在中断处理程序中将该门的调用次数加一,同时把P 位置“1”。对于因P 位为“0”而引起的中断来说,它们属于故障中断,从中断处理过程返回时,处理器还会重新执行引起故障的指令。此时,因P 已经为“1”,所以可以执行。就当前的例子而言,因为在提供调用门服务的同时,还要统计门的调用次数,故,可以在该调用门所对应的例程中将P 位清零。这样,下一次该门被调用时,又会重复以上过程。

From #

ostream_iterator输出容器内容

Content #

C++中用ostream_iterator输出下面的容器内容,应如何操作?

vector<int> v2{1, 2, 3, 4, 5};
#include <iostream>  // std::cout
copy(v2.begin(), v2.end(), ostream_iterator<int>(cout, " "));

From #

思考与言语

Content #

思考与言语之间的关系是什么?

曾经有很多人试图解释思考的本质,其中最普遍但现在基本上已被推翻的一种观点是:思考完全是言语性的。根据这种理论,思考就是我们在头脑中遣词造句,或者无声地自言自语。但如果事实真的如此,阿尔伯特·爱因斯坦就算不上思想家,因为他的思考包含更多图像而非言语。

当代的权威们一致认为,想法通常以言语的形式在头脑中形成,但这不是必须的。与表达想法一样,除了言语之外,我们也可以用数学符号和图像来构思想法。

From #

创建共享库

Content #

有libfoo1.c和libfoo2.c两个源代码文件,希望产生一个libfoo.so.1.0.0的共享库,这个共享库依赖于libbar1.so和 libbar2.so这两个共享库,可以使用如下命令行:

$gcc –shared -fPIC –Wl,-soname,libfoo.so.1 –o libfoo.so.1.0.0 \
libfoo1.c libfoo2.c -lbar1 -lbar2

当然我们也可以把编译和链接的步骤分开,分多步进行:

$gcc –c –g –Wall –o libfoo1.o libfoo1.c
$gcc –c –g –Wall –o libfoo2.o libfoo2.c
$ld –shared –soname libfoo.so.1 –o libfoo.so.1.0.0 \
libfoo1.o libfoo2.o –lbar1 –lbar2

几个值得注意的事项:不要把输出共享库中的符号和调试信息去掉,也不要使用GCC的“-fomit-frame-pointer”选项,这样做虽然不会导致共享库停止运行,但是会影响调试共享库,给后面的工作带来很多麻烦。

在开发过程中,你可能要测试新的共享库,但是你又不希望影响现有的程序正常运行。我们前面提到的LD_LIBRARY_PATH是一个很好的方法,用它可以指定共享库的查找路径。还有一种方法是使用链接器的“-rpath”选项(或者 GCC的-Wl,-rpath),这种方法可以指定链接产生的目标程序的共享库查找路径。比如我们用如下命令行产生一个可执行文件:

$ld –rpath /home/mylib –o program.out program.o –lsomelib

这样产生的输出可执行文件program.out在被动态链接器装载时,动态链接器会首先在“/home/mylib”查找共享库。

From #

程序员的自我修养