Tchaikov’s Journal

July 30, 2006

Gmail 对浏览器的要求和 Ubuntu 的 Bug Report

Filed under: GNU/Linux

今天在寝室里想整理一下我的 Gmail 信箱,设置一下 tag …… 哦,label!不想,横竖找不到 New Label 的字样,看了 Gmail 的 Help它说在 More Actions 下拉菜单下面就有,但是我的菜单下面却没有。

仔细检查 Gmail 界面的时候,这才注意到。我的Gmail 提醒我:

For a better Gmail experience, use a fully supported browser. Learn more

原来我在使用的浏览器不是一个 fully supported browser!而浏览 Ubuntu 的Bug Reports页面却需要认证。看看也需要认证么?难怪会有人不爽。等过几天,有空了还是继续用俺的Debian

套用一个朋友的话,Debian 最高。嘻嘻,开玩笑。;)

后记:刚才找到了 Ubuntu Malone,这才是 Ubuntu 的 Bug Reports 中心,但是为什么 Ubuntu Packages 上的链接还没有更新呢?

July 26, 2006

IPython 和 Emacs (续)

Filed under: Emacs, Python

上次对 Liu Jin 的 ipython.el patch 的修改被 Fernando 接受了。最新的版本在这儿可以找到。

July 25, 2006

谁动了我的 Script-Fu

Filed under: GNU/Linux

为了画一个“hackergotchi”头像,动用了 GIMP。但是怎么都找不到 Wouter 提到的 Script-Fu->Shadow->Drop-Shadow 菜单。迫不得已,我们看 source!

从 Xtns->Plug-in Browser 查到 Drop-Shadow 是在 script-fu-drop-shadow 这个函数里实现的,但是这个函数是在哪里呢?装上文档,它说 Script-Fu 都装在 Folders->Script 指定的地方。我的机器上装在/usr/share/gimp/2.0/scripts/drop-shadow.scm。过去一看,原来是久违的 Scheme,没想到在这里又见到了老朋友。 程序里说得清楚:

(script-fu-menu-register \"script-fu-drop-shadow\"
	
			 “/Filters/Light and Shadow/Shadow”)
	

加上刚才在拜 Google 大神时查到的半吊子 Script-Fu 知识,知道这个是在被编辑图像的右键菜单。按图索骥,很顺利,找到了 Drop-Shadow 菜单。不禁“欧也”一把。

这是今天晚上的成果:me

July 24, 2006

CS:APP 你还记得多少?

Filed under: Programming

CS:APP是上个学期读的。花了一个学期的功夫看完了。但是我刚才发现我把它给忘了!

昨天在看巴别塔上的雇工上关于 GNU/Linux 系统上关于运行时链接的一篇How PLT Works。觉得挺对胃口,操起 Emacs 比划了两下。突然有似曾相识的感觉。咦?CS:APP!从书堆里翻出它一看,原来都在里面呢。唉……

不过在自己操练的时候,有一点要注意的。先记下来:在用 gdb 调试 shared object 的时候,为了让 gdb 能不出类似 “Error while mapping shared library sections: libtest.so: sucess” 的错,最好能:

export LD_LIBRARY_PATH=/path/to/so-files:$LD_LIBRARY_PATH

而且新版的 gcc 产生的代码和文中 Red Hat 所带 gcc 产生的代码还颇不同,主要体现在 foo():

(gdb) disass
Dump of assembler code for function foo:
0xa7fe3510 :     push   %ebp
0xa7fe3511 :     mov    %esp,%ebp
0xa7fe3513 :     push   %ebx
0xa7fe3514 :     call   0xa7fe3505 <__i686 .get_pc_thunk.bx>
0xa7fe3519 :     add    $0x1187,%ebx
0xa7fe351f :    sub    $0x14,%esp
0xa7fe3522 :    mov    0xfffffff0(%ebx),%eax
0xa7fe3528 :    mov    (%eax),%eax
0xa7fe352a :    mov    %eax,0x4(%esp)
0xa7fe352e :    lea    0xffffef0e(%ebx),%eax
0xa7fe3534 :    mov    %eax,(%esp)
0xa7fe3537 :    call   0xa7fe340c

0xa7fe353c :    add    $0x14,%esp
0xa7fe353f :    xor    %eax,%eax
0xa7fe3541 :    pop    %ebx
0xa7fe3542 :    pop    %ebp
0xa7fe3543 :    ret
0xa7fe3544 :    nop

__i686.get_pc_thunk.bx是干什么的?

(gdb) disass 0xa7fe3505
Dump of assembler code for function __i686.get_pc_thunk.bx:
0xa7fe3505 <__i686 .get_pc_thunk.bx+0>:  mov    (%esp),%ebx
0xa7fe3508 <__i686 .get_pc_thunk.bx+3>:  ret
0xa7fe3509 <__i686 .get_pc_thunk.bx+4>:  nop

原来上面的函数功能相当简单,就是利用call命令的副作用,把下一个指令的地址压栈,再从栈顶取出,放到%ebx里。接着加上 0x1187 的偏移量,算出 GOT 的位置。下面就是例行公事一般地给 printf 分配 0x14 的栈空间。再根据 xyz 的地址在 GOT 上的偏移量(-0xF)和"OK %d\n"字符串的地址在 GOT 上的偏移量(-0x10F1),分别算出 xyz 的值和格式化字符串的地址,先后压栈。调用 0xa7fe340c。细心的同学可能注意到了,printf@plt。说明 printf 也是在 PLT 上的。的确,printf 是 libc 的一员,a.out 动态地调用它,这样可以省一些存储空间,这和调用 foo 用的方法同出一辙。。

通过这个手段给 %ebx 赋值完毕以后,我们检查一下两个参数:

(gdb) x *($ebx+0xfffffff0)
0x8049710 :        0x00000004
(gdb) x/s $ebx+0xffffef0e
0xa7fe35ae <_fini +26>:   “OK %d\n”

在 PLT 的 foo 表项中,

(gdb) disass
Dump of assembler code for function foo@plt:
0x080483ac : jmp    *0x8049700
0x080483b2 : push   $0x10
0x080483b7 :        jmp    0x804837c <_init +24>
End of assembler dump.

和文中导致段错误的 disass 不同,在我这里根本就无法反汇编到 0x804837c 地址的代码:

(gdb) disass 0x804837c
No function contains specified address.

难免有遗珠之憾。

本以为不必专门写一个 foo() 函数的,只要 printf 两次就可以探究 GOT 和 PLT 的奥秘。因为 printf 本身就是在 PLT 上的。但是,当我第二次进入 printf 的时候发现,它进入的 printf 实现代码里,没有看到 PIC 函数(即 printf 自己)引用外部数据的代码(即__i686.get_pc_thunk.bx)。另外,在调用 printf 时不能一次加“/n”,另一次不加。因为 gcc 会把加上“\n”的 printf 翻译成 puts@plt,把不加的翻译成真正的 printf@plt,否则两次调用都会写 GOT 中的相应表项。

blog 总算恢复访问了

Filed under: Life, Tech

昨天发现我的blog突然不能访问了,提示说不能写 /var/tmp/ 下面的一个文件,像是 MySQL 出错了。一看,论坛上已经有很多人有相似的遭遇。症状和我的类似。

今天发现问题解决了!

July 14, 2006

hostname in Debian

Filed under: GNU/Linux

在装 GNU/Linux 的时候,安装程序都会让我们指定一个 hostname。但是有时却不能通过 hostname 访问远端的机器,这个时候绝大多数人就直接在/etc/hosts里添加一条记录了。
其实,这和 hostname serach path 有关。要把一个 hostname 翻译成对应的 IP,我们的 Linux 系统有三条途径:

  1. 查询 DNS 服务器,这和 DNS 客户端的配置有关
  2. /etc/hosts文件
  3. 问 NIS(Network Information System)服务器

你可以打电话给你的朋友,可以询问现场观众,也可以让电脑去掉一个错误答案。但是先用哪一条求助方式呢?/etc/host.conf告诉 Linux 应该使用哪些求助方式,以及使用它们的顺序。最常见的就像这样:

order hosts,bind
multi on

order这一行告诉系统应该先读 /etc/hosts文件,再查询 DNS 服务器。在这里 DNS 服务器被称为 bind 是因为,BIND(Berkeley Internet Name Daemon) 就是一种历史悠久的 DNS 服务器。如果要用 NIS,就在这一行里加上nis就行了。

multi on会让系统在 /etc/hosts里找到所有和所要查询的 host 相匹配的 IP。

如果你的 /etc/host.conf看起来没问题,但是没有办法 ping 到对方的 hostname。那么十有八九是对方的问题。

今天就碰到一个这样的问题,有同学新装了一台 Ubuntu。Ubuntu 在一个局域网里,局域网有自己的 DNS 服务器和 DHCP 服务器。从 Ubuntu 上可以 ping 别人的 hostname,但是没办法从别人的电脑 ping Ubuntu 的 hostname。注意到其他机器都是用的 DHCP。而 Ubuntu 的/etc/hosts没有问题。我联想到 DHCP 服务器有可能被配置成能动态更新 DNS 服务器的了。RFC2136规定了这个协议 DNS Update,因此只要支持 RFC2136 的 DNS 服务器,就能接受 DHCP 服务器发来动态更新信息。这样,只要有新的电脑加入网络,一旦 DHCP 给它分配了 IP 地址,就可以把新电脑指定的 hostname 更新到 DNS 那里去了。DHCP 服务器端的配置可以看 dhcpd.conf 的 manpage。这里主要说客户端的配置。在 Debian 里,客户端的配置一般是 /etc/dhclient.conf

send host-name \"hostname\";

这样,Debian 就会把自己的 hostname 交给 DHCP 服务器,由它转达给 DNS 服务器。其他电脑就能通过你的 hostname 访问你了。

刚才的案例是一个比较常见的例子,是 DHCP+DNS。如果没有较复杂的网络应用,一般场所不会使用 NIS 服务。NIS 是 Sun 开发的,它希望给用户提供一个网络透明的环境,让用户无论在局域网的那个机器上登录,都有“宾至如归”的感觉,从用户名、用户权限都有一样的设置。这个想法和 Windows NT 的 domain system 很相似。

July 9, 2006

为什么 Python 没有 /* */

Filed under: Python

熟悉一门计算机编程语言的朋友,在学习新的编程语言时,都会自觉或不自觉地希望在新的语言中找到原来那门语言中对应的元素。很自然地,学过 C 的同学,在看待 Python 时,也会这样做。

初看之下,Python 里缺少块注释(block comment)。Python 语言里用#来标志一行里,注释的开始。如果要多行注释?这简单,每一行都用“#”打头不就得了。

有的朋友就感到很困惑,有的朋友觉得这样很累,这几篇 post 已经有好几年历史了。但是,由它带出的一些思考在今天还是有它的意义的。thread 中的发言可以分作两派:

  • 一派就事论事,提供了一些不错的解决办法
  • 用“#”注释 code block 的每一行
  • 把 code block 放在if 0: ... 的缩进块里
  • ”“”或者'''的 triple quotes 把要注释的代码包起来。不过这种办法必须要注意 triple quotes 的缩进,必须服从上下文。另外,被注释的代码不能含有 triple quote(s)。
  • 因为任何像样的编辑器都应该有块注释的功能。就算它没有,也应该能够进行配置,使它能容易地帮助用户加入希望的注释标记。Emacs: C-c # 就是块注释,C-u C-c # 就是取消块注释
  • vim 也有很多办法,可以帮我们注释大块的代码。用 google 大仙能找到大把的方案。
  • 另一派则试图说服楼主,让他理解,每一行都用“#”自有它的好处,而块注释标记则有其让人难堪的时候
    • 如果使用“/**/”来注释大块的源程序,对于浏览代码的程序员来说,看到一行代码就无法快速的知道,这行代码到底是不是注释,只能让程序员“上下而求索”了。不过这个问题对于有良好的语法着色的编辑器来说是不成问题的。
    • 而且“/**/“没有办法包含另一个”/**/“ block。相信大家在写 C++程序的时候肯定碰到过很多这种情况吧。因为里面的“*/“会和外面的”/*“配对,使里面的“*/”之后的代码没有被注释起来。
    • 而且,对于各种没有语法分析功能的文本分析程序(比如 grep,简单的 perl 程序)来说,“/**/”的出现对他们统计和处理代码都带来了挑战。它们无法仅仅根据一行是不是“#”(对于C/C++是“//”)开头的,来判断这一行是不是注释。

    看完两边的发言。掩卷沉思。有两个结论:

    1. 嗯,C++ 引入“//”的确是有它的理由啊。
    2. 生子当如孙仲谋,写程序当用 Emacs/VIM。
    附带着说一句,Emacs 里 python-mode 的缩进可以用 C-c > 和 C-c < 来成块地控制。

    July 8, 2006

    让这一切过去

    Filed under: Life

    无疑,这几个月会是我生命中的一段低潮。骑着自行车孤独地在校园中游荡,回想着过去的一点一滴。觉得身心俱疲,没有激情、冲劲去做以往感兴趣的事情。
    如果不能结束这种状态,我想,我会在短时间内崩溃的。
    为了让自己稍微好一些,前两天重温了 The Shawshank Redemption,Andy Dufresne 在 1947 年被投入 Shawshank 监狱,在 1966 年脫狱成功。19年,这其间忍受的苦痛和非人待遇足以让我这脆弱的神经崩溃一百遍。但是故事的主人公没有被这击倒,他挺了过来。现在在听崔健的《无能的力量》专辑,又感觉到了几年前的我。
    或许我也行?

    July 5, 2006

    Python 的点点滴滴——?:

    Filed under: Python

    Python 可以用 [false_exp, true_exp][bool_exp] 的方式模拟 C 语言里的a?b:c三目操作符。对于那些不在乎没有被取值,但是也同样被 evaluate 的 {true|false}_exp 来说,是相当合适的。

    但是今天碰上了一件怪事,是在运行那个用 wxPython 写的演示程序时,Python2.3 说:

    return (val/2, [0,1][val > self.upper or val < self.lower])
    TypeError: list indices must be integers
    

    开始的时候打开 ipython (Python2.4)又试了一下,好的啊。再用 Python2.3 试,也没问题。晕菜了,把[0,1][val > self.upper or val < self.lower]改成[0,1][int(val > self.upper or val < self.lower)]。终于闭嘴了,清静了。不过还是不知道他为什么会这样。

    July 4, 2006

    Debian 上装 TinyOS

    Filed under: Work

    装的是 1.x 的 TinyOS。就说几点要紧的

    1.先装编译 AVR 所要的 tool chain

    aptitude install gcc-avr binutils-avr avr-libc 

    可能还附带着装上画图的 graphviz。

    2.从 sf 下 nesc

    wget http://kent.dl.sourceforge.net/sourceforge/nescc/nesc-1.2.7.tar.gz
    ./configure --prefix=/opt/tos; make; make install
    

    3.当然还有 sun-java5-jdk

    aptitude install sun-java5-jdk

    加上 SUN Java Communication API version 3.0

    unzip comm3.0_u1_linux.zip
    cp commapi/lib/*.so $JAVA_HOME/jre/lib/i386/
    cp commapi/jar/comm.jar $JAVA_HOME/jre/lib/ext/
    cp commapi/docs/javax.comm.properties $JAVA_HOME/jre/lib/
    

    4.最后从 CVS 抓 TinyOS

    cvs -d:pserver:anonymous@tinyos.cvs.sourceforge.net:/cvsroot/tinyos login
    cvs -z9 -d:pserver:anonymous@tinyos.cvs.sourceforge.net:/cvsroot/tinyos co tinyos-1.x
    

    4.1.确定 TOS 的安装目录之后,可以在 .bashrc 里加一个函数,以后想干活只要敲tos就可以了:

    tos()
    {
        export TOSROOT=/home/save/dev/tinyos/tinyos-1.x
        export TOSDIR=\"$TOSROOT/tos\"
        export PATH=$PATH:/opt/tos/bin
        export CLASSPATH=`$TOSROOT/tools/java/javapath`
        cd $TOSROOT
    }
    

    4.2.现在 ncc 从 nesc 移到了 TinyOS 里,现在编译 ncc:

    cd $TOSROOT/tools/src/ncc
    ./Bootstrap; ./configure --prefix=/opt/tos; make; make install
    

    看一下是不是所有东西都各就各位了:

    ./tools/scripts/toscheck

    可能 toscheck 认为有的版本不对,这会导致一些问题。在 nesc 生成的汇编程序里,会有许多变量名中间有$符号,gcc-avr 看到这个会不爽。make mica2的时候,会疯狂地报错,类似这样的:

    /tmp/cch3uzvj.s:1743: Error: unknown opcode `timertask'
    /tmp/cch3uzvj.s:1743: Error: unknown opcode `timertask'
    /tmp/cch3uzvj.s:1744: Error: unrecognized symbol type
    /tmp/cch3uzvj.s:1744: Error: unknown opcode `packetsent'
    /tmp/cch3uzvj.s:1745: Error: unknown opcode `cc1000radi'
    /tmp/cch3uzvj.s:1753: Error: unknown opcode `txbufptr'
    

    这位朋友把这看作不听话的后果,其实在$TOSROOT/apps/Makeruls里改改就行:

    -OPTFLAGS := -Os
    +OPTFLAGS := -Os -Wa,--allow-dollars 

    让 gcc-avr 看到刀乐不要慌。

    4.3.编译 uisp

    用 Debian 自带的 uisp 烧写程序时会报错,说:

    /usr/bin/uisp -dprog=mib510 -dserial=/dev/ttyUSB0 -dpart=ATmega128 --wr_fuse_e=ff  --erase --upload  if=build/mica2/main.srec
    Direct Parallel Access not defined.
    

    得使 TinyOS 自己的。

    cd $TOSROOT/tools/src/uisp
    ./set-dates
    ./configure --prefix=/opt/tos && make install
    

    4.4.编译 libgetenv

    有的 TinyOS 的 Java 程序会用到 JNI 的 libgetenv。因此需要……编译:

    cd $TOSROOT/tools/java/jni make
    

    在 make 之前,还需要修改一个脚本$TOSROOT/tools/scripts/locate-jre。把从37行到47行的程序都注释掉,紧接着加上:

    j=\"$JAVA_HOME\"/bin/javac
    

    当然,前提是你设置好了$JAVA_HOME的环境变量。在修改后的第70行,改之:

    -dir=\"$jhome/jre/bin
    +dir=\"$jhome/jre/lib/i386
    

    这些问题是没有使用 IBM JDK,以及 Debian 没有安装 rpm db 造成的。

    5.现在设置一下编程板使用的口,以及 mica2 使用的频段和功率等

    写一个文件 $TOSROOT/apps/Makelocal

    DEFAULT_PROGRAM=mib510 # 编程板用的型号
    MIB510=/dev/ttyUSB0   # USB to serial 在 GNU/Linux 里就是这样的
    DEFAULT_LOCAL_GROUP=0x88 #  group id
    CFLAGS = -DCC1K_DEFAULT_FREQ=CC1K_433_002_MHZ # 无线通信使用的频段
    CFLAGS += -DRADIO_XMIT_POWER=0xFF # 用最大的功率
    

    6.看看硬件是否正常

    cd $TOSROOT/apps/MicaHWVerify make mica2 

    如果一切正常的话,说明 TinyOS 差不多装好了。之所以说“差不多”,是因为 TinyOS 还附带了相当多的工具,和 Java 库,这些东西我们都还没有编译过呢。接下来,把编译好的程序烧到板子上:

    make reinstall mica2 

    接下来,跟着Tutorial检查 通信等功能。 到现在,就差不多完成了 TinyOS 在 Debian GNU/Linux 平台上的安装。另外,如果要用 deluge 的话,需要改一下 deluge 的 Makefile:

    sed -i -e \"s/DELUGE_PLATFORM=telosb/DELUGE_PLATFORM=mica2/\" \
           -e \"s/CC2420Radio/CC1000Radio/\" $TOSROOT/tools/java/net/tinyos/deluge/Makefile
    

    6.最后有一点,是使用 USB to serial 转接线造成的。

    6.1.让 Java 程序认识/dev/ttyUSB0

    要修改$JAVA_HOME/jre/lib/javax.comm.properties文件,加一行:

    serpath2 = /dev/ttyUSB0

    6.2.让 Java 程序设置波特率时不会退出

    这只是一个 workaround,就是在 $TOSROOT/tools/java目录下 grep 所有的 java 文件,找出setSerialPortParams的地方,改成这样的:

    try {
          serialPort.setSerialPortParams(sf.BAUD_RATE, SerialPort.DATABITS_8,
                                         SerialPort.STOPBITS_1,
                                         SerialPort.PARITY_NONE);
    } catch (Exception e) {}
    serialPort.setSerialPortParams (sf.BAUD_RATE,
                                    SerialPort.DATABITS_8,
                                    SerialPort.STOPBITS_1,
                                    SerialPort.PARITY_NONE);	
    	

    就是让它执行两次,第一次一定会出错:

    Exception in thread \"AWT-EventQueue-0\" java.io.IOException: Not all params are supported by kernel
            at com.sun.comm.LinuxSerial.nativeSetSerialPortParams(Native Method)
            at com.sun.comm.LinuxSerial.setSerialPortParams(LinuxSerial.java:346)
            at SerialConnection.setConnectionParameters(Unknown Source)
            at SerialConnection.openConnection(Unknown Source)
            at SerialDemo.actionPerformed(Unknown Source)
            at java.awt.Button.processActionEvent(Button.java:388)
            at java.awt.Button.processEvent(Button.java:356)
            at java.awt.Component.dispatchEventImpl(Component.java:3955)
            at java.awt.Component.dispatchEvent(Component.java:3803)
            at java.awt.EventQueue.dispatchEvent(EventQueue.java:463)
            at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:242)
            at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:163)
            at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:157)
            at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:149)
            at java.awt.EventDispatchThread.run(EventDispatchThread.java:110)
    

    但是这一定不是内核的问题,因为C程序对串口的设置波特率操作是可以的。在这里也有人碰到相同的问题。我在这里用的是第二种办法。

    Get free blog up and running in minutes with Blogsome
    Theme designed by Jay of onefinejay.com