2009/2

从电影看编剧的艺术

《疯狂的赛车》和《疯狂的石头》有个共同的特点就是以情节取胜,把一开始看似无关的几个情节线索交织穿插到一起,最后达到一种惊奇和喜剧的效果。

做到这并不容易,单独拿出任何一条线索来看都合理,把它们组合起来依然合理,而且它们的交织还要显得自然而然,而不是看起来像是故意要巧合而巧合,所以,这是一种艺术!

我不是专业人士,不可能从专业角度去分析,我只能说说我的个人体会,我觉得,这里最关键的是要把人物的身份和职业合理地组合起来,然后靠某个重要的东西使这些人物因此发生矛盾,进而把所以线索都串联起来。

看看《疯狂的赛车》中那个主要角色:耿浩被禁赛后要不是搞海鲜运输的也不会导致后面的泰国人那样被杀;丧葬服务公司的总经理,要不他推出那种独特的丧葬服务耿浩也不会和那个黑社会老大扯上关系;那个李法拉,要不他搞假药并且在体育场做宣传也不会导致耿浩被禁赛,从而激化后面的矛盾……而要把这两条比较远的线扯上关系就得是那个两个笨贼了,再加上有警察掺和,所以这些情节开始互相交织了……可见,戏里角色的职业身份有多么重要!而且我们也许都已经注意到,这两个疯狂的电影中都有笨贼和警察(或保安),笨贼正是由于其笨,做一些荒唐的事看起来也合理,所以靠他们来连接这些情节也就不足为奇了。

上面提到的“某个重要的东西”似乎就更明显了,就是《疯狂的石头》里的翡翠,《疯狂的赛车》里的美金,没有它们就没整个故事。而且我还注意到有个比较有意思的地方,那就是这些东西到最后都没好结局,真翡翠最后当作假的被保安送女友了,美金被当作骨灰给一起埋了……争来争去到最后谁也没真正捞着,哈,不愧是喜剧!

用bash自己计算CPU和内存使用率

如果你不自己计算,最简单的办法就是去解析ps的输出(top的应该比较麻烦一些)。如果我们自己亲自来计算呢?

我写了下面这么个脚本,可以粗略计算。为了对照方便,我同时给出了以这两种方式获取的结果。比较有趣的是,我在测试的过程中发现ps有时给出的CPU占用率结果竟然是101%!没看源代码,不知道它是怎么计算的……

BTW:无意间发现还有个dstat,貌似很酷~~8-)

[bash]

!/bin/bash

if [ $# -gt 0 ] ;
then
pid=$1
if [ ! -d /proc/${pid} ] ;
then
echo “No such process!”
exit 1
fi
else
pid=$$
fi
echo “the pid is: ${pid}”
tmp1=$(awk ‘/cpu /{print $2,$4;}’ /proc/stat)
tmp2=$(sed -e ‘s/(.*)//‘ /proc/${pid}/stat | awk ‘{print $13, $14;}’)
old_sys_stime=$(echo ${tmp1} | cut -d’ ‘ -f 2)
old_sys_utime=$(echo ${tmp1} | cut -d’ ‘ -f 1)
old_prc_stime=$(echo ${tmp2} | cut -d’ ‘ -f 2)
old_prc_utime=$(echo ${tmp2} | cut -d’ ‘ -f 1)

do some dummy stuffs

for((i=0;i /dev/null

echo “ps reported its cpu usage was: $(ps -o pcpu -p ${pid} | tail -n 1)%”

tmp1=$(awk ‘/cpu /{print $2,$4;}’ /proc/stat)
tmp2=$(sed -e ‘s/(.*)//‘ /proc/${pid}/stat | awk ‘{print $13, $14;}’)
new_sys_stime=$(echo ${tmp1} | cut -d’ ‘ -f 2)
new_sys_utime=$(echo ${tmp1} | cut -d’ ‘ -f 1)
new_prc_stime=$(echo ${tmp2} | cut -d’ ‘ -f 2)
new_prc_utime=$(echo ${tmp2} | cut -d’ ‘ -f 1)

echo -n “my calculation is:”
echo $(awk “BEGIN { print (($new_prc_stime - $old_prc_stime) + ($new_prc_utime -$old_prc_utime))/(($new_sys_stime - $old_sys_stime) + ($new_sys_utime -$old_sys_utime));}”)

#

echo “ps reported its memory usage was: $(ps -o pmem -p ${pid} | tail -n 1)%”
total=$(awk -F: ‘/MemTotal/{print $2}’ /proc/meminfo | sed -e ‘s/[^0-9]//g’)
mine=$(awk -F: ‘/VmRSS/{print $2}’ /proc/${pid}/status | sed -e ‘s/[^0-9]//g’)

echo -n “my calculation is:”
echo $(awk “BEGIN{print $mine/$total;}”)

exit 0
[/bash]

一个Bash重定向的问题

网上有人问到一个有意思的问题:如何把一个命令的输出同时重定向到3个文件?分别是:标准输出一个,标准错误输出一个,这两个都有又是一个。

我一开始以为这个不难,但我仔细想想发现不是,根本就没那么简单!首先一个问题,你重定向某个文件句柄到文件(或者另一个句柄)后意味着后面你就得不到了;其次,复制一个流我们的工具只有tee。所以,我们可以看出,这里tee至少得被调用两次才可以,因为两个流被都被复制一次。我的想法是:先tee标准输出,然后交换1和2,再tee标准错误,然后再重定向1和2到文件。

我还没来得及把想法付诸于实践,别人就有解答了,而且他的比我的还简单!看了他的我反省了一下自己的解法,第2步其实没必要交换,因为1已经被重定向过了,只需要一个临时的把它保存一下即可,下面是他的解答:

{ { your_cmd | tee stdout; } 2>&1 1>&3 | tee stderr 1>&3; } 3>stdout+err

作者说应该还有更简单的,但我是想不起来了。有知道的还望不吝赐教!(自己重写个tee不算,请用已有的命令。)

如果你觉得上面的重定向难理解,那下面这个呢?:-)

{
  {
    cmd1 3>&- |
      cmd2 2>&3 3>&-
  } 2>&1 >&4 4>&- |
    cmd3 3>&- 4>&-

} 3>&2 4>&1

关于sed的N命令

N命令很简单,就是把下一行追加到匹配空间中,我一直都是这么理解的,直到我遇到这么个问题:

~% echo -e ‘anbncndne’ | sed -ne ‘N;/$/{s/cnd/FF/;p}’
a
b
FF
~% echo -e ‘anbncndne’ | sed -ne ‘N;/$/{s/bn.*nd/FF/;p}’
a
b
c
d

按理说我每次都用N,最后得到的应该是:”anbncndne”才对啊!怎么就这么奇怪呢?所以我开始怀疑对N的理解,有人告诉我可以这么测试:

$ echo ‘a
b
c
d
e’ | sed -ne ‘N;/$/{s/.*/>&</;p}’
>a
b<
>c
d<

哈!现在问题一目了然了!出问题的不是N,N确实每次都读入了下一行,但是,sed下次再调用N时已经将下下一行读入并覆盖掉匹配空间了!所以,解决方法就是不让它替换掉!怎么办呢?一个方法就是有sed的”goto”:

$ echo -e ‘a
b
c
d
e’ | sed -ne ‘:t;N;$!bt;s/bn.*nd/FF/;p’

可见,sed很拧把却又很强大!理解这个强大确实得下一番功夫!把文本编辑的逻辑抽象成一个个sed的命令,Lee E. McMahon很牛x!同时我们也可以看出:牛人们玩的都是抽象~!而我们这些非牛们只能玩玩实现了~ :-)

咋又那么倒霉呢

唉,这两天又是那么倒霉,可能最近人品太差了。

先是我把一个银行卡的密码给忘了。本来办这个卡的时候想着就用那么一次,随便选个没用过的密码吧!好,结果一年多以后再用的时候我彻底把那个密码给忘了!而且我一开始把这事也给忘了,还给里面充了一次钱,然后我用的时候密码不对,我又到了银行试密码试了好几次后我才意识到办这卡时我并没有用自己常用的密码!唉,结果在银行里那个囧啊,好像我在那里搞暴力破解似得……唉,没办法,只能当挂失处理,又交钱填了个挂失申请,等三天后再去弄。

另一件事也够倒霉的,我的两台电脑都出问题了,几乎同时!先说我的笔记本吧。我昨天稀里糊涂地升了个级,大概得有一百多个包吧,用的图形界面,没仔细看,升级完重启后发现End键突然不能用了!而且就这一个不能用!奇怪了,升级前好好的,莫非突然就坏了?退出后重新登录,在登录窗口和纯命令行下试了试,没任何问题,但一到桌面就不能用!我想可能是KDE的问题吧,换了Gnome,E16什么的也一样!换了多个键盘布局还是一样。后来我又想可能是内核的问题,但换了以前用的内核也一样!最后我想可能是xorg的问题,我手工把xorg的键盘驱动给降级,结果还是一样。。。无语了,我只能说这可能是xorg的bug什么的,抽空到fedora论坛上问问去,暂时先凑合着。

怎么就又波及到我的另一台电脑了呢?上面我提到整xorg,一开始整的我连图形界面都进不去了,然后我换到另一台上去上网搜索,弄了半天总算又把图形界面弄出来了。这时我想给这台电脑也升升级吧!好家伙!一升级又出问题了!400多个包,缺少的依赖的包一堆!一开始我在那里纯手工解决依赖!后来实在受不了,网上一搜,发现fedora 7(台式机用的还是7)确实有问题,照着网上说的把源一改就行了。后来又出现几个依赖错误,手工全部干掉了,还好不多……

所以说啊,没事别瞎升级,赤裸裸的教训啊!

从那段历史看大屠杀

非常奇怪,这本书居然不是禁书。

关于文||革的事在书上电视上没少看了,感觉荒诞得很,可要是看了这本书你会发现以前看到的那些连荒诞都称不上,书中的故事有的比那荒诞几十倍!我们这些没经历过的人对这段历史了解实在是太少了!

书中给我印象最深的故事是那个不会笑的人的故事,一个人不笑居然也能被当作是怀疑证据然后抓起来!恐怕荒诞剧作家也写不出这样的故事来吧!而它在当时活生生地发生过!还有,里面提到了一个更少有人知道的“63号”,至少我在看这本书之前从来没听说过,不看不知道,一看吓一跳,那个地方当时比集中营还集中营!一些整人手段恐怕Nazi看了也自愧不如!更可怕的是,才过了短短几十年,我们就对那段历史了解那么少了……

我们一提到大屠杀就想起南京,而对这场大屠杀却置若罔闻。国际ren quan委员会对大屠杀的定义是:“大屠杀是指在同一地点杀害五人或五人以上,并且受害人没有防卫能力。”按照这个定义,大屠杀在当时无处不在。

这让我想起了前几天看的电影《卢旺达饭店》,也是讲大屠杀。我很震惊,为什么人类都进入文明社会这么久了而大屠杀依然存在?我想原因可能是,每个人都有潜在的暴力倾向,正常的时候这种倾向一直被抑制着,而在某种条件下一旦被唤醒并利用起来就会造成大规模的无秩序,进而导致战争,大屠杀。而且我注意到,大屠杀都有一个共同点,制造者都要给被害者一个污蔑的称呼来达到其目的:卢旺达大屠杀中是“蟑螂”,文||革中是“右派”。一个简单的称呼就把朝夕相处的邻居划分为另类了!甚至就不是人了!瞧!人类依旧是那么愚蠢!!

“当心你的邻居!”这可以说是最恐怖的一句话!

所以说,永远都不要让憎恨和仇视存在人们之间,只有爱才能拯救这个可怜的世界!

关于sem_open(3)

今天新闻组上有人问到这么个问题,为什么sem_open(“/tmp/nimeni”,O_CREAT|O_EXCL,SHM_MODE,1);总是得到ENOENT?

翻开sem_open的man手册看看,我们大体就会知道,第一个参数有问题,man手册中这么说:

sem_open(3):

sem_open() creates a new POSIX semaphore or opens an existing
semaphore. The semaphore is identified by name. For details of
the construction of name, see sem_overview(7).

sem_overview(7):

Named semaphores
A named semaphore is identified by a name of the form
/somename. Two processes can operate on the same named
semaphore by passing the same name to sem_open(3).
我看了下面一个人的引用,发现我实在是没看man手册的必要了,这里的描述有问题!所以我找到了sem_open()在glibc中的实现(nptl/sem_open.c):
[c]
//….
if (mountpoint.dir == NULL)
{
__set_errno (ENOSYS);
return SEM_FAILED;
}

/ Construct the filename. /
while (name[0] == ‘/‘)
++name;

if (name[0] == ‘’)
{
/ The name “/“ is not supported. /
__set_errno (EINVAL);
return SEM_FAILED;
}
size_t namelen = strlen (name) + 1;

/ Create the name of the final file. /
finalname = (char *) alloca (mountpoint.dirlen + namelen);
mempcpy (mempcpy (finalname, mountpoint.dir, mountpoint.dirlen),
name, namelen);

/ If the semaphore object has to exist simply open it. /
if ((oflag & O_CREAT) == 0 || (oflag & O_EXCL) == 0)
{
try_again:
fd = __libc_open (finalname,
(oflag & ~(O_CREAT|O_ACCMODE)) | O_NOFOLLOW | O_RDWR);

//….
[/c]

所以这里的问题其实很简单,最根本的问题是那个path参数就究竟是用来干什么的?用一句话说,其实就是指定一个文件位置,这个位置是以tmpfs的挂载点为根目录的。也就是说,如果它以/开头,说明是从tmpfs根目录开始,而如果不是,就是从相对路径开始,不过“当前目录”也是根目录(这正是为什么sem_open()的实现中会去掉开头的/)。到了这里我们可以看出一开始那个问题的原因了,它传递的是”/tmp/nimeni”,也就假设了tmpfs的挂载目录(通常是/dev/shm)下有tmp这个目录,而事实上没有,所以即使他加上了O_CREAT|O_EXCL也会返回ENOENT。

所以,传递给sem_open()的第一个参数究竟怎么写才好呢?”/somename”和”somename”都可以,都容易读而且也都对,但是名字中间有/就不行了。

从上面我们也可以看出,对于程序员来说,有些时候啊,读文档真的还不如直接去读代码来得更快!所以这也提示我们:代码要写得像文档一样易读!我坚信好的代码就应该如此!

唉,情

唉,今天又是情人节,可是年年都和我无关。不过比较公平的是,情人节过不了我可以过光棍节。:-)

前几天和小林子聊天时聊起了我们这些兄弟们的感情问题,起因是老大的somebody,我们简单地总结了一下:

老大:上大学的时候就很受欢迎,身边经常是小姑娘成群,妹子一大把。毕业以后又以迅雷不及掩耳盗铃之势又泡了一个他所谓的somebody~!所以在兄弟当中,他是当之无愧的最幸福的一个!要不是小林子提示,我都没看出来,原来某人的文章中还暗示了过3年我们就该给他们的婚礼送红包了现在得先提前准备着~!

小林子:上大学的时候和王敏华同学好上了,记得当时传得满系风雨,我和小公子惊讶了好久……不过他们毕业后分了,但起码曾经有过,所以仍然算是不错的~~而且小林子现在所在的公司美女很多,找到下一个应该不是什么问题!

DP:比较痴情的一个娃,可以看他的博客。他曾经有过一段时候喜欢一个女孩,后来不知道怎么就分手了,反正不是人家DP的错,DP到现在还对她恋恋不舍。真是“爱过方知情重”啊!希望DP早日找到一个更合适的。

Kermit Mei:速度和老大有一拼,也是以“闪电战”搞定一个我们迄今为止还没见过的女生(小林子除外)。虽然工作还没搞定,但有个女朋友起码也是一种安慰,也算是不错!

金经理:这个貌似和我一样都是没谈过恋爱的,但是,据内部不愿透漏姓名的小林子同学透露,金主席(注意头衔的变化)当年是感情相当丰富滴,然后$%@#。而且,人家经理现在早已有目标了哦,就是他那个传说中的爱笑的眼睛~!希望经理可以早日搞定。

相比之下我是最惨的一个,三年没谈过恋爱,现在别说目标,就是连个人影都没看到呢!!虽然我一直喜欢折腾这无聊的生活,但说实话,偶尔有些时候还真是感觉挺孤单的,想找个说话的人都没有……唉,现在找女朋友也不是为了别的,就为了无聊的时候能找个人说说心里话。而这我看也很悬……

不抱怨了。祝上面提到的那些有情人们终成眷属!就像下面这个flash中的说的:“你叫声老拐,她叫你声老头子,那小日子过得是多么滋润呐,多么滋润!”

P.S. 很老的一个flash了,《爱情上甘岭》,一直比较喜欢。

Happy Birthday, UNIX!

% python -c ‘from time import strftime, localtime; print strftime(“%a, %d %b %Y %H:%M:%S”, localtime(1234567890))’
Sat, 14 Feb 2009 07:31:30

% date -d ‘@1234567890’
Sat Feb 14 07:31:30 CST 2009

% perl -e ‘print scalar localtime(1234567890),”n”; ‘
Sat Feb 14 07:31:30 2009

查找网络设备

在新闻组上看到有人问如何通过名字查找某个网络设备,其实不难,首先想到的就是通过ioctl,示例代码见下:

[c]
int get_interface(const char interface)
{
int sock = socket(PF_INET, SOCK_STREAM, 0);
struct ifconf ifc;
struct ifreq
ifr;
int ret = -1;

if (sock &lt; 0)
    return -1;

ifc.ifc_len = 0;
ifc.ifc_req = NULL;
if (ioctl(sock, SIOCGIFCONF, &amp;ifc)  0) {
    ifc.ifc_req = malloc(ifc.ifc_len);
    if (ifc.ifc_req) {
        if (ioctl(sock, SIOCGIFCONF, &amp;ifc) &lt; 0) {
            free(ifc.ifc_req);
            goto close;
        } else
            for (ifr = ifc.ifc_req;
                 (char *)ifr ifr_name);*/
                if (!strcmp(ifr-&gt;ifr_name, interface)) {
                    ret = 0;
                    break;
                }
            }
    } else
        return -1;

    free(ifc.ifc_req);
}

close:
close(sock);
return ret;
}
[/c]

然后有人说POSIX其实还有个if_nameindex(),它明显要比ioctl可移植性要高,用它重写上面的代码如下:
[c]
int get_interf(const char *interf)
{

int ret = -1;
struct if_nameindex *ifp, *ifpsave;
ifpsave = ifp = if_nameindex();

if (!ifp)
    return -1;

while (ifp-&gt;if_index) {
    if (strcmp(ifp-&gt;if_name, interf) == 0) {
        ret = 0;
        break;
    }
    ifp++;
}

if_freenameindex(ifpsave);
return ret;

}
[/c]

我查了一下glibc中的实现,发现在linux上其实如果不用netlink的话,if_nameindex()也就是对ioctl(…SIOCGIFCONF…)的一个包装,具体可参考sysdeps/unix/sysv/linux/if_index.c。netlink这个东西够奇怪的,在这里居然也能派上用场,有机会要研究研究~