2008/10

又见汇编技巧

在 arch/x86/include/asm/uaccess.h 中有这么一段代码:

define __range_not_ok(addr, size)

({
unsigned long flag, roksum;
__chk_user_ptr(addr);
asm(“add %3,%1 ; sbb %0,%0 ; cmp %1,%4 ; sbb $0,%0”
: “=&r” (flag), “=r” (roksum)
: “1” (addr), “g” ((long)(size)),
“rm” (current_thread_info()->addr_limit.seg));
flag;
})

这段汇编写得很有技巧性,充分利用了sbb指令carry flag,值得你仔细体会一番。还有一个关于sbb的技巧:

sbb eax,eax
sbb eax,0FFFFFFFFh

读机器人系列

机器人系列完全可以作为基地系列的一部分。机器人系列的时间跨度虽然大概能够达到1700多年,但故事情节主要集中在中间的不到一百年的时间中,这一点和基地系列截然不同。而且在机器人系列的第一部《我,机器人》中,阿西莫夫使用了他少有的一种叙述方式——通过一个机器人学家的叙述来描述整个故事,通过若干个小故事讲述了机器人的发展情况。当然了,在今天看来,阿西莫夫当时过于乐观了。:-)

毫无疑问,整个机器人系列的主人公是贝莱——这个地球人侦探,他三次出手破解悬案,三次挽救了地球的命运。这正和阿西莫夫的手法:看似不起眼的小故事却可以直接影响大局。而在这三个故事中,机器人和人类的关系也展现得淋漓尽致,机器人如何从被人依赖到被人抛弃,以及人类如何从地球走向整个宇宙。机器人和人类的关系错综复杂,但毫无疑问,有个机器人改变了整个人类的命运,他就是丹尼尔,这个协助贝莱破案的类人机器人,同时,如果你读完基地系列的话你也会知道,他也是后来基地系列中的帝国宰相——丹莫尼茨,他活了上万年,他是连接机器人系列和基地系列唯一的角色。如果我们把机器人系列和基地系列之间的故事也写出来的话,那他注定会是主角。他之所以能够影响整个人类的命运是因为两个原因:一是他领悟出了机器人第零定律,并把它放到第一定律之前来遵守;二是他从另一个机器人——吉斯卡特——那里得到了一个强大的能力——读心术。那个机器人是最早拥有读心术本领的机器人之一(未必是第一个),他其实也影响了整个人类的命运,而且他在临终前留给丹尼尔的话暗示了基地系列的故事发展。

如果说谢顿计划是基地系列的核心,那么机器人三定律则是机器人系列的核心,在机器人系列中,这三大定律一次又一次的被演绎着,多次成为决定故事结局的关键。而到了最后,三大定律被机器人“推翻”,演变成了四大定律,并从此以后开始改变整个人类和宇宙的命运……

纵观整个机器人和基地系列,故事从地球起到地球终,似乎画了一个完美的圆圈。不得不感叹阿西莫夫的本领!

最后值得一提的是:

1) 其实心理史学并不是最先由谢顿提出来的,早在人类开发宇宙的前期,奥罗拉的机器人学家法斯托尔弗就提到了心理历史学(见《曙光中的机器人》的最后);
2)《基地与地球》中提到地球已经因为辐射而被人类遗弃,《机器人与帝国》中很好的解释了这个原因。索拉里亚人的离开成为机器人系列的疑点,吉斯卡特成功地领悟到了这个事件的重要性,这将在最后的《基地与地球》中提到;
3) 阿西莫夫的《苍穹微石》也应该被囊括进系列中来,它像是连接机器人系列和基地系列的纽带,故事恰好发生在它们之间,其中进一步解释了地球辐射的问题。
4) 想读《基地的胜利》,虽然不是阿西莫夫本人写的,但其最后很好地按时间先后顺序总结了整个系列的重大事件。从其介绍来看,此书叙述的情节恰好是弥补阿西莫夫系列中缺少的部分。

(机器人系列的阅读顺序见我上一篇关于基地的介绍。)

整数除法

C似乎没多少疑问,不过当有负数时还是需要特别注意的,因为标准要求是要向0对齐的。所以-5/2会是-2而不是-3。

6.5.5

When integers are divided, the result of the / operator is the algebraic quotient
with any fractional part discarded [Footnote].

Footnote: This is often called “truncation toward zero”
在C89的时候,这个问题还是交由编译器决定的。C99加强了这一点。C为什么会选择这一点?其实原因很简单,因为这么做节省时间:直接舍去小数部分。

Python在这一点上和C不同,因为它还保留了一个int(),这个和C在取整方向上是一致的:Python中的整数除法向负无穷对齐;而int()才是向0对齐的。见PEP238

Note that classic division (and floor division) round towards negative infinity,
while int() rounds towards zero, giving different answers for negative numbers.
不要被迷惑了,在Python里int(-5/2)依旧是-3而不是-2,你知道为什么。:-) 在PEP 238中,Python引入了一个新的运算符://,它始终会向下截取除法的结果(数学上的取整,英文谓之floor),看下面的例子:

>>> -5//2
-3
>>> -5.1/2.0
-2.5499999999999998
>>> -5.1//2.0
-3.0

Perl更不同,它只能通过int()来进行取整,因为普通除法在Perl里得到的结果是浮点数。《Programming Perl》中提到:

You should not use this function for generic rounding, because it truncates towards 0 and because machine representations of floating-point numbers can sometimes produce counterintuitive results.
所以这个也是向0对齐的。

再看ruby,这个似乎很简单,一律是向负无穷对齐的:

$ ruby -e ‘puts -5/2; puts -5.div(2); puts Kernel.Integer(-5/2);’

-3
-3
-3

Bash和C一致:

$ echo $[5/2] && echo $[-5/2]
2
-2

Java似乎也和C一致,不过我未验证。:-)

这里还有一个问题值得思考:为什么这些编程语言要把整数除法和浮点数除法区别对待?对于C,这个问题很简单,因为很久很久以前C并没有浮点数,只有整数,而其它语言似乎是因为受到了这一影响,当然Perl例外。不过,在很多情况下整数除法已经够用,无须打扰浮点数。

又遇SNMP

差点被SNMP给整疯了……

先是要从QoS中提取指定信息,要导出到SNMP。这看似简单但做起来相当麻烦,基本上就是左手拿着表格查对应的行和列,右手拿着QoS输出对比,然后脑子里要思考如何提取正则表达式。总共50多项,我是一项一项对出来的,哎,没办法,这根本不可能由机器完成(瞧瞧计算机有多笨!!不过它要是聪明到这种程度我们这些程序员就该失业啦~!)。

然后要考虑整个程序的构架,看似一个简单的程序,如果什么都不考虑直接去写肯定也可以,50多个函数嘛,可问题是怎么把代码组织得更和逻辑?这确实得下一番功夫。一开始我想出一个颇为简洁的构架,后来发现不行,经过修改最后居然保持住了简洁,纯属侥幸~~最后再看看整个程序的构架,依然简洁而又合理,先得意一下。;-)

最后测试时又出问题了,因为我几乎把SNMP忘干净了(Dr. Chen,我对不起你啊~~~!),然后翻怎么才能查询到对应的表项,满头大汗……不过最后终于完成了,还好,挑了几个经典的项放进去测试了一下,一切正常。不过,上头要求把50多项全部放进SNMP里测,我晕,难道我还得一个一个把它们手工输入到snmp配置文件里???岂有此理!我怒了,端出如下一行命令搞定一切:

sed -ne ‘/cmd_map/,/#EOF/ p’ qosscript | grep ‘=>’ | awk -F’=>’ ‘{print $1}’ | while read EN; do echo exec $EN /eos/qosscript $EN; done | sed -e ‘s/‘“‘“‘//g’ >> /etc/snmp/snmpd.conf

任务结束,世界安静了许多。:-)

另外记一下在写这个脚本时用到的一个技巧——用Perl正则表达式匹配多行文本:

local $/;
$output = <$fd> ;

然后就可以这么匹配了:

$output =~ m/class htb . root rate ([0-9]+)Kbit .nsSent ([0-9]+) bytes ([0-9]+) pkt (dropped ([0-9]+).ns*rate ([0-9]+)bit/m

美丽中国

英国BBC拍的一部记录中国自然风景和野生动物的纪录片,相当美,里面的一些画面美得简直震撼人心!印象最深刻的是蝙蝠在水面上拍打的画面,太美了……我们的祖国真的很美!

第2集提到了香格里拉——我心中那个向往已久的胜地,回国一定去那里看看!!另外还有西藏,丝绸之路,内蒙古大草原……

强烈推荐此片,给每个人。:-)

陆止于此,海始于斯!

Cabo da Roca是欧洲大陆的最西端,有着非常壮阔的风景。这里天很蓝,风很大,站在悬崖边上望去是一片蔚蓝的大海,非常迷人。在卡罗角的纪念碑下,你终于可以感慨:我站在欧洲大陆最西端了!

500年前,葡萄牙诗人卡蒙斯在此写下一行诗:

AQUI … ONDE A TERRA SE ACABA E O MAR COMECA …

翻译成英文是:

Here… Where the land ends and the sea begins…

中文的翻译是本文的标题。“天涯海角”的含义在这一刻你能更深刻地体会到!

500年前的葡萄牙应该是航海英雄的时代,不知道500年前的他站在此处时又是怎样的心情?对于真正的水手来说,或许真的是“天无涯,海无角”……

fakeroot vs sudo

1. sudo是一个setuid程序,而fakeroot不是。

2. sudo真正地给与root权限,而fakeroot不会,它只会欺骗程序看起来像是以root身份执行得到的效果。

3. fakeroot最初是为了创建debian包使用的,Fedora上也有fakeroot包,但不知为何找不到其man手册……

4. fakeroot的文档中提到:

fakeroot works by replacing the file manipulation library functions
(chmod(2), stat(2) etc.) by ones that simulate the effect the real
library functions would have had, had the user really been root. These
wrapper functions are in a shared library /usr/lib/libfakeroot.so*
which is loaded through the LD_PRELOAD mechanism of the dynamic loader.
[…]
Internally, faked keeps track of all files which have had operations
done on them that the user would not normally be able to do, in order to
fake the effect later.

For example, if you create a device node, faked will actually create an
empty file, but remember that it was a device node (along with the
relevant details) so that if you subsequently do an “ls -l”, you’ll see
it as a device node.
所以fakeroot并不会对静态链接的程序起作用。

5. fakeroot使用了很多trick,不信的话查看其源代码(看看里面那个awk脚本!)。

bash变量作用域

bash的变量作用域其实很简单,不过有些时候可能看起来很奇怪。下面简单总结一下。

在默认的情况下,bash的变量都是全局的,除非你使用了local或者typeset命令来改变,那样就可以变成局部的了。

export改变的是变量是否对其子进程可见,并不会影响父进程,如果想要改变父进程中的变量可以用source(或者“.”)。需要额外说明的是,source后面的脚本不能有exit,否者父进程就跟着一起退出了。

在subshell中,对变量的改变不会影响到外面的shell。这似乎很容易理解,不过需要注意的是,一些管道(比如管道的一端是while循环时)也是通过subshell传递的,比如下面这个例子

  1. foo=123
  2. echo 456 | while read line ; do
  3. foo=$line
  4. done
  5. echo $foo


这时就不那么容易觉察了。解决方法是,用重定向去替换管道。

城堡

葡萄牙有很多城堡,在Coimbra附近的有三个地方的城堡比较有名,这几个星期利用周末把这几个地方都转了一遍。

Leiria:山顶上有个城堡,这个城堡是唯一一个收费的,不过不贵,里面可以看到不错的风景,很值得一去。

Montemor-o-Velho:离Coimbra最近,不过坐火车无法直接到,需要在Alfarelos下车后打个的才能到,车站旁边那个出租车司机很nice的说~~!这个城堡独特的地方是里面有个教堂,还有个草坪。

Pombal:这个城堡不那么出名,去的人似乎很少,我到那里的时候里面就我自己~~可以自己随便逛,站到最高出远望感觉挺不错的~~

这个周末打算去传说中的Cabo da Roca——欧洲大陆的最西端。:-)