2007/11

回家几天

恩,现在工作的事也不用愁了,期末还没到,学习也不是很紧张,再加上暑假没有回家,于是决定这个周末回家一趟,待两三天再回来。

下午5:30的火车,明天一早就能到了!就能见到爸妈了,也能住进刚搬的新楼了。;-)

这三天的邮件肯定就不能回复了,不管您有什么事情,等下周二我回来了再说吧!

八皇后问题的非递归解法

用C++实现了八皇后问题的非递归算法。原理很简单,看代码就是了,无须多说。

BTW: C++的algorithm就是好用啊!

include

include

include

include

using namespace std;
const int MAX = 8;

vector board(MAX);

void show_result()
{
for(size_t i = 0; i < board.size(); i++)
cout<<"("<<i<<","<<board[i]<<")";
cout<<endl;
}

int check_cross()
{
for(size_t i = 0; i < board.size()-1; i++)
{
for(size_t j = i+1; j < board.size(); j++)
{
if((j-i) == (size_t)abs(board[i]-board[j]))
return 1;
}
}
return 0;
}

void put_chess()
{
while(next_permutation(board.begin(), board.end()))
{
if(!check_cross())
{
show_result();
}
}
}

int main()
{
for(size_t i =0; i < board.size(); i++)
board[i] = i;
put_chess();
return 0;
}

谈谈版权

虽然我是一个自由软件的支持者,但说来惭愧,一直以来我对版权这问题关注比较少。因为我更关心的是自由软件可以自由获取/修改源代码这一面。

但最近听说的一些事情,使我不能不再关注这个问题了。一件事就是美国的“P2P的圣女贞德”事件,另一件是我们老师上课提到的,说学校曾因使用盗版VB而接到警方的“通知”。

这两件事使我对这个问题有了更深刻的认识,版权问题就实实在在地在我们身边,如果我们再不关心这个问题,下一个被起诉的,或者接到警方“通知”的可能就是我们自己!

在版权方面,我绝对站在Richard Stallman这边。他写的两篇文章对我触动很大,一是《为什么软件不应该有所有者?》,里面讨论了专有软件版权的不合理性,限制软件自由的危害——不利于良好的社会风气,不利于社会的自由。这也是为什么他一贯主张“自由的软件,自由的社会”。另一篇是《科学必须摒弃版权》,里面的最后一句话至今记忆犹新:

“版权的存在是‘为了推动科学的进步’。当版权阻碍科学进步时,科学必须摒弃版权。”
这句话再正确不过了。任何阻碍科学发展的东西我们都应该摒弃它。Richard Stallman发起浩浩荡荡的自由软件运动的本意也正在于此!(所以我更愿意把Richard Stallman看作是一个哲学家,一个圣人。;-)

记得美国的一个博客作家曾说过这么一句话:

“比特天生就是用来拷贝的,它们永远不会变得难以复制。”
我非常欣赏。把上面的话换一句说就是,错误不在那些拷贝比特的人,而在那些想方设法阻止你拷贝的人!现存的版权制度有很多不合理的地方,它已经远离了最初作为“保护作者权益”的目的,沦落成了某些组织借此打压弱者的工具!我从来不反对保护软件作者的权力,但是,一个显而易见的事实是,没有了版权的约束作者利益并不会受到侵犯,反而倒有可能对作者更有利。软件方面最好的例子莫过于Linus,假设当年他并没有公开Linux源代码,他还会像今天这么有名吗?显然不能!也就是说,Linus放弃了用版权法限制他的作品反而倒让他获得更大的成功。其它方面,Magnatune就是一个很好的例子,把自己的作品“开源”并没有让这些艺术家吃不上饭,反而找到了更多的知音。

技术,能够给我们提供自由,也能剥夺我们的自由。关键在于你是怎么选择。我所能做的,就是用自己的技术来为更多人提供自由,自由的软件,自由的社会。

引用Zoom Quiet模仿马丁·尼莫拉名言的话作为结束:

在中国,起初他们忽视Unix用户,我没有说话——因为我不是Unix用户;
接着他们忽略GNU/Linux用户,我没有说话——因为我不是GNU/Linux用户;
后来他们封杀Mac用户,我没有说话——因为我不是Mac用户;
此后他们追杀D版XP用户,我没有说话,因为我不是D版XP用户;
最后他们奔我而来,不交钱,不使用捆绑广告的专用软件就不准开户使用任何在线银行业务,却再也没有人站出来为我说话了!

TCP Closure

今天在看TCP的释放时遇到几个问题,查了一下《TCP/IP详解 卷一》和RFC793,也请教了一下Herbert Xu,基本上都解决了。在这里总结一下。

TCP的释放过程如下图所示(取自《TCP/IP详解 卷一》):

问题1:既然TCP的CLOSE是单工的,那么,一方在收到FIN的ACK之后可能会过很长一段时间才收到对方的FIN。那么如果这个时间里这方已经关闭该进程,谁又来ACK对方的FIN呢?

Herbert解释到:

For you particular scenario, the key is that the TCP socket is
maintained by the OS, not the process.

So even after your process has called close(2) and exited, the
OS will continue to respond in ways required by RFC793 until
such a time when the socket has been CLOSEed in the sense of the
protocol.
问题2:两方同时发送FIN又如何呢?

没想到这个问题连RFC里都有,里面这样解释:

A simultaneous CLOSE by users at both ends of a connection causes
FIN segments to be exchanged. When all segments preceding the FINs
have been processed and acknowledged, each TCP can ACK the FIN it
has received. Both will, upon receiving these ACKs, delete the
connection.
也就是像下图所示(也是取自TCP/IP详解):

问题3:关于MSL。

MSL是maximum segment lifetime的缩写。TCP状态机里有个TIME_WAIT状态,设置它是为了防止最后一个ACK丢失,这个时间一般就是2MSL。而且,在这个2MSL的时间中,这个socket是不能被重用的,除非我们setsockopt(…,SO_REUSEADDR,…)。

shebang

在Unix中,shebang其实就是指“#!”,它取自#(SHArp)和!(bang)。

它是很多脚本文件中第一行的前两个字符,用来告诉Unix系统要用shebang后面指定的解释器
来解释该脚本。所以,在很多脚本中,第一行往往都是这么写的:

! /abs/path/to/interpreter

根据wikipedia上的解释),shebang最初由
Dennis Ritchie引入的,时间大概是在Version 7和Version 8之间。也正是因为shebang是以#
开头,所以很多Unix上的脚本都是用#作为注释的开始。常见的有:

!/bin/sh

!/usr/bin/perl -w

!/bin/awk -f

!/usr/bin/env python

!/usr/bin/ruby

shebang没有你想象得这么简单。首先,它后面的解释器路径一般来说必须是绝对路径。(当然了,
有人也说像#!python这样的也能执行。)如果不是,可能就会出现类似的错误:


bash: ./foo.py: python: bad interpreter: No such file or directory

因为shell会直接用execve去执行这个文件,出错马上就退出。而execve会通过shebang辨认出这个一
个脚本文件,然后尝试用后面的解释器去执行,但它并没有在PATH中寻找解释器,而是完全依靠
你给出的路径。如果你不想指定绝对路径或者出于可移植的原因不好指定,那么你应该试试用env(1)
,就上面的python的shebang一样,它会帮你在PATH中搜索。python之所以更倾向于这个还有个原因,
就是env一般固定在/usr/bin目录下,而python的安装位置就相对不那么固定。

用env时你应该注意这么一个事实:传递给解释器的argv和你想象得并不一样。下面这个就是不对的:

!/usr/bin/env perl -w

shell会提示:/usr/bin/env: perl -w: No such file or directory
错误的根源就是perl -w被当成了整体传递给env。

用下面的程序来看一下参数传递过程:


/test.c/

include <stdio.h>

int main(int argc, char** argv) {
int i;
for (i=0; i<argc; i++)
fprintf(stdout, “argv[%d]: “%s”n”, i, argv[i]);
return 0;
}
然后把编译出的test当作解释器:


$ cat invoker.sh

!/home/wangcong/test -1 -2 -3

结果如下:


$ ./invoker.sh a b c
argv[0]: “/home/wangcong/test”
argv[1]: “-1 -2 -3”
argv[2]: “./invoker.sh”
argv[3]: “a”
argv[4]: “b”
argv[5]: “c”

当然了,并不是所有的Unix都是这样,但最起码Linux上的bash和zsh上就是如此。所以,要编写可移植
的脚本,你应该当心这一点!

RSS的问题

因为这个博客自带的rss出了点问题,使用这个网址订阅的看客会收不到更新。原因我不清楚,毕竟我对php一窍不通。;(

鉴于此,我在feedsky上注册了一个。使用rss订阅本博客的可以换用下面这个地址来更新。

http://feed.feedsky.com/wangcong

Sigh, feedburner is blocked by something…

强符号,弱符号

对于链接器来说,所有的全局符号可分为两种:强符号(Strong symbols),弱符号(Weak symbols)。gcc的attribute中有个attribute((weak)),就是用来声明这个符号是弱符号的。gcc手册中这样写道:

The weak attribute causes the declaration to be emitted as a weak symbol rather than a global. This is primarily useful in defining library functions which can be overridden in user code, though it can also be used with non-function declarations. Weak symbols are supported for ELF targets, and also for a.out targets when using the GNU assembler and linker.
对于这个gcc扩展,这里作了一个简洁的介绍。我们来看更通用的情况。;)

一般来说,函数和已初始化的变量是强符号,而未初始化的变量是弱符号。对于它们,下列三条规则适用:

1. 同名的强符号只能有一个。
2. 有一个强符号和多个同名的弱符号是可以的,但定义会选择强符号的。
3. 有多个弱符号时,链接器可以选择其中任意一个。

这三条规则看起来很好理解,其实不然,尤其是当这些弱符号类型和强符号不同时!表面上看起来正确的程序会导致严重的错误!考虑下面这个csapp中的例子:

===a.c===
int x=7;
int y=5;
p1() {}

===b.c===
double x;
p2() {}

我们把它们一起编译,并且在p2()函数中给x赋值,你会发现,y也改变了! 虽然x被看作是double,但其定义会取a.c中的int x,也就是说,在b.c中会把a.c中的int x当double来用!这当然是错误!之所以会这样,就是因为上面的规则2。避免这种错误的一个方法是,给gcc加上-fno-common选项。

关于弱符号,man手册中这样解释到:

When a weak defined symbol is linked with a normal defined symbol, the normal defined symbol is used with no error. When a weak undefined symbol is linked and the symbol is not defined, the value of the weak symbol becomes zero with no error.
因此,我们也可以看出:强弱符号和声明没任何关系。弱符号不会有static的storage duration。同名的多个弱符号的定义不应该出现在同一个翻译单元中,如果出现,链接器会选择第一个。

关于学习Linux

今天给我们兴趣小组作了简单的讲座,讲了一下自己学习Linux的经验。

Slides可以在<—这里—>下载。希望新来的同学能比我们做得更好!

把下面这句话和大家共勉:

God helps those who help themselves. (自助者天助!)

整数开平方根

今天被问到如何求一个整数的平方根,不知,速查之,得下面一程序:


unsigned int sqrt(unsigned int n){
unsigned int a;
for (a=0;n>=(2a)+1;n-=(2a++)+1);
return a;
}

厄,仔细一看,它的原理其实也很简单,可以用下图表示:
{0,1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4……}

喜欢的几首古诗词

今天去参加AMD的笔试,计算机题就不说了,狠简单,连memcpy的也不在话下(谁让咱看过它的汇编实现呢!!直接翻译成C就可以了。)。

最值得一提的是,上面居然还有一道题说让写出你最喜欢的一首诗词。恩,那我就在这里也写写这个话题吧!

说实话,在高中学古诗词的时候,给我感触最深的基本上都是悼念亲人的那些诗词,文章。大概人在最悲痛的时候的感情才是最深刻的吧!在这里面,最琅琅上口的当推苏轼的《江城子》,这首苏东坡悼念亡妻的诗词字字含泪,每每读起仍是心有感动!

十年生死两茫茫,不思量,自难忘。
千里孤坟,无处话凄凉。
纵使相逢应不识,尘满面,鬓如霜。

夜来幽梦忽还乡,小轩窗,正梳妆。
相顾无言,惟有泪千行。
料得年年肠断处,明月夜,短松冈。
苏东坡不仅有高唱“大江东去”的豪迈气概,更有英雄为美人落泪的柔情。不得不佩服的说。

另一个当推韩愈的《祭十二郎文》,里面的一句“一在天之涯,一在地之角,生而影不与吾形相依,死而魂不与吾梦相接”感人至深!是不可多得的佳作!

当然了,另一篇类似的古文当推袁枚的《祭妹文》,里面的“汝死我葬,我死谁埋?汝倘有灵,可能告我?”也是令人悲痛万分的名句!