函数调用栈和栈帧

在计算机科学中,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 罢了。

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 的某些功能而不是全部。

为 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 的类型,返回值的存放,等等等等,这些后面的文章都会仔细介绍,也是主要介绍的部分。

为 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 也是一件很有趣的事情。

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.

常用正则表达式

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

匹配中文字符的正则表达式: [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.

又重装了

自从来到鼓楼以后,RedFlag出了些问题,必须关闭电源再启动才能连接上网络,否则显示网卡无连接。其实这问题早就存在,应该是RedFlag网卡驱动的问题,在浦口时候经常停电,也就无所谓了。可到了鼓楼后,一直是不断电的,每次为了上网还要拔插销,麻烦死。Ubuntu是5.0的,自带Firefox是1.0版本,也就不耐烦用,所以一直在用XP。

前几天中了个IE插件,Kaspersky5.0一直杀不掉,结果换装6.0时候就引起了冲突,什么services.exe错误,然后就60秒关机,用shutdown -a停止掉关机,但是发现忽然所有程序都无法上网了,网络是一点问题没有,连接也在。估计是网卡的服务进程出现了问题。重启,没用,修复安装,也没用,只好重装了。其实自从装了几十个更新包之后系统已经变得巨慢,早就想重装了,正好体验下Ubuntu6.06。

重装Windows速度很快,半个小时就搞定了,再装上杀毒软件,免费的简化版Kaspersky6.0:Active Virus Shield,是Kaspersky和AOL一起推出的有一年使用许可的杀毒软件。然后天网,Office,剩下的基本就不用装了,全在D盘里。

Ubuntu6.06的安装方式真让我吃了一惊,居然还能先进LiveCD再装Linux的,不用说配置安装的速度巨慢,真不如原来的简单界面,而且最恶心的是分区,在step5分好的分区,在step6里居然看不见,只能先取消,然后再执行一遍安装过程才能在step6看见。不过系统安装速度似乎比原来快了些。

装好Ubuntu后比较烦的就是升级,由于需要代理出校,在apt-get软件包管理器的代理设置却不能设需要密码的代理,只好到同学那里开一个配好的CCproxy,再从我电脑上用他的代理,软件包下载速度也很慢,不超过10K,一般在3K,是从官方的下载。从镜象是可以下载,但是有好多包镜象站点上没有,非常郁闷。

本来还想体验下SUSE10,上次听讲座弄了张盘,结果给它8G的空间它却说:空间太小,无法安装最小系统。得,伺候不起你,咱闪。

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

Linux学习笔记(7)

apache服务器

apache2的启动、停止和重启动命令:

当更改了服务器的配置文件后,必须重新启动服务器读取配置文件才能生效。

[1]:杀死进程
kill -TERM `cat /var/run/apache2.pid`
这样就看到了前一篇文章讲过的PidFile的作用了,不用再用ps找进程了。

[2]:apache2ctl脚本
apache2ctl是apache2的控制命令
apache2ctl -k restart | stop | …………
从字面意思就可以理解了。

下面以Twiki,一个cgi的wiki程序为例讲解apache2的基于cgi 脚本的服务器的其他配置。

如何允许CGI的运行:

方法[1]:修改站点或者虚拟目录设置文件
站点虚拟目录的设置放在/etc/apache2/sites-available/目录下,默认站点配置文件为default
NameVirtualHost *
虚拟目录的名字,这里*大概是指默认站点。
ServerAdmin solrex@localhost
服务器管理员信箱,主要用来出错时候页面上显示请联系xxx来处理错误。
DocumentRoot /var/www/twiki/
服务器文档根目录,这个很清楚,就不用再说了。
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
可以针对/目录的动作,这个还不是很清楚各个命令指什么。
ScriptAlias /cgi-bin/ /var/www/twiki/bin/
这个就是动作脚本的目录,假如从URL+/cgi-bin/+file,就是用script来执行file,并返回结果给请求的客户端。这样在/var/www/twiki/bin/下的文件就是作为脚本来执行,而不是返回文件了。

方法[2]:修改主配置文件apache2.conf中的AddHandler
如学习笔记(6)里所讲的,把AddHandler cgi-script .cgi .pl前面的注释符号去掉
然后再建一个配置段:
<Directory /www/somedir>
Options +ExecCGI
</Directory>
这样在/www/somedir下的文件就可以用脚本方式来执行了。

方法[3]:修改.htaccess文件
学习笔记(6)里说过,.htaccess用于配置所在目录的访问属性。
在这个文件里加上:
<Directory /home/somdir>
Options ExecCGI
SetHandler cgi-script
</Directory>
即可实现cgi脚本的运行。

CGI的库:

CGI会包含很多库,有些并不是默认安装的,就比如Twiki就需要CGI::Session,这样就要手工安装。
下载CGI-Session-x.xx.tar.gz,解压,make,然后把生成的2进制和perl脚本文件拷贝到CGI的目录中去。我的目录是/usr/share/perl/5.8/CGI,把CGI下的东西拷贝到这个目录里,把man3下的文件拷贝到系统的man库里。这样就可以有手册参考。 这样配置好以后就可以运行CGI的脚本网站了,至于Twiki的配置,我现在的Twiki版式不是很对,左侧的一部分下沉到右侧下方了,还没找出什么原因。等搞清楚了再写。
Copyright ?? 2005-2006 Solrex Yang. All rights reserved.

Linux学习笔记(6)

apache服务器

安装:

可以从官方下安装包,不过一般的Linux发行版本都会带,除了一些叫Desktop的版本(就比如Red Flag Desktop)。现在一般都是apache2.x。

根据版本的不一样,一些命令可能有差别,Red Hat一直是用httpd守护进程来执行的一些命令和管理,但是Ubuntu就是直接用apache相关的命令来管理,可能根据各自的使用范围有些差别吧,但是核心的东西还是一样的。我用的是Ubuntu,大致debian也应该如此吧。

基本配置:

基本配置文件的位置和名称也随着发行版本的不同而不同,Red Hat用的是httpd.conf,Ubuntu的是apache2.conf,虽然也有httpd.conf,但是作用是不一样的。如果你是自己编译源代码,可以设置它的名字和位置为你自己的东西。在Ubuntu中它的位置在/etc/apache2/目录下。

由于好多书介绍的服务器配置针对的是低版本的服务器,这里尝试对apache2的conf文件的格式和内容进行一下解释:
格式:一行一个指令,指令不区分大小写,指令参数要区分大小写,"#"开头行表示注释,被忽略。
内容:
ServerRoot "/etc/apache2"
apache2配置文件所在目录,主要是寻找站点的设定和模块的链接。
LockFile /var/lock/apache2/accept.lock
系统初始化时控制脚本使用LockFile来协调启动和关闭进程。
PidFile /var/run/apache2.pid
服务器启动时候保存进程序列号,以便于管理。
Timeout 300
在终止某个功能之前,用这个变量来设置时间,单位为秒,也是为了便于管理。
KeepAlive On , MaxKeepAliveRequests 100 , KeepAliveTimeout 15
是否允许每个连接提出多于一个的请求,最多允许多少个请求,服务器对请求的等待时间
<IfModule *.c>
………………
</IfModule>
用于特定模块的指令,当加载时候才会生效,仅在启动中起作用。其中有几个比较重要的:
MinSpareServers 5 , MaxSpareServers 10
系统用来处理瞬时负载的后备服务器监控程序的最小(大)数目。
StartServers 5
控制脚本运行时,默认启动的监控程序的数目。
MaxClients 20
最多客户端连接数。
MaxRequestsPerChild 0
最大请求数,超过这个次数就停止服务器,0为不限制。
User www-data
Group www-data
执行服务器的用户名和所属组
LogFormat "%{User-agent}i" agent
服务器日志格式
ErrorLog /var/log/apache2/error.log
全局错误日志位置
Include /etc/apache2/mods-enabled/*.load
Include /etc/apache2/mods-enabled/*.conf
包含模块的设置
Include /etc/apache2/httpd.conf
包含用户的配置
Include /etc/apache2/ports.conf
包含监听的端口,在低版本的服务器上是用Listen命令直接加到这里的。
Alias /icons/ "/usr/share/apache2/icons/"
别名,或者叫做映射,在URL路径中加上前面的/icons/,服务器默认指向后面的目录。
<Directory "/usr/share/apache2/icons">
…………
</Directory>
此类模块中的指令和配置只作用于后面指定的目录中,若是<Files "">则是指文件。
ErrorDocument 400 /error/HTTP_BAD_REQUEST.html.var
指定的服务器错误页面指向,一般只用默认即可。
DirectoryIndex index.html index.cgi index.pl index.php index.xhtml
默认主页文件名,如果目录下存在其中的一种,就直接将它返回,比较经常修改的一个属性。
AccessFileName .htaccess
辅助访问文件名,将此文件放到某一个目录下,可以定义此目录的访问设置。
UseCanonicalName Off
TypesConfig /etc/mime.types
HostnameLookups Off
IndexOptions FancyIndexing VersionSort
这几个不是很懂。
AddIcon , AddEncoding , AddLanguage, AddCharset, AddType
顾名思义,没什么意思,默认即可。
AddHandler cgi-script .cgi .pl
以cgi或者pl为后缀的文件当作cgi-script,返回cgi处理后的数据。在这里设置以后对全局生效,一般情况下出于安全性考虑会被注释掉。
BrowserMatch "Mozilla/2" nokeepalive
对于访问类型使用不同的方法对待连接。
Include /etc/apache2/sites-enabled/[^.#]*
包含站点的设定文件,即是虚拟目录设定所在目录,在apache中,站点属性和虚拟目录的设定是在另外的目录中进行的。

也是刚开始学习,有些地方说不太清楚,以后会有更详尽的解释。
Copyright ?? 2005-2006 Solrex Yang. All rights reserved.

Linux学习笔记(5)

有关Ubuntu的一些心得

Ubuntu的安装:

安装方法和普通Linux没什么区别,不过主要是英文界面,要注意看,尤其是分区的时候,不然万一选错了,硬盘上数据就惨了。分区的时候可能会有明明可用的分区它标为不可用,不知道什么原因。

在安装基本系统的时候有时会出现错误,但是Ubuntu最好的就是它会让你再选择从什么步骤开始,只需要重复一下安装基本操作系统这个步骤基本就可以解决。相比而言Redhat做的就太差了,一个小文件错误就得从头开始重新安装。

Ubuntu退出安装按ESC就可以,然后选择最后一个步骤退出安装。安基本系统大概在8分钟左右,拷贝文件大概在15分钟左右,重新启动以后安装软件大约20分钟。

Ubuntu安装时候没有设置root的密码,需要进入single模式修改密码,方法是在grub引导的时候选择safe mode,更一般地,对所有Linux都适用的是,光标放到引导选项上按E进入编辑启动项,在启动内核的那句(就是最长的那句)后面加一个词:single,结束编辑再按B启动。

进入single模式不需要用户名,直接就有root权限,更改密码用:passwd。

Ubuntu中库的安装:

有些程序库默认是没有安装的,就如GCC的一些库。有时候会用到,比如Ubuntu的Firefox是1.0版本的,想用1.5的话,可以下一个1.5的免安装包,运行就需要libstdc++5.so,这时候到软件包里找到这个包装上就是了。Debian核心的Linux的介绍比较少,现在也不是很清楚怎么装软件包以外的软件。rpm包是可以用alien -i命令来安装的,但是会有诸多问题,比如依赖性不好解决。不过直接从源代码编译应该是最好的办法,就是太浪费时间了。最好是上网从Ubuntu的官方介绍的方法下载安装。

Ubuntu中如何拨号上网:

版本Ubuntu 5.10,系统中没有附带像FC里system-config-network和Red Hat中redhat-config-network这样的带图形用户界面的网络设置工具,但它附带了一个类图形界面的专门拨号工具:pppoeconf,默认是不安装的,需要在开机后从软件包中安装。可以在虚拟控制台下运行,配置拨号上网的命令是:pppoeconf,在控制台中会有页面提示,按照提示操作即可。配置好以后,可以用 pon dsl-provider 命令拨号,poff 挂断,plog 查看状态。

有时候拨号连不上,如果网路没问题的话,而且再用pppoeconf 配置,在开始的modem检查阶段就通不过,重启一下Modem可以解决问题。

Ubuntu的配置:

配置方法在Ubuntu的官方主页上就有,UbuntuWiki给出了很多指导,有什么问题到那里查一下就行了。包括软件的更新和安装,也可以从那里找到下载。
Copyright ?? 2005-2006 Solrex Yang. All rights reserved.

心烦意乱

最近心情不是很平静,大概又该到五一的缘故。每逢节日的时候就会感到孤单,也许和环境有关吧,痛恨自己。

第三天晚上没有出去自习了,本来今天是没打算呆在宿舍的,可是昨天踢球时候把腿磕破了,正好在膝盖上方,裤子一碰到就痛得不行。想想在教室里卷着裤腿也不雅观,还是回来吧。自制力还是不够,懦弱的人那。

在宿舍也干不了什么事情,迷上了wiki,用来作笔记挺不错的。调了一下一个asp的,可是没想到上传到空间里居然不能用,两个能用的吧自己又不喜欢,算了,就只在自己的机子上装一个好了,asp的东西还是不好用。

跟电脑过不去,Linux老是对中文支持不好,FC和Ubuntu用起来都很麻烦,已经习惯了Redhat的Linux,对Debian还是不习惯。一怒之下,干脆装了个RedFlag,正好自己还有原装的盘,也不用再去下了,还支持国货。没想到居然用起来很爽(这句话说出来恐怕是要被那些用Debian,SuSE的人鄙视的),但用起来真的很方便,中文输入法和文件名都根本不用配置。可惜的是它为了追求易用在界面上丧失了一些Linux共有的东西,一切全仿照Windows来的,虽然说核心没变。KDE的窗口也有点耗资源。决心以后再也不在操作系统上消耗时间了。

唉,越来越不习惯在BLOG上写自己的心情了,也没有太多的空闲去牢骚。手机也很少短信,每天晚上回去开一会儿机,有一两条就回一下,没有就关机睡觉。现在觉得实在是身外之物,不带也罢,乐得没辐射,就让它天天在宿舍躺着吧。

这个学期还是蛮平静的,偶尔烦躁一下也是难免,有点像大一时候了。对一切不再抱有期望,冷眼看人来人往,说是随遇而安也不为过。想起来东坡的词:余问柔:岭南水土,应是不好?柔对曰:此心安处,便是吾乡。可是仍有勘不破啊。

又,每晚听“City夜动听”临末了张艺的祝福:春暖心安,是一个不错的词,送诸位!

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

Linux学习笔记(4)

SWAP分区的重建:

由于前面重装系统时候,不小心用第三方软件把硬盘分区表重建了一下,结果就丢失了SWAP分区。
用了下面的方法恢复:

先用第三方软件重新分出一个大小为256M的分区,注意要先保证这个里面没有文件。
重新启动Linux,这个时候可能会启动不了,下面有方法介绍如何重新正确启动。
启动到Linux以后,在控制台界面下(Xwindow里的虚拟控制台不可以,会提示找不到命令。),用下面命令重建SWAP分区:
mkswap -c -v1 -L label /dev/hda*
mkswap是建立swap分区或者swap文件的命令,-c是指在建立分区以前检查硬盘有没有坏道,-v1是指建立一个新的类型swap分区,-L是指建立一个标志,这样挂载的时候可以不考虑硬盘分区,从标志挂载(只能用于新型SWAP分区,即-v1选项必须要有)。标志要不要取决于fstab里面的设置,如果fstab里面对swap是这样定义的话:
LABEL=SWAP-hda3 swap swap defaults 0 0
最好建立一个标志为:SWAP-hda3,这样就不用修改fstab了。/dev/hda*是指要建立为swap分区的那个分区,如果不确定的话可以先用:
fdisk -l /dev/hda查看一下分区表。
这样做好以后,再启动就不会出现SWAP分区失败的情况了。

分区表变化以后如何用GRUB引导启动:

由于分区表的变化,很有可能GRUB找不到Linux内核的位置,这时候需要修改grub.conf来重新定位Linux内核,可以用Linux光盘的第一张选救援模式启动,这时候现有系统会挂载到/mnt/sysimage目录下,从这个目录里找到/boot/grub/grub.conf,修改一下就行了。如果搞不太懂硬盘上的Linux在哪个分区,建议用第三方软件光盘启动查看分区表,找到Linux格式的分区,看哪个是100M左右,就是安装系统时候分给boot分区的大小,就知道该是哪个分区了。

在救援模式下挂载系统时候一定要选择可修改文件,并且有时候虽然提示说失败,实际已经将硬盘上的系统挂到/mnt/sysimage下了。

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

浪费时间

如果想浪费时间,装操作系统可能非常适合。这两天我就干了好多愚蠢的事情

NIGHTMARE BEGIN:

觉得XP系统太慢了,装的东西太多,重装一下吧!->
放进去安装光盘,屏幕上方打出一行字,“安装程序正在检查你的硬件配置”(翻译),然后黑屏,半个小时都没有反应,换了3张光盘都是如此。->
怀疑是不是C盘为NTFS分区的缘故,或者是Windows正版验证给动了什么手脚,就用其他程序将C盘全写空。->
还是安装不了,更无奈的是原来的系统也进不去了。算了,安个2000吧。->
2000安装倒是一点事情都没有,安装成功以后,重装了几个程序和系统更新。->
安完系统更新重启电脑的时候忽然想起我还有一键恢复,想想以前是完全格式化过系统,不知道恢复还有没有,就按了一下。->
发现居然还能一键恢复,最不幸的是为了试验能不能用我让它试着恢复了下,看到果然有效忽然想起自己的2000,赶紧停掉,系统提示说你现在结束的话,原来的系统也会被破坏。->
2000果然进不了了,只好再用一键恢复重装XP,这倒是挺顺利。->
可是恢复以后进不去,提示说是system32下的hal.dll有问题,要重新覆盖一个好的。在DOS下给覆盖了一下,还是没用。->
后来猜测是分区表有问题,重建了一下分区表(为Linux SWAP分区的丢失埋下了隐患)。再一键恢复,好了,系统可以进去了。->
XP装好了,OFFICE也安上了,补丁也全打上了,结果,又出现去年的毛病,干别的一点事情都没有,只要一ADSL拨号,马上黑屏掉电重启。5555,我心中那个怒火啊!!!!->
唉,放弃一切努力,准备把XP只当作一个工作站,Linux用来上网和处理其他事务,实在不想再装什么系统了。以后实在闲得没事时候看看能不能装2003 Server吧。->
把以前备份的blog的rss源导入ThunderBird,还有Gmail帐户和Firefox的收藏夹。

END

就这样,两个晚上过去了,一点书都没看。

到现在我仍然不明白两个问题:

1)为什么用XP安装光盘启动系统就黑屏,然后硬盘灯一直亮,都不闪的,但就是没反应。而2000却可以,一点问题都没有。
这个问题百度知道上有人告诉我说是Linux引导的问题,建议我把引导程序安到/boot扇区。现在可以把Linux卸载然后再安装。
但不懂的是为什么2000没问题?

2)为什么ADSL一拨号就重启,就算换第三方拨号程序也没用,安装的时候就会重启。
这个问题我知道是怎么来的,是因为我去年装VMware的时候,没有考虑周全,先把网卡给禁用了,然后VMware也需要虚拟网卡吧,可能写到系统里面什么东西了,就发生冲突了。
可是不明白的是为什么一键恢复以后还是这样?那可是刚安好系统的时候联想的人给做的备份那。而且为什么本地连接没有问题?
要是哪个兄弟知道什么原因,能告诉我,就非常感谢了。
还好还好的是,我平常有比较经常的备份,没有损失什么重要文件。可惜的是我要告别FeedDemon和Outlook,还有迅雷,MSN 8.0,QQ2006。迎接LumaQQ,IM和ThunderBird.
最好的是FC5现在支持SCIM,比原来的那个IIIMF好用多了,输入法那个多啊,不用让我再告别智能ABC的双拼。

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

Linux学习笔记(3)

关于GRUB的一些记录

鉴于最近RP的一些危机,电脑也一直在欺负我,就写下了一些遭遇。下面的Linux和GRUB版本以FC4,FC5及其附带的GRUB为例,GRUB大概是0.95版本。

2000重装以后的GRUB恢复:

重装Windows会把MBR(Main Boot Record)覆盖掉,这时候就进不了Linux系统了,可以用很多方法进行GRUB的恢复,我用的可以说是最简单的方式,因为我有FC4的第一张安装盘。
光盘启动,按F5(FC默认,其他版本大概是F4)->
在boot:下输入命令:linux rescue->
出现#提示符后输入命令:chroot /mnt/sysimage 注:意思大概是把内核改为硬盘上的而不是光盘->
在#后输入:grub-install /dev/hda 注:该是把grub重装到第一块硬盘->
重新启动

这里install大概就是把GRUB重装到MBR里,而因为GRUB的引导配置是在/boot扇区下面放着的,所以原来的引导配置都还在,一般情况下不需要更改。可,不幸的是,我恰恰碰到了该修改的情况。

重建GRUB后的Windows系统寻找:

我不知道安装盘为什么会把2000安到第二个区,其实我也不知道这个第二个区是什么意思,大概似乎不是分区。
由于原来的GRUB引导配置对XP的默认是:
rootnoverify (hd0,0) //启动XP系统的扇区
chainloader +1

而现在却启动不了,可以用下面的方法一个一个尝试:
启动后按任意键,出现GRUB引导选择界面->
光标移动到Windows项上,按’e’,就是edit的意思->
将光标移动到第一项启动的分区选择上,再按’e’->
修改(hd0,0),其中前面的0代表是第几块硬盘,默认0为第一块,后面的0代表第几个扇区。->
一般情况下修改后面一个扇区,一个一个的试,改了以后按’b’,就是boot的意思,如果启动不了,按’Esc’,再加1,然后boot.
我是试到了2,才成功的。

修改GRUB的引导配置:

上面的介绍是在启动时候人工修改,但是每次改也是很麻烦的事情(除非你想藉此来增加别人进入你系统的难度),可以到/boot扇区中修改掉GRUB的配置。
用root用户进入Linux,在/boot/grub/目录下找到grub.conf文件,打开,会看到下面的东西(唉,我还是记不住vi怎么用,只好用emacs打开):
default=0 //缺省启动系统标号,0表示FC5,1表示XP
timeout=3 //GRUB启动界面等待时间
splashimage=(hd0,7)/grub/splash.xpm.gz //GRUB默认的背景文件
hiddenmenu //大概是要不要显示选择界面,这个意思是如果不按任意键就直接进入默认系统
title Solrex’s Fedora Core 5(2.6.15-1.2054_FC5) //GRUB选择菜单的选项标题
root (hd0,7) //boot所在扇区
kernel /vmlinuz-2.6.15-1.2054_FC5 ro root=LABEL=/ rhgb quiet //Linux内核所在位置
initrd /initrd-2.6.15-1.2054_FC5.img
title Solrex’s Windows XP professional
rootnoverify (hd0,0) //启动XP系统的扇区
chainloader +1
显然修改一下rootnoverify (hd0,0)后面那个0就行了。


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