Tchaikov’s Journal

March 31, 2006

活泼的 GDB,兼谈 automake 对待 CFLAGS 和 AM_CFLAGS 的方式

Filed under: Programming

只知道运行gdb的时候加上“-g”的选项是让它生成调试符号,“-g3”能让内联函数也无处藏身。但是有很多次,在调试的时候,游标会上下跳动,同一个参数在不同的栈帧也会有不一样的值。这让我很困扰。
试试看把“-g”换成“-g3”是否有起色。当然,这次不能直接改 Makefile 了。在 Makefile.am 上动手。在 Makefile.am 里加入

AM_CFLAGS = -Wall -g3

不过在生成的 Makefile 里,我发现 gcc 会同时 honour $(AM_CFLAGS) 和 $(CFLAGS) 。CFLAGS 缺省值用的是“-g -O2”,而我设置的“-Wall -g3”。就不知道最后 gcc 一并服下后,它的“-g”的参数有没有“3”了。而且上层 Makefile.am 里定义的 AM_CFLAGS 的设置不会影响到 SUBDIRS 里定义的 Makefile 的 CFLAGS 设置,不知道是不是我的问题。后来索性在所有的 Makefile.am 里把 CFLAGS 和 AM_CFLAGS 设置成一样的了。不过在 google 大神的帮助下,发现好些项目在 configure.ac 里面设置了 CFLAGS:

CFLAGS=\"-g -O2 -Wall\"

这可能是更好的办法。另外,现在把 configure.ac 最后的 echo 改成用 AC_MSG_RESULT 了,彰显专业本色。嘻嘻。

March 28, 2006

GNU Autotools 之旅

Filed under: GNU/Linux, Programming

突发奇想,希望把我的车辆检测工程改用 autotools 改造一把。关于 GNU autotools,在网上搜一下,就能找到大把的资料。
我主要看了 使用 GNU autotools 改造一个软件项目,GNU Autoconf, Automake and Libtool,和Using Automake and Autoconf with C++。我是跟着第一篇文章作的,但却是最后一篇解决了我的一个疑问。
而且,Using Automake and Autoconf with C++的风格我相当喜欢。我的项目目录结构是这样的:

wsn
    src
         main.c
         ata
             ata.h
             ata.c
             fsm.h
             fsm.c
             fsm-init.c
             fsm-noninit.c
             config.h
         cdc
             cdc.h
             cdc.c
         lib
             loop.h
             loop.c
             dataread.h
             dataread.c
    data
下面是几个问题,和它的解决办法(如果有的话):

  • lib 下面是一些工具函数,负责读文件,和实现数据结构。我把它们单独出来。在 Makefile.am 里指定把它编译成了一个临时的静态库,libdataread.a。这样就可以比较清爽地分目录编译了。
  • 现在我实现了两种算法,一个叫 ata,另一个唤作 cdc。名字取得不错吧。呵呵。main.c 要么用 ata,要么用 cdc。两者只能用一个。但是我在 src 目录里的 Makefile.am 的 SUBDIRS 却只能指定一个。要不怎么条件指定编译谁啊?就像:
  • if define(cdc)
    SUBDIRS = cdc $(SUBDIRS)
    else
    SUBDIRS =ata $(SUBDIRS)
    endif
  • 回头再研究一下
  • 目前,我在 configure.ac 里加了两条 AC_ARG_ENABLE,让 configure 时可以选择 enable-cdc 还是 enable-ata,根据选择决定CFLAGS里是-DDECTEC=CDC还是ATA,但是缺少一个缺省值。同时也没有把这个 AC_ARG 传给 automake 判断。这是一个小小的缺憾。
  • 还有一点,main.c 的链接依赖于 libdataread.a。当它的依赖关系无法满足时,libdataread.a 无法自动编译。只能手动 cd 到 libdataread.a 所在的目录,再 make。不知道怎么样才能让 cdc 下的 Makefile 知道 lib 下面有它想要的东西。
  • 后记

    • 看来 manual 是四海一家的解决方案啊。其实是 info 啦,GNU 的 info 比起 manual 来要详细很多。automake-1.9info->6.1 Recursing subdirectories 解释了 automake 的遍历目录树的方式。
      现在,我把 src/Makefile.am 中的 SUBDIRS = cdc lib 改成 SUBDIRS = cdc lib 就可以了。
    • 后来,又碰到了两个比较 trivial 的小错误:
    • 在 configure.ac 里对变量赋值等号两边不能有空格。习惯在C语言里加空格的习惯在这里要改过来。
    • AC_MSG_ERROR 里如果是有比较长的警告信息的话最好用方括弧包起来,有转义字符的话,还必须加斜杠,就像这样:
      AC_MSG_ERROR(\"No Mesa/OpenGL utility \(GLU\) library appears to be installed\")
      或者这样:

      AC_MSG_ERROR([
      *********
      * 不听话,
      * 叫你不听话!
      *********
      ])
  • 最后,我在 AC_OUTPUT 后面加了些 echo,显示用户选择的算法模块。很专业了,已经。嗯。
  • March 16, 2006

    LiveJournal 的客户端

    Filed under: GNU/Linux, Life

    还是有个客户端方便些,不管是网络抽风,还是在本机备份,都有应对。这里列举了好些。其中,

    • LogJam 是我现在在用的。如果不是因为 Drivel 目前没有 tag 支持,那我就用 Drivel 了。LogJam 仍在持续的开发中。界面相当的简单朴素,给我的感觉就是一个带 subject 栏的 Gedit。但是它几乎拥有你需要的所有功能:支持代理、tag,有一些常用的快捷键。注意,我说的是“几乎”,也就是它还是有一些缺憾,没有 HTML 的语法高亮。
    • Drivel,我第一眼就喜欢上了它。但是似乎开发者并不是很积极,主版本一直停留在 2.0.2。添加 tag 支持的请求被作者注意到了,不过他似乎正在进行一个较大的重构——把所有的功能都做在 plugin 里,而且 plugin 也要处理 UI。唔,看来这的确要花不少时间。半年过去了……也许 Todd 只是太忙了吧。
    • 似乎在 Emacs 里能完成所有的事,ljupdate让我们能在 Emacs 里 update lj。还没试过,我会瞧瞧的。
    • Deepest Sender 是一个 Firefox 的 extension,它支持相当多的 blog 平台,。而且开发者相当勤快,一直更新不辍。嗯,就凭这一点我也要试试。


    后记,通过 Deepest Sender,我又能访问我的 WordPress 帐号了。但是,问题在于,和 LJ 相形之下, WP 速度太慢,而且访问太不便了。好吧,想个办法让两处同步,使用 WP 作为备份,这样就兼顾安全和速度了。嗯,就这样。嘿嘿……

    March 15, 2006

    calling convention 和 printf

    Filed under: C++

    前两天和同学谈到这个问题。同学说,Pascal convention 是无法实现类似printf 的变长参数的。虽然我也知道 C convention 和 Pascal convention 的意思和它们的区别,但是为什么后者无法实现 printf,这让我没有一点思路。
    不管三七二十一,先补一下基础知识。Call Convention 是一套约定。它规定了调用方(caller)调用被调用的函数(callee)时,函数的参数应该以何种顺序传递给 callee,以及调用完成后,谁负责清理 stack frame。对了,为了简单起见,我们不讨论 C++ 的 inline 函数和成员函数。
    通常使用的有三种 Calling Convention:C convention、Pascal convention 和stdcall convention。在 GCC 中,它们分别被记为:__cdecl、__pascal 和__stdcall。顾名思义,C convention 就是通常 C 语言所采用的约定。它要求参数传递的顺序为从右向左(即先把最右侧的参数压栈),同时由caller 负责清栈。Pascal convention 恰恰和 C convention 相反,参数从左至右,由 callee 负责清栈。
    两个 Calling Convention 互有利弊。如果遵照 C convention,编译器每次看到有函数调用都会在调用处加入清理代码,每一处调用都会生成相应的二进制目标码,有100次调用的话就会有100份这样的清理代码(尽管有时调用的只是同一个函数)。这种效果如果累积的话,很可能会增加可执行文件的大小。倘若按照Pascal convention 行事,每个函数只在自己返回的地方加入清理代码,就算主程序调用了一百次子程序,可执行文件中的清理代码也只会有子程序的个数那么多份。如果大多调用的是相同的子程序,那么就会节省很多可执行文件的大小。在内存有限的情形,二进制文件的大小对程序的性能来说会是一个相当重要的因素。所以,Pascal convention 在很多情况能给程序稍稍减肥,从空间效率的角度来说,也提升了程序的性能。
    但是,金无足赤,人无完人。Pascal convention 无法处理变长参数(variant length argument)的函数调用。在 C 里,printf 就是一个例子。若使用Pascal convention 的话,则要求 callee 在用毕输入参数后清栈(即调整%esp)。但是问题在于编译器在编译 printf 的时候根本就不知道它的参数到底有多少个!噢,你可能会说,“编译的时候源代码明明白白写在那里,编译器怎么会不知道呢?”。诚然,编译器在编译 caller 的代码时,它是清楚我们塞给printf 多少个参数的。但是 printf 作为 libc 的一员,一般是预编译好的库(precompiled library),在链接时才成为可执行文件的一部分。它,编译器,在那时(预编译时)是没有办法知道如何设置 %esp 的。这些信息只有在编译 caller 的源程序时,编译器才能知晓。
    还有一个不是很充足的理由选择 C convention。假设我们使用汇编撰写子程序,call convention 使用 C。如果子程序的参数列表要添加一个参数(绝大多数情况是在后面添加)。为了访问这个参数,callee 只要用类似 (12)%ebp 的引用就可以了,对前面其它参数的引用不用更动。如果选了 Pascal convention 就麻烦了。对所有参数的引用都要往高地址移动 sizeof(last_param) 字节。另外,即使使用 C convention。虽然约定要求 caller 清栈,编译器也有自由不做这项工作,它可以重复使用参数在栈上占用的空间。
    stdcall 是 win32 API 使用的 calling convention,它糅合了 C 和 Pascal两者的长处,参数从右到左传递,callee 负责清栈。不过因此,在 stdcall 下同样无法使用变长参数列表。由于访问寄存器比访问内存要快达数十倍,因此各家编译器也提供了(部分)使用寄存器传递参数的机制。在 MSVC 中,称之为 fastcall。GCC 则通过
    “-mregparm=NNN”的参数来使用这个机制。用这种办法传递参数,对寄存器比较多的体系架构会有较大的性能改善。


    http://gcc.gnu.org/onlinedocs/gnat_ugn_unw/Calling-Conventions.html
    http://my.execpc.com/~geezer/osd/libc/index.htm#call

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