Hacking 《自己动手写操作系统》Chapter 1&2

——Writing x86 PC Bootloader With Free Software

本文内容已整理成一本电子书,请到这里下载

今天在看一本书,《自己动手写操作系统》(于渊,电子工业出版社),虽然很欢迎这样一本详尽介绍怎样写操作系统的书出现,但看完前两章后对作者的某些做法很不以为然。比如使用 Windows 作为开发平台,采用商业虚拟机作为测试平台,不是每个人都买得起这些软件的(我们要在心中牢固树立使用盗版软件就是犯罪的观念 X-D)。

我这篇文章的目的就是为了展示 Linux/Free Software 的强大,不使用任何商业软件,不用自己写的任何工具,使用免费的工具链,照样可以完成而且更高效地完成《自己动手写操作系统》前两章的 demo。

当然我也希望这篇文章为推动 Linux 和 Free Software 的发展出一点力,比如让部分读者(尤其是 EE 和 CS 学生)看完这篇文章后舍弃瘟都死,投身到 Linux 和 Free Software 阵营中来,或者《自己动手写操作系统》的作者在下一版(如果有的话)中完全使用 Free Software 来做 demo。

好了,下面开始,先介绍一下需要使用的工具。
操作系统:Ubuntu 7.04 Feisty,平台: i386 PC。
使用工具:gcc, binutils(as, ld, objcopy), dd, make, hexdump, vim, virtualbox。
上面所说的工具中,除了 virtualbox 虚拟机,剩下的工具在任何能用做开发环境的 Linux 版本上都是默认安装的。VirtualBox 也是遵从 GPL 协议的开源软件,可以从这里 [href: http://www.virtualbox.org ]下载。Ubuntu 只需要 sudo apt-get install virtualbox 安装即可。

首先,我们看第一个示例代码:
[wbyang@solrex-PC loader]$ more boot.asm
org 07c00h ; 告诉编译器程序加载到7c00处
mov ax, cs
mov ds, ax
mov es, ax
call DispStr ; 调用显示字符串例程
jmp $ ; 无限循环
DispStr:
mov ax, BootMessage
mov bp, ax ; ES:BP = 串地址
mov cx, 16 ; CX = 串长度
mov ax, 01301h ; AH = 13, AL = 01h
mov bx, 000ch ; 页号为0(BH = 0) 黑底红字(BL = 0Ch,高亮)
mov dl, 0
int 10h ; 10h 号中断
ret
BootMessage: db "Hello, OS world!"
times 510-($-$$) db 0 ; 填充剩下的空间,使生成的二进制代码恰好为512字节
dw 0xaa55 ; 结束标志
此段代码使用 Intel 格式的汇编语言写成,本也是用同样开源的 NASM 编译,但是鉴于很少有人在 Linux 下使用此汇编语法,它在 Linux 平台上的扩展性和可调试性都不好(GCC 不兼容),而且不是采用 Linux 平台上编译习惯,所以我把它改成了使用 GNU tool chain 去编译连接。这样的话,对以后使用 GNU Toolchain 编写其它体系结构的 bootloader 也有帮助,毕竟 NASM 没有 GAS 用户多。

上面的汇编文件可以用 AT&T 风格改写为:
[wbyang@solrex-PC loader]$ more boot.S
.code16 ;使用16位模式汇编(GAS 默认认为 .S 文件是 pure 32-bits i386 code)
.text ;代码段开始(为 link script 做定位)
mov %cs,%ax
mov %ax,%ds
mov %ax,%es
call DispStr ;调用显示字符串例程
INF: jmp INF ;无限循环(GAS 没有 $ 作为当前行标号的约定)
DispStr:
mov $BootMessage, %ax
mov %ax,%bp ; ES:BP = 串地址
mov $16,%cx ; CX = 串长度
mov $0x1301,%ax ; AH = 13, AL = 01h
mov $0x00c,%bx ; 页号为0(BH = 0) 黑底红字(BL = 0Ch,高亮)
mov $0,%dl
int $0x10 ; 10h 号中断
ret
BootMessage:.ascii "Hello, OS world!"
.org 510 ; 填充到 510 字节,使生成的二进制代码恰好为512字节
.word 0xaa55 ; 结束标志

但有一个问题, NASM 可以直接使用 nasm boot.asm -o boot.bin 将 boot.asm 编译成 bin 文件,GAS 不能。但是 GAS 的不能恰好给开发者一个机会去分步地实现这个过程,使编译更为灵活。下面请看 link script 和 Makefile:
[wbyang@solrex-PC loader]$ more solrex_x86.ld
SECTIONS
{
. = 0x7c00;
.text :
{
_ftext = .; //告诉链接器程序加载到内存的7c00处
} = 0
}

这个连接脚本的功能就是,在 link 的时候,将程序加载到内存 0x7c00 的位置(BOIS 将 PC 控制权转交给这个位置运行的程序),相当于 boot.asm 中的 org 07c00h 一句。有人可能觉得麻烦,还需要用一个脚本控制加载地址,但是 《自己动手写操作系统》就给了一个很好的反例:Chapter 1.5 代码 1-2,作者切换调试和运行模式时候需要对代码进行注释,而使用脚本控制,只需要编译时候调用不同脚本进行连接,就能解决这个问题。

这在嵌入式编程中是非常常见的处理方式,使用不同的连接脚本一次 make 生成某个程序分别运行在 board 上和 simulator 上的两个二进制文件 。

相信只要能耐心看到这里的人对下面 Makefile 中的内容都不会陌生。
[wbyang@solrex-PC loader]$ more Makefile
CC=gcc
LD=ld
LDFILE=solrex_x86.ld #使用上面提供的连接脚本 solrex_x86.ld
OBJCOPY=objcopy

all: boot.img

boot.img: boot.bin
@dd if=/dev/zero of=emptydisk.img bs=512 count=2880 #生成空白软盘镜像文件
@dd if=boot.bin of=boot.img bs=512 count=1 #用 bin file 生成对应的镜像文件
@dd if=emptydisk.img of=boot.img skip=1 seek=1 bs=512 count=2879 #在 bin 生成的镜像文件后补上空白,最后成为合适大小的软盘镜像

boot.bin: boot.elf
@$(OBJCOPY) -R .pdr -R .comment -R.note -S -O binary boot.elf boot.bin

boot.elf: boot.o
$(LD) boot.o -o boot.elf -e c -Tsolrex_x86.ld

boot.o: boot.S
$(CC) -c boot.S

clean:
@rm -rf boot.o boot.elf boot.bin boot.img

使用 Makefile,一个是方便,直接就可以生成可引导的软盘镜像文件,二是为了清楚,其间对源文件的任何处理都一清二楚。下面解释一下 Makefile 都做了什么:
第一步, gcc 调用 as 将 boot.S 编译成目标文件 boot.o;
第二步, ld 调用连接脚本 solrex_x86.ld 将 boot.o 连接成可执行文件 boot.elf。
第三步, objcopy 移除 boot.elf 中没有用的 section(.pdf,.comment,.note),strip 掉所有符号信息,输出为二进制文件 boot.bin。
第四步, dd 使用 boot.bin 生成可引导的软盘镜像 boot.img。

到此,我们使用开源工具链编译生成 bootloader 的过程已经结束,没有使用到任何商业软件,也没有自己写任何转换工具。《自己动手写操作系统》文中提到的 HD-COPY 和 Floopy Writer 都没有使用到。 为了验证也可以先用 hexdump -x -n 512 boot.img 将 boot.img 前 512 个字节打印出来,可以看到 boot.img dump 的内容和附送光盘中的 TINIX.IMG dump 的内容完全相同。这里我们也显然用不到 EditPlus 或者 UltraEdit,即使需要修改二进制码,也可以使用 vim 的 %!xxd 命令进行十六进制编辑。

写得真够累的,那个 virtualbox 就不说了,用法跟 virtualPC 没有什么大差别。安装上以后,新建一个虚拟机,加载 boot.img 光盘镜像到软驱,然后选择虚拟机重新启动,就可以看到红色的:
Hello, OS world!
当然,你如果喜欢的话,可以改成 "F-U_C-K Microsoft!" 不过要注意的一点是,做这件事是需要权限的,要把当前用户加到 vboxusers 组中,否则这个行为无法成功 :-)。

综上,很显然 VirtualPC 也不需要。《自己动手写操作系统》可怜的作者费了半天劲在瘟都死的 VirtualPC 里装 Redhat Linux,解决瘟都死和 Linux 的文件共享问题,解释了一番自己“工欲善其事,必先利其器”的动机,但我实在没看出有一点儿需要瘟都死的原因。瘟都死什么都不能给你,但 Linux 能给你整个世界!

Tor - Anonymity on Line

其实以前就见过 Baosheng 在博客里推荐 Tor 这个工具,据说可以访问 Wikipedia,只是当时潜意识里觉得太麻烦,就懒得搞。昨天晚上 Baosheng bg,请 ufx222, xum84, daoming, proline 我们一起吃饭,又提到了这个洋葱路由,今天就尝试了一下,果然好用。

简单来说,Tor 的功能就是,采用不同的“洋葱路由器(onion router 意思和代理服务器差不多)”来重定向网络请求,并且随时间更换路由,这样别人就无法监视你的网络访问情况,主要目的是为了保护隐私。而由此带来的副产品就是,onion routers 遍布全世界各地,所以就可以使用这个工具通过 onion routers 访问被“墙”封掉的站点(比如现在的 blogspot.com )。更多介绍请登录 Tor 官方网站 [href: http://tor.eff.org/ ] 。

从我的使用来看,刚开始使用访问网站速度可能慢点儿,用了一段时间之后速度就快很多了,大概是 Tor 自动在寻找洋葱路由服务器。而且最牛的是,我们公司的 DNS 坏了一小段时间,其间什么网站都没办法访问,而通过 Tor 居然访问正常,大概是 Tor 记录的是 IP 而不是 domain 吧。有意思的是访问 google.com 往往被重定向到某个欧洲的 Google 服务器,比如: google.de, google.es 之类的。

在 Linux 下 Tor 的配置很简单(我估计在 Windows 下会更简单,官方网站上有各种系统安装介绍):

首先,安装 Tor 软件,在 Ubuntu 下面就是:
sudo apt-get install tor
使用默认配置即可,即在 9050 端口打开一个 SOCKS 服务器。Tor 程序会以后台方式一直运行在系统里。

其次,为 Firefox 下载 torbutton 插件[href: https://addons.mozilla.org/en-US/firefox/addon/2275 ],安装。安装完成后,到"Tools->Add-ons->Extensions->Torbutton Preferences",在 "Proxy Settings" 中选择 "Use custom proxy settings",然后将前四个都留空,最后 "SOCKS Host:" 中填: localhost, Port: 9050,这样配置就完成了。

可以留意 Firefox 状态栏的最右端(窗口右下角),这时候会出来一个 "Tor Disabled",用鼠标点击该区域,就可以在 "Tor Disabled/Tor Enabled" 状态切换。这样在访问某些敏感站点时候就可以 Enable 它,在不需要时 Disable 它。也可以在 Torbutton Preferences中把这个文字格式的提醒改成一个洋葱头的图标提醒。

其实我想,不安装插件,只在网络设置中使用 SOCKS 代理也可以实现这种功能,如果这样的话,这里插件的作用只是启用和不启用代理的区别。那么,如果在校园网内,这个插件能否起到在 Firefox 的代理和插件这两个代理之间切换的目的呢?如果可以的话,就算不使用或者不能使用 Tor,这个插件也可以用作切换校园网代理使用了,倒也蛮方便的(只是我不需要用到了,所以就留给别人验证吧 X-D)。

PS: Yo2.cn 服务质量在下降,从 2007年7月19日8:25 到 9:00 之间都无法连接上,wget 可以下载但无法显示速度,更无法登录,最后出来一个错误:504 Gateway Time-out。唉,国产的 BSP 千万别学国产的足球队啊!烦死了,回去睡觉。

类成员函数作为 pthread_create 函数参数

花了三个工作日把原来写的一段通信守护进程代码从过程方法改到了 template class,对于 template 的使用和类的派生明白了不少道理。还有个很受启发的一点,就是 C++ 中如何使用类的成员函数作为创建线程的开始函数。
pthread_create 是 POSIX 标准下创建线程的函数,函数原型是:
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void*(*start_routine)(void *), void *arg);

在 C 中,这个函数使用很简单,只要定义一个参数和返回值均为 void * 类型的函数,使用函数名字作为参数即可。就算不完全符合,可以使用 (void *(*)(void *)) 将其强制转换为符合类型检查规格的函数指针。但是,类的非静态成员函数隐含 this 指针作为第一个参数,所以参数完全不可能转化为 void * 类型,而 C++ 的类型检查要比 C 严格许多。由于我原来写的代码是 C 风格的,自然不会出现类型不符的问题,现在将线程开始函数封装到一个模板类中,再创建线程的时候就不能满足需要了。

在试了几种转换无效之后,从网上搜到一种方法:定义线程开始函数为类的静态成员函数(static member function),这样就不隐含 this 指针了,然后将 this 指针从 pthread_create 最后一个参数传给开始函数,在函数中将 void * 类型的 this 指针强制转换为类指针。看来静态成员函数还是有些妙用的。

===================我叫分割线===================

带着我妈和我妹在北京城里转悠了几天,累得不行。主要原因在我,没有考虑到身体因素,连着玩消耗太大,再加上自己也没车没房,倒公交车和住宾馆也要走很远,费时间又费劲。我都快受不了了,别说我妈了。以后再出去旅游,坚决不会再连着转三天以上,要么紧紧张张地玩两天,要么就花时间长点儿,走走歇歇。唉,谁让咱是穷人呢,又有钱又有闲的日子还没过上呢!

今天看了下积压很久的博客订阅,同学里开始写和继续写的人越来越多了。觉得有些文章比较阴沉低迷,因为自己从大三开始心情就老是跌宕起伏,反而在面对这许多次的分别聚首时坦然一些。要不是周熹在散伙饭时候专门招我,也不会哭得那么厉害。散了散了散了吧,没有离别,怎么会有重逢呢?

还是在 Yourui 的博客上看到小恪去新疆的消息,要是当面看到他,肯定会玩笑说发配三千里伊犁充军去了。这在边关待了两年之后,再回来学积分拓扑之类的数学还能看得下去吗?都说是命运无常,旅途坎坷却能看更美风景,只是不知道那关外还是不是大漠孤烟长河落日的大西北?

刚才去吃饭,走在晚间暖暖的懒洋洋的空气中,忽然有点儿秋天的感觉。想起 7 年前爸爸送我到商丘一高上学的情景,日子可真快啊!老了,老了!

又回到北京了

好些天没有更新博客了(针对我的更新频率而言),原因很简单,有更重要的事情去做 :-)。

1 号到的北京,把寄放在同学那里的被子拖到租住的公寓,请邓飞、丽君吃了顿饭。2 号到公司报到,换了个办公桌,换了个电话。寄来的包裹和录取通知书同时到达,纳闷不已,为什么北京往南京挂号需要七天而南京挂号到北京只需要三天?害得我为了一张纸折腾(中科院的录取通知书着实简陋,信封是牛皮小信封,通知书就是一张请柬大小)。我原以为是通知书的那玩意儿是入学须知,但搞不明白的是,为什么入学须知要寄给我两次?

一毕业,这同学们好久不更新的博客都重新拾起来了,反而显得我有点儿懒。这下到公司工作,晚上的时间就多了些,可能又要恢复我正常的更新速度了。

这两天工作也没干什么事,FPGA 调试还不太熟练,模拟器又改了不少,而且更新中工作不太正常,所以就随便看看代码、看看邮件列表和一些 patch。GDB 的 mailing list 里这两天挺有意思的,有个人发了一个希望把 GDB 代码用 C++ 重写的邮件,然后一群大佬说这个问题别讨论了,各持己见讨论起来会没个头的。但是某个邮件里有巨牛的一句话:“The more C++ code I see, the more convinced I get that the language should die. ”让我叹服不已,哈哈。

重装了一个 Ubuntu,当然也装上了 beryl,比原来用着更方便了。Google desktop for linux 推出了,试用了一下,挺好的,特别喜欢它的两下 ctrl 就可以弹出搜索框,这样就免得我还得用鼠标把光标定位在 Google toolbar 里去。Google desktop 的 sidebar 没有在 Linux 版本上体现出来比较遗憾,那些小工具很有意思,但其实 Linux 本身就带有很多有一些的小玩意儿,喜欢玩的能把桌面配置得很 cool 或者 cute。为 Firefox 装了一个 IE7 的主题,看起来还是蛮好的,看来 Microsoft 的 UI 工程师也不是吃干饭的。但是 wine 现在还不支持 IE7,用 ies4linux 测试版装上之后,IE7 的 UI 效果一点儿也没有,真没劲。

毕业了,大家都在干不同的事,还有人(XHO)感叹没假期了,哈哈,准备成家立业的时候已经开始了,慢慢就该习惯了。

从中科大音乐站抓MP3的一个脚本

由于南京大学的 COS 音乐 FTP 被关闭了,原来很喜欢 COS 按歌手和专辑分门别类的风格,现在就不知道到哪里去下歌了。今天发现了一个好的音乐网站:中国科学技术大学音乐站,[href: http://music2.ustc.edu.cn ],按歌手和专辑分类,在教育网内速度算是相当快的,可以在线听。我就动了心思,想把专辑自动下下来。

中科大音乐站是这样的,你先选择歌手、专辑和歌曲,点击"播放"会下载下来一个 music@ustc.m3u 文件,使用播放软件打开就可以在线播放选中的歌曲。其 music@ustc.m3u 文件中每首歌的条目如下例:

#EXTM3U 双节棍
http://music2.ustc.edu.cn:8088/6e6bbc6495aec1f5fc719dcbc826ab4c%2F64%2Ff05523485d63a9c422393215c676f194.mp3

可以看出,中科大音乐站在存储 mp3 音乐文件的时候采取了某种文件名加密方法,很难直接猜出文件名,而且在提交 form 的时候采取 post 方式,不知道它的 action 文件接受什么样的参数,所以想在这一步上减少工作量是很难的。而且从网站上拿下来这个列表并不算很麻烦,因为每个人基本只会对某些专辑感兴趣嘛。那么下面要做的工作就是自动处理这个列表,将歌曲下载下来存在应该的位置和保存为正确的文件名。

Linux Bash Shell 脚本如下:

for album in $(ls *.m3u)
do
# Test the encoding of music list file.
  encode=$(file -i $album | awk ’{print $3}’ | sed -n ’s/charset=//p’)
if [ $encode != "utf-8" ]
then
  iconv -f gbk -t utf8 $album > ${album}.1
  mv ${album}.1 $album
fi
# Test if the album already exists.
if [ ! -d ${album%.*} ]
then
  mkdir -p ${album%.*}
fi
# Get the url list.
urls=(`grep -v "#" $album`)
# Download the song.
N=0
for song in $(grep "#" $album | sed -n ’s/#EXTM3U //p’ | sed ’s/ /_/g’)
do
  echo "Processing $song..."
  if [ ! -f ${album%.*}/${song}.mp3 ]
  then
    wget -O ${album%.*}/${song}.mp3 ${urls[$N]}
  fi
N=`expr $N + 1`
done
done

这个脚本所做的工作是:
1.找到当前目录下所有的以 m3u 为后缀名的专辑播放列表文件,循环处理它们。
2.检查专辑列表文件的编码方式,如果不是 utf-8 格式编码,将文件编码转换成 utf-8(为了处理歌曲文件名需要)。
3.去掉专辑列表文件后缀名 .m3u,以此为名字建立专辑目录。
4.得到专辑中歌曲的 url 列表。
5.下载歌曲到专辑目录中,并以对应的文件名命名,歌曲文件名中的空格被转化为下划线。

使用方法,先手动下载希望下载的每个专辑的播放列表(.m3u 文件),并将其文件名改为对应的专辑名,放到与脚本同一目录下,运行脚本,就会将每个专辑中的歌曲下载到以专辑名命名的目录下。

函数调用栈和栈帧

在计算机科学中,Callstack 是指存放某个程序的正在运行的函数的信息的栈。Call stack 由 stack frames 组成,每个 stack frame 对应于一个未完成运行的函数。

在当今流行的计算机体系架构中,大部分计算机的参数传递,局部变量的分配和释放都是通过操纵程序栈来实现的。栈用来传递函数参数,存储返回值信息,保存寄存器以供恢复调用前处理机状态。每次调用一个函数,都要为该次调用的函数实例分配栈空间。为单个函数分配的那部分栈空间就叫做 stack frame,也就是说,stack frame 这个说法主要是为了描述函数调用关系的。

Stack frame 组织方式的重要性和作用体现在两个方面:第一,它使调用者和被调用者达成某种约定。这个约定定义了函数调用时函数参数的传递方式,函数返回值的返回方式,寄存器如何在调用者和被调用者之间进行共享;第二,它定义了被调用者如何使用它自己的 stack frame 来完成局部变量的存储和使用。


|------------------|<--high address
|     ......       | Previous
|     ......       |  frames
|------------------|<-----------
| registers and    |
| local variables  |
|------------------|  Caller's
|   argument 6     |
|   argument 5     |  frame
|   argument 4     |
|   argument 3     |
|   argument 2     |
|   argument 1     |
|------------------|<--previous sp
|  return address  |(n-4)sp
|------------------|(n-8)sp
| saved registers  |
|     ......       | Current
|------------------|  frame
| local  variables |(Callee's
|     ......       |  frame )
|------------------|
|   next calling   |
|  arguments list  |
|------------------|<--sp
|     ......       |
|__________________|<--low address

图片链接:http://solrex.googlepages.com/frame.jpg

上图描述的是一种典型的(MIPS O32)嵌入式芯片的 stack frame 组织方式。在这张图中,计算机的栈空间采用的是向下增长的方式,SP(stack pointer) 就是当前函数的栈指针,它指向的是栈底的位置。Current Frame 所示即为当前函数(被调用者)的 frame ,Caller’s Frame 是当前函数的调用者的 frame 。每个 frame 中所存放的内容和存放顺序,则由目标体系架构的调用约定(calling convention)定义。如图所示,MIPS O32调用约定规定了所占空间不大于4 个字节的参数应该放在从 $4到 $8 的寄存器中,剩下的参数应该依次放到调用者 stack frame 的参数域中,并且在参数域中需要为前四个参数保留栈空间;如果被调用者需要使用 $16 到 $23 这些保留寄存器(saved register),就必须先将这些保留寄存器的值保存在被调用者 stack frame 的保留寄存器域中,当被调用者返回时恢复这些寄存器值;当被调用者不是叶子函数时,即被调用者中存在对其它函数的调用,需要将 RA(return address) 寄存器 ($31) 值保存到被调用者 stack frame 的返回值域中;被调用者所需要使用的局部变量,应保存在被调用者 stack frame 的本地变量域中。

在没有 BP(base pointer) 寄存器的目标架构中,进入一个函数时需要将当前栈指针向下移动 n 字节,这个大小为 n 字节的存储空间就是此函数的 stack frame 的存储区域。此后栈指针便不再移动,只能在函数返回时再将栈指针加上这个偏移量恢复栈现场。由于不能随便移动栈指针,所以寄存器压栈和出栈都必须指定偏移量,这与 x86 架构的计算机对栈的使用方式有着明显的不同。

在 RISC 计算机中主要参与计算的是寄存器,saved registers 就是指在进入一个函数后,如果某个保存原函数信息的寄存器会在当前函数中被使用,就应该将此寄存器保存到堆栈上,当函数返回时恢复此寄存器值。而且由于 RISC 计算机大部分采用定长指令或者定变长指令,一般指令长度不会超过32个位。而现代计算机的内存地址范围已经扩展到 32 位,这样在一条指令里就不足以包含有效的内存地址,所以RISC计算机一般借助于一个返回地址寄存器 RA(return address) 来实现函数的返回。几乎在每个函数调用中都会使用到这个寄存器,所以在很多情况下 RA 寄存器会被保存在堆栈上以避免被后面的函数调用修改,当函数需要返回时,从堆栈上取回 RA 然后跳转。移动 SP 和保存寄存器的动作一般处在函数的开头,叫做 function prologue;恢复这些寄存器状态的动作一般放在函数的最后,叫做 function epilogue。

SVN you should know

SVN 是 Subversion 的缩写,一个开源的版本管理工具,类似于 CVS 和 Visual Studio 的 Source Safe。不过据它的作者称是因为忍受不了 CVS 的缺点所写的,可能会比 CVS 表现好一点,因为我只有在 check out 某些开源代码时候才会用到 CVS,并不经常,所以只是猜测。不过肯定是比 Source Safe 表现要好,因为两者的理念是不同的,Visual Source Safe 管理小型项目还可以,是不适合管理多用户的大项目的,再说,Source Safe 也只能在 VS 里面用,而 SVN 的客户端有 Linux 和 Window 的版本。顺便提一下的是,WIN 下有种最流行的 SVN 客户端就是集成在右键菜单的一个小乌龟,很有意思的。

SVN 有非常好的使用文档,相比其它的开源项目来说,SVN 的中文化也做的很好,有专门的中文站,可以搜一下。SVN book 介绍的相当全面,但是,读完那么长的文档也没有必要。我这里就介绍一些常用且是最常用到的命令(Linux 下,Win 下就不说了,都是 GUI 操作)。

建立版本库:svnadmin create /data/svnpool
这是指定版本库存放的位置,貌似必须是一个绝对路径,建立好以后里面会建立起一些配置和数据库的文件。

指定编辑器:export EDITOR=vim
因为每次的版本操作都要添加一些信息在 log 里,把 EDITOR 放在环境变量里可以使用自己熟悉的编辑器来编辑这些信息。这句话经常放在 .bashrc 里。

导入项目:svn import file:///data/svnpool
在当前目录下的所有目录和文件都会导入到版本库里。

列表查询:svn list file:///data/svnpool
一般情况下都要先查询一下某个目录存在不存在才去导出。

导出项目:svn co file:///data/svnpool/project
这时候会在当前目录下建立一个 project 的目录,里面存有 SVN 的信息。

更新项目:svn update
使当前目录下的所有文件和目录版本与版本库中保持一致。

查看不同:svn diff
查看当前目录下的所有文件和目录与版本库中有那些不同,也可以加上版本参数 -r revision1:revision2 查看两个版本有什么不同。

添加文件和目录到当前项目:svn add subdir/file
把当前目录下的 subdir/file 添加到当前目录对应的项目中去。

删除、移动或复制当前项目中的目录和文件: svn rm/mv/cp ...
把某个文件或目录更改名字、复制或从版本库中删除。

提交更改:svn commit
把更改后的文件提交到版本库中形成新的版本。

在初始导出和导入的时候,可能需要给出 URL,主要有几种格式,file:///, http://, svn://, svn+ssh://user@host。当项目已经导出时候,就不需要打路径名了,有可能需要密码,如果验证的话。

这些是最常用的命令,像其它的一些命令,比如 merge 之类的,用到时候再查 svn book 和 man page 也不晚。版本控制很有用的,比如自己平常写的代码,放到版本库里就比较好查询和撤销修改,而不用每次都保存个备份。而且增量修改时当代码发生错误时候可以很快定位更改在哪里,找到错误原因。其实所有文档都可以放在 SVN 库里的,只是某些文件类型不好做 diff 罢了。

为 GDB 添加新的目标机 (3)

本文遵从 GNU Free Documentation License (see http://www.gnu.org/copyleft/fdl.html ),并特别对 冒充另类(TeaWater) 的《移植GDB》一文表示感谢 (see http://teawater.spaces.live.com )。由于大体和细节的东西在《GDB Internal》和《移植 GDB》两文中已经有了很多描述,所以本文的目的在于 Howto,step by step 地描述如何为 GDB 添加新的目标机,并对某些以上两文中阐释不清的地方做出自己的解释。

3. taget dependent 文件补充说明
其实关于 solrex-tdep.h, 和 solrex-tdep.c 文件中所涉及的函数, TeaWater 的《移植 GDB》一文讲得非常清晰,我也没必要重复,我只对一些他没提及的东西做一下解释。

(1) solrex-tdep.h 中应该放些什么

其实这个主要是根据需要,如果一些东西会被别的文件使用,那就放在这里,如果没有,这个文件甚至不需要。当然,一些应该放在头文件中的东西还是放这里比较好。

(2)关于 struct gdbarch_tdep

这个结构体是用户定义的,最重要的使用也是在这两个文件中。可以根据需要增减,一般的话需要几个:registers map,重要的 register number,然后就是 abi 的东西。只要用户觉得某个东西有用,就可以放在这个结构体里面。

(3)关于 pseudo registers

这个东西主要看编译器的实现,如果编译器就根本没有这些东西,没有必要处理这些东西。

(4)关于 unwind_pc

不要被它的名字迷惑,unwind_pc 所起到的作用原理很简单,返回一个位于(参数 next_frame 的上一层 frame)中的地址,一般情况下就是这个函数的返回地址。因为函数的返回地址一般就是对这个函数调用的汇编指令的下一个指令,GDB 内部会把这个返回地址再减去一个指令长度,这样就回到了函数调用那句话,函数调用肯定位于被调用的函数的上一个 frame 中。但是有两种情况你需要考虑,如果 next_frame 是一个 sentiniel frame, 只需要返回当前 pc 的位置,如果是一个 normal frame,就需要返回这个 frame 的函数返回地址。这个具体怎么做需要看特定的 target,比如 mips 很简单,只是把 pc 给它,但不是所有的 target 都是这样的。只需要看一下汇编代码就知道该怎么做了,一般情况下进入一个函数,首先是把移动 sp, 给这个函数留出一段栈地址,然后是把 ra 压栈。但是有时候也不完全这样,假如 ra 不是 GPR,不能直接存入栈中,只能先放到一个 GPR 中然后再把那个 GPR 压栈。

之所以要讲到上面的例子就是因为,GDB 的逻辑是这样的:告诉他要 unwind 哪个 register,它会先调用注册的 frame_prev_register 函数去找这个 register,很多情况下 target 都是先注册 dwarf2 的 dwarf2_frame_prev_register。这个函数会根据 debugging information 中的 FDE 和 CIE 去找这个 register 是放到哪里了,然后去那里找到这个 register 的值。但是这个函数有个缺陷就是,假如把一个 register 放入另一个 register 中,它只会到下一层的 frame 中去找那个 register,这样在上段说的最后一个例子,就会出现错误,因为被存入的 register 只是做一个中间值的用处,它会把自己压到栈中,而 dwarf2_frame_prev_register 到下一层的 frame 中去找它的值,显然不可能得到正确的结果。

(5)关于注册特定的 frame_unwind_append_sniffer 有没有用

在很多情况下,尤其是在 linux 的编译器都会为 ELF 文件加入 DWARF2 格式的 debugging information,所以很多情况下用户注册的 frame_sniffer 都是没有太大用处的,但是如果不注册也会发生问题。因为 GDB 中 DWALF2 的处理是先找 symbol 里对应于这个 pc, 有没有 FDE,如果有 FDE 就采用 dwarf2 的 frame sniffer,如果没有就采用用户另外注册的 frame sniffer。如果用户没有另外注册 frame sniffer 的时候,GDB 会出现 internal error,而当注册了以后,GDB 内部会在二进制文件中找这个 pc,如果找不到,会返回用户不知道现在位于什么位置。所以,当二进制文件中有 debugging information 时候,再注册特别的 frame sniffer 只是为了不出现 internal error。因为当 pc 正确时候,自然有 dwarf2 处理,当 pc 不正确的时候,GDB 也不会使用用户定义的 frame sniffer。用户定义的 frame sniffer 唯一有用的时候就是二进制文件中没有 debugging information,这样 GDB 会调用用户定义的 frame sniffer 来找 frame 的开始位置,栈分布和 register 的处理。

(6) gdbarch 中那么多东西,应该注册多少

我觉得,至少应该告诉 GDB 的是: info.byte_order, tdep, num_regs, sp_regnum, pc_regnum, stab_reg_to_regnum, dwarf2_reg_to_regnum, register_name, register_type, skip_prologue, inner_than, unwind_pc, call_dummy_location, unwind_dummy_id, push_dummy_call, print_registers_info, print_insn,然后再加上注册一下 frame_unwind_append_sniffer 和 frame_base_append_sniffer 就行了,剩下的就要看各个 target 的特性,需要不需要添加了。

唉,写了以后才发现自己没多少可写的,因为每个函数的用处 TeaWater 的《移植 GDB》中都讲到了,而要是真 step by step 地写,工作量也太大了点,算了,就这样吧,其实很多东西是只有到用的时候才知道哪里会出现问题的。不过用 GDB debug GDB 也是一件很有趣的事情。

为 GDB 添加新的目标机 (2)

本文遵从 GNU Free Documentation License (see http://www.gnu.org/copyleft/fdl.html ),并特别对 冒充另类(TeaWater) 的《移植GDB》一文表示感谢 (see http://teawater.spaces.live.com )。由于大体和细节的东西在《GDB Internal》和《移植 GDB》两文中已经有了很多描述,所以本文的目的在于 Howto,step by step 地描述如何为 GDB 添加新的目标机,并对某些以上两文中阐释不清的地方做出自己的解释。

2.对所需添加进 gdb 目录的文件的功能介绍

这部分和 《GDB Internal》中的 9.12 Adding a New Target 一部分是重合的,不过我添加一些实例和自己的介绍。

’gdb/config/solrex/solrex.mt’:这个文件里的东西会添加到 Makefile 里面,很简单,给个例子:

$ cat gdb/config/solrex/solrex.mt
TDEPFILES= solrex-tdep.o
DEPRECATED_TM_FILE= tm-solrex.h

第一个就是要编译 solrex 的 target 需要的文件,solrex-tdep.o 是下面要讲的 solrex-tdep.c 的目标文件,还可以加一个 TDEPLIBS= ,意思是需要的 lib。 DEPRECATED_TM_FILE= tm-solrex.h 的意思这种做法已经 deprecated 了,但是我们仍然可以使用,是 target 所需要的头文件,下面会讲到这个文件,但是这个文件如果没有的话可以不要。

’gdb/config/solrex/tm-solrex.h’:这个文件里可以定义一些通用的宏,给个例子:

$ cat gdb/config/solrex/tm-solrex.h
#define GDBINIT_FILENAME ".solrex-gdbinit"
#define DEFAULT_PROMPT "(gdb-solrex) "

第一个是 GDB 启动时候的默认读取文件名,第二个就是 GDB 的缺省 prompt。这个头文件会变成编译时候的 tm.h 文件,这些宏定义会覆盖 GDB 的本身设定,当然,如果你不需要这些宏完全可以不要此文件。

’gdb/solrex-tdep.h’ && ’gdb/solrex-tdep.c’:这两个文件是最重要的文件,也是 GDB 能够识别 solrex-elf 这个 target 的根据,这两个文件中定义了很多 solrex arch 特定的东西,比如:target 是 big-endian 还是 little-endian 的?target 的 registers map;target 的字长,register 的类型,返回值的存放,等等等等,这些后面的文章都会仔细介绍,也是主要介绍的部分。

Adding a New Target to GDB (1)

为 GDB 添加新的目标机 (1)

工作基本结束,留下些文字,希望能对别人有些用处。本文遵从 GNU Free Documentation License (see http://www.gnu.org/copyleft/fdl.html ),并特别对 冒充另类(TeaWater) 的《移植GDB》一文表示感谢 (see http://teawater.spaces.live.com/ )。由于大体和细节的东西在《GDB Internal》和《移植 GDB》一文中已经有了很多描述,所以本文的目的在于 Howto,step by step 地描述如何为 GDB 添加新的目标机,并对某些以上两文中阐释不清的地方做出自己的解释。

为 GDB 添加新的目标机的意思就是让 GDB 支持一个新的 CPU 体系架构。这点从 GDB 的编译选项中可以看出来,在./configure 命令后有一个 --target= 的选项,这个等号后面就是 GDB 调试的二进制文件应该在什么目标机上运行,可以是: mips-elf,arm-elf 等等,在 config.sub 文件中你可以看到这些选项是如何工作的。

下面我们假设要添加的新 target 为 solrex-elf。

1.如何配置编译环境

当你为一个 GDB 添加 target solrex-elf 的时候,至少需要让 GDB 在 configure 为 solrex-elf 时候能够编译通过,为此你需要添加和修改一些文件。

GDB 其实由很多部分共同组成,GDB 源文件目录下有几个目录,bfd 目录下是 LIB BFD (Binary File Descriptor Library),opcodes 目录下是 opcode library,这两个 library 都是跟随 binutils 分发的; libiberty 目录下是 libiberty library,它给许多 GNU 软件提供一些通用的子程序,好像是跟随 gcc 分发的;readline 目录下是 readline library,提供一些命令行功能,比如<tab>补全,它好像是独立分发的;最关键的 gdb 目录,这个下面才是 GDB 的主体。

是想让 GDB 编译通过,不能只修改 gdb 的东西。要调试程序,必须要有先编译出来二进制代码,所以对 BFD 和 opcodes 的修改应该是最早的,binutils 依赖这些库来编译出来二进制工具,比如ar, as, ld, objdump, readelf 等,然后 gcc 利用这些二进制工具把它编译出来的汇编码最终组合成目标文件和可执行文件。

但是,在某些阶段,比如编译器尚未完善之际,可以先 cheating GDB,就是拿来一个已有 target 的 BFD lib 和 opcode lib,修改成自己的 target。

下面是要编译 target=solrex-elf 需要修改的文件:

Key source files:
gdb
* gdb/config/solrex/tm-solrex.h: New file.
* gdb/solrex-tdep.h: New file.
* gdb/solrex-tdep.c: New file.
bfd
* include/elf/solrex.h: New file.
* bfd/cpu-solrex.c: New file.
* bfd/elf32-solrex.c: New file.
* bfd/archures.c: New arch.
* bfd/bfd-in2.h: New arch.
* bfd/targets.c: New target.
opcodes
* opcodes/solrex-dis.c: New file.
* include/dis-asm.h (print_insn_solrex): New function.
* opcodes/disassemble.c (ARCH_solrex): New arch.
Config files:
gdb
* gdb/config/solrex/solrex.mt: New file.
* gdb/Makefile.in (solrex-tdep*): New target.
* gdb/configure.host (solrex*, solrex*-*-elf*): New target.
* gdb/configure.tgt (solrex*, solrex*-*-elf*): New target.
bfd
* bfd/config.bfd: New target.
* bfd/configure.in: New target.
* bfd/Makefile.am: New target.
opcodes
* opcodes/configure.in (bfd_solrex_arch): New target.
* opcodes/Makefile.am (solrex-dis*): New target.
general
* configure.in (solrex*-*-elf*): New target.
* config.sub (solrex, solrex*-*): New target.
* readline/support/config.sub (solrex, solrex*-*): New target.
Auto Generated files:
* configure (solrex*-*-elf*): New.
* bfd/configure: New.
* bfd/Makefile.in: New.
* bfd/doc/bfd.info: New.
* opcodes/configure (bfd_solrex_arch): New.
* opcodes/Makefile.in (solrex-dis*): New.

上面列出的是想成功编译出 solrex-elf target 需要修改的最少的文件。至于如何修改 config 和 makefile 文件,只需要查看一下某个特定的 target,比如:mips,或者简单点的 xtensa,就知道该怎样改了。

在 bfd 和 opcodes 里的文件不是这篇文章中讲的重点,想 cheat GDB 最简单的办法就是把另外一个 target 的名字,比如:mips, xtensa 全部换成 solrex 就行了,使用另一种 target 的 binary file descriptor 会影响 GDB 的某些功能而不是全部。

Boy, what is your future?

最近好累,没完没了的调试,对GDB做了几遍 checking test,好多 fail 。只能 一个一个去找,却发现基本上是在给 compiler 做测试,基本都是因为 debugging information 的不完全还有 compiler 的优化。因为都是底层问题,只能 dump 出 来 assembly code 和 debugging information,一点一点的去找,看得我视力急剧下降。

工作是基本熟悉了,只是发现自己的很多问题又浮现出来。在保送研究生之前的大学生活大部是一段焦虑期,那种对未来的不可知和眼界的狭小让自己很惶恐。保完研,顺利找到实习以后基本上算进入了一个安静期,如果不算情感上波动的话。很欣欣自得于自己的状况,学业暂时无忧,经济暂时独立,工作也算理想,于是乎毫无念想去考虑其它。

现在应该是大四下学期了,前日系里开班会,我也没打听消息,无外乎是毕业论文和学期安排,大概还有毕业委员会的事情吧。这个世界离了谁都转,我这个小年级长在不在也没所谓。只是想到毕业论文,才觉得自己浪费的时间不少了。上个学期曾兴致冲冲地去找吴朝阳老师要求跟他作毕业论文,老师也给我指了两个方向。可惜的是,我把论文看得太轻了,只顾花费时间在一些别的事情上,告诉自己,还有时间,还有时间。

其实现在来说,我也不担心论文通不过的问题,本科论文也不会有太大意思。我是在怀疑自己有没有研究能力,独立思考的能力,到底适合不适合做科研。我丝毫不怀疑自己的行动力,但是我知道自己有很大缺点在意志力和创造力上。从小到大的人云亦云,从小到大的好学生,我对自己所学过的东西一片茫然,对自己要做的事情也无甚了解,我实在不知道自己选择学术这条路是对还是错。读研,我能读出来什么吗?

我不知道自己这种对能力的怀疑是暂时的还是永久的。不过我想这种情况也应该不只在我身上出现,地质大师许靖华在自己的自传中也写到在刚到美国开始自己的学术生涯时候也是不知所措,谁知道未来是怎样呢?我只是觉得自己对数学的兴趣不大,对计算机的兴趣也是一般,严格的来说,我没觉得什么学科对我来说有很大吸引力。高中的时候,倒是挺喜欢生物的,现在来说也了了了。一个对学术没有极大热情的人去从事它,会变得喜欢上吗?

其实说到了,可能还是功利心太强了,但是,这个世界允许我没有功利心吗?我没有那个条件去淡泊名利。

Scim comes back

哈哈,我的 Ubuntu(feisty)能输入中文字了,虽然 Kmail 里还是不能显示所有中文字,但编码是没有问题了的。不能用诡异,能用也诡异,我什么都没
做,就过了几天登录ubuntu,不小心点到了ctrl+space,惊喜地发现scim弹出来了,开心开心。

我要搬到Ubuntu下工作了,要享受beryl带来的快感了。

How To Add New Features To DejaGnu Testing Framework

如何为 DejaGnu 添加新特性

DejaGnu 是一个非常好的开源的测试工具,尤其是做 GNU 开源工具链的软件开发,更离不开 DejaGnu 的支持,因为 binutils, gcc, gdb 自带的测试用例都是使用 DejaGnu 进行测试的。但是,DejaGnu 也有一些不足之处。因为它是开源的,我们可以修改它来满足我们的需求。

DejaGnu is a very good open-source testing framework, expecially in GNU development tool-chain developing. Because binutils, gcc, gdb use DejaGnu to test themselves. However, DejaGnu can not cover all aspects. Luckily it is open-sourced, so we can modify it to meet our demand.

我曾经想把下面代码提交到 DejaGnu,但是很遗憾,它的维护者似乎并不希望添加这些特性。所以我只好写在这里,希望能对某些人有所帮助。

I have tried to commit the fellowing code to DejaGnu, but unfortunately its maintainers did not want these features. So I write them down, in the hope that them will be useful.

1. DejaGnu 的官方介绍
1. Introduction From DejaGNU’s website

DejaGnu is a framework for testing other programs. Its purpose is to provide a single front end for all tests. Think of it as a custom library of Tcl procedures crafted to support writing a test harness. A test harness is the testing infrastructure that is created to support a specific program or tool. Each program can have multiple testsuites, all supported by a single test harness. DejaGnu is written in Expect, which in turn uses Tcl -- Tool command language.

2. 怎样给 runtest 命令添加新的选项
2. How to add new options to runtest command

runtest 有很多选项,可以通过 runtest --help 查看。下面是向 runtest 添加两个选项的代码,"--pr"是带优先权的测试,通过在 site.exp 或者 unix.exp 等其它设置文件中添加 pre_test_list 变量(list类型)来实现优先测试 pre_test_list 中的测试用例;"--sm"是 smoking test,意思是可以在抽一支烟的时间内测完的用例列表,也是通过在设置文件中添加 smoking_test_list 变量(list类型)来实现。

"runtest" command has many options, you can use command "runtest --help" to get more information. The following code adds two options to runtest. "--pr" is for test with priority on some testcases, via setting a list variable "pre_test_list" in configure files like site.exp and unix.exp. "--sm" is for smoking test, set a list variable "smoking_test_list" in the same place as --pr.

在 dejagnu/runtest.exp 中,添加下面的代码:
Adds the following code to dejagnu/runtest.exp:

/* 在最后一个 switch -glob -- $option 的 swith 体中,添加:*/
/* Find the last "switch -glob -- $option", adds the fellowing to it’s switch body:*/
# Options handler.
"--pr*" { # (--priority) run preferential tests first, then others
if {[info exists pre_test_list]} {
set SOLREX_testlist $pre_test_list
# Flag to control priority and smoking test.
# 0: run before whole test
# 1: run only listed test
set SOLREX_flag 0
verbose "Running preferential tests $SOLREX_testlist"
continue
} else {
warning "Variable pre_test_list is null. runtest as default."
continue
}
}

"--sm*" { # (--smoking) run smoking tests
if {[info exists smoking_test_list]} {
if { $smoking_test_list != ""} {
set SOLREX_testlist $smoking_test_list
set SOLREX_flag 1
verbose "Running smoking tests $SOLREX_testlist"
continue
} else {
warning "Variable smoking_test_list is null."
exit 0
}
} else {
warning "Variable smoking_test_list is not defined. "
exit 1
}
}

/* 在最后一个 set testlist 的循环语句后,添加下面代码: */
/* Find the last "set testlist " statement, jump out of the foreach loop, adds: */
# Exe code for --pr and --sm.
# If SOLREX_testlist is defined, run these *.exp files listed in it first.
# code in foreach loop bellow is copied from above.
if {[info exists SOLREX_testlist] } {
foreach x $SOLREX_testlist {
verbose "trying to glob ${srcdir}/${x}" 2
set s [glob -nocomplain ${srcdir}/$x]
if { $s != "" } {
lappend testlist $s
} else {
set f [exec find $srcdir -name "$x"]
set f [split $f "
"]
set f [lindex $f 0]
set s [glob -nocomplain ${srcdir}/$f]
if { $s!= "" } {
lappend testlist $s
}
}
}
if { $SOLREX_flag == 0 } {
foreach test_name $testlist {
if { ${ignoretests} != "" } {
if { 0 <= [lsearch ${ignoretests} [file tail ${test_name}]]} {
continue
}
}
set subdir [file dirname $test_name]
set p [expr {[string length $srcdir] - 1}]
while {0 < $p && [string index $srcdir $p] == "/"} {
incr p -1
}
if {[string range $subdir 0 $p] == $srcdir} {
set subdir [string range $subdir [expr {$p + 1}] end]
regsub "^/" $subdir "" subdir
}
set runtests [list [file tail $test_name] ""]
runtest $test_name
lappend ignoretests [file tail $test_name]
}
set testlist ""
}
}

/* 在 usage 函数中,添加下面两句:*/
/* Find "proc usage", adds following two lines in it: */
# Help information.
send_user " --priority (-pr) Run tests in ’pre_test_list’ first and orderly, defined in ’site.exp’
"
send_user " --smoking (-sm) Run tests in ’smoking_test_list’ only, defined in ’site.exp’
"

3. 怎样给 DejaGnu 添加新的开发板描述文件
3. How to Add Board Description File to DejaGnu

DejaGnu 在baseboards目录下内建了很多开发板描述文件,例如:arm-sim.exp, mips-sim.exp,可以在这个目录下新建或修改原有文件来为你的开发板配置一个正确的环境。

DejaGnu have many board description files in directory dejagnu/baseboards, i.e. arm-sim.exp, mips-sim.exp. You can add or modify file to get a right configuration for your board.

开发板描述文件示例:
Example board description file :

$cat dejagnu/baseboards/gdbserver-sample.exp
......ignored headers......
# gdbserver running over ssh.
/* 加载 tool-and-target-specific interface 配置文件,这个文件应该在当前测试环境的 config 目录下,可以在它里重写很多库函数。*/
/* Load tool-and-target-specific interface file, it should be in your testhome’s config dir. You can overwrite library functions in this file. */
load_generic_config "gdbserver"

/* 下面都是很明了的了,但是需要说明的一点是 board_info 并不是一个 struct, 可以自定义很多内容。比如 GDB 就定义了许多变量,例如:gdb_prompt, mathlib 等等。 你也可以为自己定义一些变量,只需要在这里 set_board_info xxxx yyyy, 当你要使用时,先测试它是否存在:[target_info exists xxxx],然后取得它的值:set YYYY [target_info xxxx]。*/
/* The following is self-explained. But maybe you are glad to know that "board_info" is not a struct, you can define variables in it. For example, GDB defines many variables, i.e. gdb_prompt, mathlib. Of course you can define youself some variables for configuration use. Put "set_board_info xxxx yyyy" here first. When you want to use xxxx, you test if it exits: "[target_info exists xxxx]", then get xxxx’s value: "set YYYY [target_info xxxx]" */

process_multilib_options ""

# The default compiler for this target.
set_board_info compiler "[find_gcc]"

#set_board_info compiler "/opt/src/gcc/install-30/bin/gcc"
#set_board_info c++compiler "/opt/src/gcc/install-30/bin/g++"

set_board_info rsh_prog /usr/bin/ssh
set_board_info rcp_prog /usr/bin/scp
set_board_info protocol standard
set_board_info hostname voltaire.debian.org
set_board_info username dan
set_board_info gdb_server_prog /home/dan/gdb/mv/obj/gdb/gdbserver/gdbserver

# We will be using the standard GDB remote protocol
set_board_info gdb_protocol "remote"

# Path to the gdbserver executable, if required.
set_board_info gdb_server_prog "../gdbserver/gdbserver"

# Name of the computer whose socket will be used, if required.
set_board_info sockethost "voltaire:"

# Port ID to use for socket connection
# set_board_info gdb,socketport "4004"

# Use techniques appropriate to a stub
set_board_info use_gdb_stub 1

# This gdbserver can only run a process once per session.
set_board_info gdb,do_reload_on_run 1

# There’s no support for argument-passing (yet).
set_board_info noargs 1

# Can’t do input (or output) in the current gdbserver.
set_board_info gdb,noinferiorio 1

# Can’t do hardware watchpoints, in general
set_board_info gdb,no_hardware_watchpoints 1

Beryl is so cool

以前看到 SUSE 10.1 带的 beryl 效果,觉得非常有意思,但肯定会吃资源,就没想过尝试。自从那天看到同事的桌面也用了 beryl 之后,我才知道原来集成显卡也是可以安装滴。只可惜我的 SUSE 是 10.0,Qt 库的版本不够,只能望屏兴叹。昨天晚上下好了 Ubuntu 7.04, 叫什么 herd-3 测试版还是什么东西,我也记不住这种东西。心想,安上这个就可以玩 beryl 了。

测试版真的是很讨厌,我的 SATA 硬盘,WinC 盘是 FAT32 的,为什么把光盘镜像放在这个分区上就找不到呢?我差点想去改 initrd.gz 里面的寻找路径,还真从那里面让我找到了,但头痛的是,我不知道我改了后再压缩会不会识别不了。心想,算了,说不定是分区的问题,失败两次之后就学乖巧了些,拷贝到 ext3 格式的 linux 分区一个镜像,结果再找镜像就 OK 了。

但是安装时候还出了一个问题,我选择语言是中文,国家和地区是中国,它就是会停到安装 openoffice-java-commen 这个包上,真是奇怪了。只好选择语言英文,但是,但是,为什么我选择语言是英文不能选择国家和地区是中国呢?这是什么逻辑?没的办法只好选择香港。

安装好以后,彻底 upgrade 一番,这没出问题。但在安装中文支持的时候,最后一步说不能识别 zh locale。我郁闷了,一看,下载下来的中文支持包是 6.06 版本的,怪不得,那么久没有更新了。所以,现在我的 feisty 能正常显示中文了但不能输入,scim 能启动但就是没法换输入法,唉,忍了吧,谁让咱是中国人呢?中国博大精深的文字那些鬼佬们怎么能轻易而举地使用呢?

然后就是安 beryl,看网上教程看到头痛,丫的安一个软件这么费劲。我一怒,不管了,就把 ubuntu 的 beryl org 的源加到 source.list 里面,然后 wget 一下 key,直接 sudo apt-get update, sudo apt-get install beryl。我紧张地看着进程跑完,哈哈哈哈,居然没出问题,直接就能用。看来 Ubuntu 在这方面还是下了点功夫的。

我最喜欢 beryl 的就是桌面的转换,cool 啊,而且使用快捷键比原来 1234 桌面换来换去直接地多,也能在脑子里形成反射:恩,去左面的桌面,恩,去右面的桌面。而不用像以前一样老在想,该进几号桌面啊?

版上还有人评价 windows vista 和 beryl 达到同等效果所耗费的内存。虽然两个操作系统这么比没意思,但还是觉得 beryl 做的相当不错。

使用自由软件构建交叉编译、汇编和连接工具, Cross-build gcc for mips.

唉,好郁闷,做的东西自己都不懂多少。我 GDB 用的都不熟,还要给 GDB 添加新的 target。自定义 elf 文件的 BFD support, target dependent, disassembly function, 好几千行的代码啊,弄不好得上万行,GDB 那么多文件,看得我头都晕了。

因为我们的芯片和 MIPS 关系比较密切,就先编了个 MIPS 的交叉编译和调试环境玩玩。

Concepts:

交叉编译,就是在一个平台上生成另一个平台上的可执行代码。这里的平台包含两个概念:Architecture 和 OS,x86 Linux平台是Intel x86 arch 和Linux for x86 OS 的统称,Sparc SunOS 4.x 就是 Sparc arch 和 SunOS 4.x(or Solaris 1.x) 的统称。生成交叉编译器后就可以在主机上生成目标机的代码,就比如说显然低端手机上不可能装一个操作系统,更不用说编译器了,但手机上仍然需要安装软件才能提供服务。就需要使用交叉编译器,在 PC 上编译好程序以后将二进制文件装载到手机的内存中去(有可能直接烧进去,或者使用 bootloader 加载)。这个时候,PC 就是主机,手机就是目标机。这个程序在主机上是不可能直接运行的,因为它的代码指令是目标机的指令,很可能和主机的指令集完全不兼容(不然要交叉编译器干吗?)。

调试环境,程序编译成可执行文件之后肯定需要调试,但是代码在主机上无法运行,如何调试?常用的有几种办法:1.把二进制文件加载到目标机上,打印出调试变量。2.把二进制文件加载到目标机上,通过串口或者网口向管理程序交流,实现在线调试。3.把二进制文件放到主机的模拟器软件上运行,进行调试。其实还有一些其它的方法,比如 ICE(in-circuit emulator),但是代价比较高。这几种方法一般都是混合使用的,很难一种就完全满足要求,就连 Nokia 出的手机模拟器也是漏洞百出的,必须得在板子上运行之后才知道代码有没有错误。

GDB 是支持远程调试的,可以通过 serial port, tcp, udp 和 pipe 与目标机进行交流,使用的语言叫做 RSP(Remote Serial Protocol),同样这需要目标机使用同样的语言和它交流,这样就需要一个翻译。这个目标机上的翻译程序就是 stub(裸机和简单操作系统) 或者 gdbserver(可以运行gdb的操作系统)。但是光有这些还不行,因为 GDB 必须知道目标机的结构(寄存器,字长等等),二进制文件的格式(elf,pe,寄存器代号),操作指令才可以实现远程调试,不然它怎么知道寄存器名对应的是哪个?怎么加载函数的符号表来设置断点?怎么反汇编地址内容?唉,我现在要做的就是把这些添加到 GDB 中去,然后把 GDB 和模拟器用 RSP 连接起来。其实我们的模拟器就支持直接调试的,但是客户不一定喜欢字符界面啊,要做 IDE 的话,而且以后还要支持在线调试,还是用 GDB 方便,毕竟 GDB 可以和很多 IDE 集成。

Practice:

我们的编译器是改的 openCC,我也不懂。这里编的是GCC,它已经支持了很多平台,arm, mips, sparc, powerpc...,通用性更强些。

编译工具需要好几个部分,这就有个编译顺序问题,应该先编什么?显然应该从最底层编起,顺序大致应该是:

binutils, gcc-cross, library, gdb.

其实 GDB 无所谓了,放在什么时候编都一样。但 binutils 必须放在 gcc-cross 前面编(如果不能使用主机上的as, ar, ld, nm 这些二进制工具的话),而 lib 也得看需要不需要 gcc-cross,如果直接使用主机上的编译器编译的话,就放在前面编,如果不能使用,就放后面编。glibc 太大了,很多时候会使用其它的 lib 代替,比如 uclibc, newlib 等等,特别是目标机操作系统有要求时。

编译过程都不难,无非是 wget,tar,configure,make,make install。但是有很多情况下过程不会那么顺利,尤其当不了解整个工具链的要求时,碰到编译不过最好找个懂的人问问,是缺少 lib 啊,还是配置问题。

我用的版本都比较新,binutils-2.17,gcc-core-4.1.0,newlib-1.15.0,gdb-6.6,最重要的就是 configure 啦,随便写一下,编译最好在新建目录里。

/src/binutils-2.17/configure --prefix=/program --target=mips-elf
/src/gcc-core-4.1.0/configure --prefix=/program --target=mips-elf --with-newlib --enable-languages=c
/src/newlib-1.15.0/configure --prefix=/program --target=mips-elf
/src/gdb-6.6/configure --prefix=/program --target=mips-elf

要注意的是,如果编译 gcc-cross 时候加 --with-newlib 的话,要到主机编译器能找到的地方,比如对应的 lib 目录下建两个软连接,分别指向解压后的 newlib 根目录下的 newlib 和 libgloss,而且必须先编译出 gcc for mips-elf 后再编译 newlib for mips-elf。编译成功的 gcc 安装到 prefix/bin 目录后,会加上 mips-elf- 前缀。最好不要使用 --program-prefix 选项,这样会造成后续编译自动寻找工具的麻烦。最好将 prefix/bin export 到 PATH 里去。

其它还好,最头痛的是 gcc,时间很长,用 core 也得编10多分钟,还是在服务器上,配置也繁,比如 target 选 mips-linux-gnu 就是死活编不过去,选 xtensa-elf 也是编不过去,肯定是少什么或者设置不对,但就是不知道少的是什么,还是没有经验啊。要是再加上操作系统的东西,编译 gcc 前面还要先编 kernel headers, glibc headers,gcc 可能需要编两遍,还有 glibc, kernel,更麻烦。反正我现在至少不用费那个劲,现成的编译器已经有了,只是需要 map 一些东西到 gdb 而已。

GDB 自带 mips, arm 这些程序的模拟器,不知道功能怎么样,但至少可以用。但是编译时候的参数还有程序中函数入口等等实在是太烦了,写和调试嵌入式系统的代码那叫一痛苦。

Copyright © 2005-2007 Solrex Yang. All rights reserved.

又得到支持啦,耶

Ben Elliston (Maintainer of DejaGnu) 这个老顽固,我好说歹说就是不愿意采纳我的建议。最后没辄了给我发了封邮件(注意:没有 cc 到 mailing list):
DejaGnu already has too many knobs that can be turned. I am reluctant to add more when the environment already provides ways for users to customise its behaviour (and, after ~15 years of DejaGnu development, you’re the first peron to request this feature).

嘿嘿,人家都 reluctant 了,我就别难为人家了。谁知道今天又有一支持者,是我们亲爱的 IBM 德国公司的一位先生,开心死啦,看着倍儿亲,这下 3:1 了,看你怎么办,Mr Hard! 哼哼!

==============小样,我分割你们(this line copyright (c) You Xu)===============

哈哈,发现一个有意思的网站,有道,它的博客搜索狂有意思,在博客搜索结果里可以找到博客档案,评论相当搞笑啊!^_^

比如我的评论是:

伟大的企业家杰克•韦尔奇在书中提到,要注意工作与生活的平衡,只有这样员工才能发挥更高的效率。如果你是博主的领导,请不要因此苛责他(她)。如果你是博主的同事或者朋友,请不要转给他(她)的领导看——毕竟,杰克•韦尔奇只有一个。一句话,博主很能写!现在就把博主的博客文章全部集结起来,联系出版社准备发行《祥林嫂后传》吧。人称每周一歌的就是博主了,其实适当的距离反而更有新鲜感。

还有更多的,习惯发文时刻,习惯发文星期,发表文章长度统计,可以点这里查看。感兴趣的就过去看看吧。

PS:

郁闷,用一下博客搜索,居然发现有人抄我文章的,连里面出现的 solrex 都没改,也没标明出处,敢情我 copyright 白写啦!! 算了算了,也没心思计较那么多,提醒一下得了。
Copyright © 2005-2006 Solrex Yang. All rights reserved.

First Mails to GNU project(DejaGnu) Maillist

A suggestion on DejaGnu testing framework. Today’s Topics:

1. Why not add tests order control to Dejagnu, I find a way.
(wenbo.yang@simplnano.com)
2. Re: Why not add tests order control to Dejagnu, I find a way.
(Stefan Reinauer)
3. Re: Why not add tests order control to Dejagnu, I find a way.
(wenbo.yang@simplnano.com)

----------------------------------------------------------------------

Message: 1
Date: Thu, 14 Dec 2006 12:28:18 +0800
From: <wenbo.yang@simplnano.com>
Subject: Why not add tests order control to Dejagnu, I find a way.
To: <dejagnu@gnu.org>
Cc: bje@gnu.org, rob@welcomehome.org
Message-ID: <002201c71f38$4ade22d0$7e01a8c0@parttime01pc>
Content-Type: text/plain; charset="gb2312"

My work needs to test some cases before others, but I found Dejagnu didn’t support it. It tests cases according to their directory order only. So I modified runtest.exp to achieve my tests order control. My code is not very mature, but it is a way. So I think this feature is valueable to some people of special requests, why not add it to Dejagnu framework?

Explanation of my method:

If there exists file "priority.seq" in test source directory, test cases specified in that file will be done first and orderly. After done them, others should be done. Here is an example content of "priority.seq":

third/third.exp
./second/second.exp
./first/first.exp

Bellow are changes in "runtest.exp", my version is 1.4.99:

diff runtest.exp original_src/runtest.exp

1791,1834d1790
<
< # If exist priority.seq, read it to priorities list orderly.
< if { [file exists $srcdir/priority.seq] } {
< set sequence_file [open $srcdir/priority.seq RDONLY]
< foreach line [split [read $sequence_file]
] {
< if { [file exists $srcdir/$line ] } {
< lappend priorities $line
< } else {
< warning "$srcdir/$line does not exist. Please check $srcdir/priority.seq for error entries."
< }
< }
< close $sequence_file
< }
<
< # Run these *.exp files in priorities list first.
< if { $priorities != "" } {
< foreach test_name $priorities {
< if { ${ignoretests} != "" } {
< if { 0 <= [lsearch ${ignoretests} [file tail ${test_name}]]} {
< continue
< }
< }
<
< # set subdir to the tail of the dirname after $srcdir,
< # for the driver files that want it. XXX this is silly.
< # drivers should get a single var, not "$srcdir/$subdir"
< set subdir [file dirname $test_name]
< set p [expr {[string length $srcdir] - 1}]
< while {0 < $p && [string index $srcdir $p] == "/"} {
< incr p -1
< }
< if {[string range $subdir 0 $p] == $srcdir} {
< set subdir [string range $subdir [expr {$p + 1}] end]
< regsub "^/" $subdir "" subdir
< }
<
< # XXX not the right thing to do.
< set runtests [list [file tail $test_name] ""]
<
< runtest $test_name
< lappend ignoretests [file tail $test_name]
< }
< }
<

Wenbo Yang
2006-12-14
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.gnu.org/pipermail/dejagnu/attachments/20061214/420181aa/attachment.html

------------------------------

Message: 2
Date: Thu, 14 Dec 2006 10:36:07 +0100
From: Stefan Reinauer <stepan@coresystems.de>
Subject: Re: Why not add tests order control to Dejagnu, I find a way.
To: wenbo.yang@simplnano.com
Cc: dejagnu@gnu.org, bje@gnu.org, rob@welcomehome.org
Message-ID: <20061214093607.GA21407@coresystems.de>
Content-Type: text/plain; charset=utf-8

* wenbo.yang@simplnano.com <wenbo.yang@simplnano.com> [061214 05:28]:
> My work needs to test some cases before others, but I found Dejagnu didn’t
> support it. It tests cases according to their directory order only. So
> I modified runtest.exp to achieve my tests order control. My code is not very
> mature, but it is a way. So I think this feature is valueable to some people of
>

This sounds interesting! To work around this "shortcoming" I was writing
all tests that require sequential execution into a single file. Another
method might be to call files and directories 01_*, 02_* ...

--
coresystems GmbH â?¢ Brahmsstr. 16 â?¢ D-79104 Freiburg i. Br.
Tel.: +49 761 7668825 â?¢ Fax: +49 761 7664613
Email: info@coresystems.de â?¢ http://www.coresystems.de/

------------------------------

Message: 3
Date: Thu, 14 Dec 2006 18:31:15 +0800
From: <wenbo.yang@simplnano.com>
Subject: Re: Why not add tests order control to Dejagnu, I find a way.
To: "Stefan Reinauer" <stepan@coresystems.de>
Cc: dejagnu@gnu.org, bje@gnu.org, rob@welcomehome.org
Message-ID: <005201c71f6b$0c5aafa0$7e01a8c0@parttime01pc>
Content-Type: text/plain; charset="utf-8"

But even if you call files and directories by sequential numbers, you can not test it crossingly( I do not know if this word is proper.)
I mean, take a case for example. You have a directory tree like that:

01_xx/01_yy/01.exp
01_xx/02_zz/02.exp
02_aa/01_bb/03.exp
02_aa/02_cc/04.exp

You now have a sequence: 01.exp,02.exp,03.exp,04.exp. But mybe at one point you have follow another order 03.exp,01.exp,04.exp,02.exp. You must change the whole directory tree, at most times, it is not really you want.

So if you have a file specified the priority with no dependence on directory, you can do what you wish just by changing some entries. It is why I said this feature is valueable for some people like you and me. A lot of no sense working should be avoided.

Wenbo Yang

SimpLight Nanoelectronics, LTD.
Email: wenbo.yang@simplnano.com
------------------------------

_______________________________________________
DejaGnu mailing list
DejaGnu@gnu.org
http://lists.gnu.org/mailman/listinfo/dejagnu

End of DejaGnu Digest, Vol 43, Issue 2
**************************************

Copyright © 2005-2006 Solrex Yang. Under GFDL.

Sun’s Open-sourcing

继11月2日的 Microsoft-Novell Deal 的又一件开源软件界大事: 11月12日,Sun Microsystem 总算在几年的观望后决定把 Java 开放源代码了,而且选择了 GPL ( general public licence ) 协议,更进一步的是,今天(14日) Sun 又在它的 Glassfish ( its open source Java EE application server ) 加入了 GPL 作为候选 licence, 这样开发者就可以从 CDDL( Common Development and Distribution Licence ) 和 GPL 中选择自己需要的一个了。看来 Sun 是要专心在开源这条路上走下去了,有评论说:“A similar scenario could be a likely future for Solaris. ”,想来也不是没可能。

其实 Sun CEO Jonathan Schwartz 11月3号在自己的博客里就说了 On the latter(open source landscape), from where I sit, we’re seeing an increasing schism in the market, separating those that protect their customers at the expense of the open source community, from those that protect the community while leaving customers vulnerable. Our view is you have to respect both, and that we’ve effectively threaded the needle with both OpenSolaris and OpenSPARC, and Sun’s commercial derivatives of both - and that those best practices will guide our approach going forward. If you want proof, stay tuned for next week’s announcements. (It won’t be a boring week.) 这个星期确实不是一个 boring 的 week。不过有意思的是,这个声明正好比 Microsoft 和 Novell 的条约缔结晚一天,不知道是不是 Sun 看到微软和 Linux 阵营的靠近心生不安了,才加快了它的开源步伐。这样它能更多的得到 Linux 社区的支持和拥护,因为 Java 如果采用了 GPL 协议的话,估计在每个 Linux 发行版的下一个版本里都会把它作为一个标准配置,或者至少是可选的配置。呵呵,其实对我来说可能这一点才是最重要的,:),这样就不用自己再去下载 JDK 了。

对于 Java 语言我是非常喜欢的,它的开源我也是非常希望看到的,这样我就会有更优秀的语言和更强大的库使用了。不过还是希望 Sun 能够维护一个业内比较强大的标准,避免像 Linux 那样慢慢地走上分歧的道路。至于 Java 的开源对软件业以及计算机语言发展的影响,自然有很多专家去评论,我没有那个前瞻性。

在这个事件里还能看到的比较有趣的一点是 Sun 对 GPL 协议观点的转变,去年它把 Solaris 开源的时候选择的是 CDDL 协议,当时它对关于其选择 CDDL 批评的回击是 the GPL was unfair and predatory because it requires developers to publish changes to the source code. Schwartz(当时是COO)说: the GPL was unfairly used as a way force developers to share their work because the creators have a hidden agenda of forcing a social model on the world. 才过去18个月,Sun 就改变了它对 GPL 协议的观点,看来没有什么是不可以改变的,就连微软都能和 Linux 走到一起,Sun 的观点转变也不算太过。

不过正如有人说过 Sun 的开源一般都是在压力下才达成的,Solaris 是因为服务器市场份额的急剧下降,SPARC 也是因为在处理器市场的不得志,NetBeans 是因为 Eclipse 吃掉了其大量的用户,Sun 究竟还能在开源的道路上走多久,就得看它到底把自己的盈利模式定义在什么位置上了。没有公司愿意赔本做开源,尤其像 Sun, IBM, Microsoft 这样的大公司。开源软件不是福利事业,和赚钱并不矛盾。其实开源软件真正想发展壮大,就必须得找到一个好的盈利模式,这样才能形成良性循环,不然一个个公司的破产肯定会给开源事业造成打击。开源事业需要社区的努力,也需要这些厂商的鼎立支持。看来要订阅 Jonathan 的博客了,要看看他今年能折腾出来多少东西。

Copyright © 2005-2006 Solrex Yang. All rights reserved.

常用正则表达式

在我的文档中发现的,不想再刻到盘上了,自己从网上总结的,比较全,比较干净,放这吧。

匹配中文字符的正则表达式: [u4e00-u9fa5]
评注:匹配中文还真是个头疼的事,有了这个表达式就好办了

匹配双字节字符(包括汉字在内):[^x00-xff]
评注:可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)

匹配空白行的正则表达式:
s*

评注:可以用来删除空白行

匹配HTML标记的正则表达式:< (S*?)[^>]*>.*?|< .*? />
评注:网上流传的版本太糟糕,上面这个也仅仅能匹配部分,对于复杂的嵌套标记依旧无能为力

匹配首尾空白字符的正则表达式:^s*|s*$
评注:可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式

匹配Email地址的正则表达式:w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*
评注:表单验证时很实用

匹配网址URL的正则表达式:[a-zA-z]+://[^s]*
评注:网上流传的版本功能很有限,上面这个基本可以满足需求

匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
评注:表单验证时很实用

匹配国内电话号码:d{3}-d{8}|d{4}-d{7}
评注:匹配形式如 0511-4405222 或 021-87888822

匹配腾讯QQ号:[1-9][0-9]{4,}
评注:腾讯QQ号从10000开始

匹配中国邮政编码:[1-9]d{5}(?!d)
评注:中国邮政编码为6位数字

匹配身份证:d{15}|d{18}
评注:中国的身份证为15位或18位

匹配ip地址:d+.d+.d+.d+
评注:提取ip地址时有用

匹配特定数字:
^[1-9]d*$ //匹配正整数
^-[1-9]d*$ //匹配负整数
^-?[1-9]d*$ //匹配整数
^[1-9]d*|0$ //匹配非负整数(正整数 + 0)
^-[1-9]d*|0$ //匹配非正整数(负整数 + 0)
^[1-9]d*.d*|0.d*[1-9]d*$ //匹配正浮点数
^-([1-9]d*.d*|0.d*[1-9]d*)$ //匹配负浮点数
^-?([1-9]d*.d*|0.d*[1-9]d*|0?.0+|0)$ //匹配浮点数
^[1-9]d*.d*|0.d*[1-9]d*|0?.0+|0$ //匹配非负浮点数(正浮点数 + 0)
^(-([1-9]d*.d*|0.d*[1-9]d*))|0?.0+|0$ //匹配非正浮点数(负浮点数 + 0)
评注:处理大量数据时有用,具体应用时注意修正

匹配特定字符串:
^[A-Za-z]+$ //匹配由26个英文字母组成的字符串
^[A-Z]+$ //匹配由26个英文字母的大写组成的字符串
^[a-z]+$ //匹配由26个英文字母的小写组成的字符串
^[A-Za-z0-9]+$ //匹配由数字和26个英文字母组成的字符串
^w+$ //匹配由数字、26个英文字母或者下划线组成的字符串
评注:最基本也是最常用的一些表达式

利用正则表达式限制网页表单里的文本框输入内容:

用正则表达式限制只能输入中文:
onkeyup="value=value.replace(/[^u4E00-u9FA5]/g,’’)" onbeforepaste="clipboardData.setData(’text’,clipboardData.getData(’text’).replace(/[^u4E00-u9FA5]/g,’’))"

用正则表达式限制只能输入全角字符:
onkeyup="value=value.replace(/[^uFF00-uFFFF]/g,’’)" onbeforepaste="clipboardData.setData(’text’,clipboardData.getData(’text’).replace(/[^uFF00-uFFFF]/g,’’))"

用正则表达式限制只能输入数字:
onkeyup="value=value.replace(/[^d]/g,’’) "onbeforepaste="clipboardData.setData(’text’,clipboardData.getData(’text’).replace(/[^d]/g,’’))"

用正则表达式限制只能输入数字和英文:
onkeyup="value=value.replace(/[W]/g,’’) "onbeforepaste="clipboardData.setData(’text’,clipboardData.getData(’text’).replace(/[^d]/g,’’))"

得用正则表达式从URL地址中提取文件名的javascript程序,如下结果为page1

s="http://www.solrex.cn/page1.htm"
s=s.replace(/(.*/){0,}([^.]+).*/ig,"$2")
alert(s)

应用:javascript中没有像vbscript那样的trim函数,可以利用这个表达式来实现,如下:

String.prototype.trim = function()
{
return this.replace(/(^s*)|(s*$)/g, "");
}

我(Solrex Yang)用的一些正则表达式
用户名的:
^[A-Za-z0-9][w-.]{1,18}[A-Za-z0-9]$

密码的:
^S{4,30}$

真实姓名的:
^[u4e00-u9fa5A-Za-z .]{1,32}$

学号的:
^d{9}$

Email地址的:
w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*

Copyright © 2005-2006 Solrex Yang. All rights reserved.

Ubuntu is so cool

上次用Breezy,还没有体验到Ubuntu的好处,这次装上Dapper,才发现Ubuntu用起来那么爽,真是很酷,怨不得有那么多FANS。

1.免费的光盘派送

虽然说这个不是起决定作用,但是能有正版的无差错的安装光盘,谁愿意去下载那些很难保证质量的光盘镜像呀,就像上次下的FedoraCore5,更新安装时候还好,起码剩个系统用,全新安装时候根本无法成功安装。

2.方便的软件安装,丰富的更新源

这也是那么多人喜欢Debian的原因,可以很方便的管理软件,而且,apt-get命令简化了很多麻烦的操作,避免了编译软件包或者直接安装时候出来的某个某个库找不到的罗嗦,直接apt-get就能把所有需要安装的部分自动下载安装好。况且,Ubuntu的更新源也很多,国内国外教育网都有,速度也还可以。

3.全面的使用和配置文档,尤其是中文文档

Ubuntu Wiki涵盖的范围非常广泛,包含了许多可能遇到的问题和解决方案,尤其是中文方面,比其他版本的要好很多,遇到问题再也不用Google半天了。

4.简单快速的系统

显然,只有一张CD,安装速度也很快,半个小时左右,比起5张CD的FC5,4张CD的RedFlag,DVD的SUSE是方便多了。启动速度也很快,配置好以后启动不超过1分钟。简单并不代表不好,基本功能已经很全了,需要什么更多的软件apt-get一下就行。而且桌面也很漂亮,GNOME桌面能做成这个效果,真的很不错了。

总之,作为桌面使用来说,Ubuntu绝对是相当相当的优秀。到现在为止发现它唯一不爽的地方就是,它不带金山词霸,这也是垃圾RedFlag为数不多能让我赞一下的地方:),BTW,RedFlag自带的应用软件真的是很丰富。

P.S. 贴一下速度比较快的源,sources.list For Dapper(6.06),系统自带的那些个不是很好,比较难连上,速度也不行。
# http://www.ubuntulinux.nl/source-o-matic
#
# If you get errors about missing keys, lookup the key in this file
# and run these commands (replace KEY with the key number)
#
# gpg --keyserver subkeys.pgp.net --recv KEY
# gpg --export --armor KEY | sudo apt-key add -

# Ubuntu supported packages (packages, GPG key: 437D05B5)
deb http://de.archive.ubuntu.com/ubuntu dapper main restricted
deb http://de.archive.ubuntu.com/ubuntu dapper-updates main restricted
deb http://security.ubuntu.com/ubuntu dapper-security main restricted universe multiverse

# Ubuntu community supported packages (packages, GPG key: 437D05B5)
deb http://de.archive.ubuntu.com/ubuntu dapper universe multiverse
deb http://de.archive.ubuntu.com/ubuntu dapper-updates universe multiverse

# CN99
deb http://ubuntu.cn99.com/ubuntu/ dapper main restricted universe multiverse
deb http://ubuntu.cn99.com/ubuntu/ dapper-updates main restricted universe multiverse
deb http://ubuntu.cn99.com/ubuntu/ dapper-security main restricted universe multiverse
deb http://ubuntu.cn99.com/ubuntu/ dapper-backports main restricted universe multiverse
deb http://ubuntu.cn99.com/ubuntu-cn/ dapper main restricted universe multiverse

# LUPA浙江
deb http://mirror.lupaworld.com/ubuntu/archive/ dapper main restricted universe multiverse
deb http://mirror.lupaworld.com/ubuntu/archive/ dapper-security main restricted universe multiverse
deb http://mirror.lupaworld.com/ubuntu/archive/ dapper-updates main restricted universe multiverse
deb http://mirror.lupaworld.com/ubuntu/archive/ dapper-backports main restricted universe multiverse
deb http://mirror.lupaworld.com/ubuntu/ubuntu-cn/ dapper main restricted universe multiverse

# Ubuntu官方
deb http://archive.ubuntu.com/ubuntu/ dapper main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu/ dapper-security main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu/ dapper-updates main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu/ dapper-backports main restricted universe multiverse
deb http://ubuntu.cn99.com/ubuntu-cn/ dapper main restricted universe multiverse

# 上海交通大学
# deb http://ftp.sjtu.edu.cn/ubuntu/ dapper main multiverse restricted universe
# deb http://ftp.sjtu.edu.cn/ubuntu/ dapper-backports main multiverse restricted universe
# deb http://ftp.sjtu.edu.cn/ubuntu/ dapper-proposed main multiverse restricted universe
# deb http://ftp.sjtu.edu.cn/ubuntu/ dapper-security main multiverse restricted universe
# deb http://ftp.sjtu.edu.cn/ubuntu/ dapper-updates main multiverse restricted universe
# deb http://ftp.sjtu.edu.cn/ubuntu-cn/ dapper bleeding main multiverse restricted universe

# 清华大学
# deb http://mirror.net9.org/ubuntu/ dapper main multiverse restricted universe
# deb http://mirror.net9.org/ubuntu/ dapper-backports main multiverse restricted universe
# deb http://mirror.net9.org/ubuntu/ dapper-proposed main multiverse restricted universe
# deb http://mirror.net9.org/ubuntu/ dapper-security main multiverse restricted universe
# deb http://mirror.net9.org/ubuntu/ dapper-updates main multiverse restricted universe
# deb http://mirror.net9.org/ubuntu-cn/ dapper main multiverse restricted universe

Copyright © 2005-2006 Solrex Yang. All rights reserved.