Linux下的常用编程工具初探

  Linux在很多人眼中是非常好的操作系统,不仅因为它的内核和函数库的完整源代码都是公开的,而且因为它拥有许多好用的程序开发工具。 下面就介绍几种常用的编程工具,熟悉这些工具对于开发Linux应用程序是很有必要的。当然了,像其它Linux程序一样,更详细的内容你能在man手册或info页中找到。

一.gcc编译器:

    gcc是GNU提供的优秀的软件之一,其性能不亚于任何商业编译器。它具有惊人的可移植性,而它也号称其最优化功能非常强大。gcc功能实在太多(不信的话你试一下man gcc),这里无法一一列举,只能介绍一些常用的参数。



-o filename

:这可能是你最常用的一个选项了。它用来产生以指定名字的可执行输出文件。如果不指定文件名,gcc产生默认的a.out。键入./filename就可执行程序,查看结果。

$ gcc hello.c -o hello
$ ./hello
hello world!

提示:在使用gcc命令的时候不要用Tab键来补齐文件名,因为那可能会覆盖源文件。

-c

: 只编译不链接,产生以.o结尾的目标文件。当然它也可以同时编译多个文件。

$ gcc -c hello.c 
$ls -l hello.o 
-rw-r--r-- 1 wangcong wangcong 888 6月 11 23:20 hello.o

-g/-ggdb

: -g参数可以让gcc将一些调试信息加入目标文件中,而-ggdb专为gdb调试器产生足够的调试信息。如需用gdb调试程序应加上此项。注意:这会使目标文件大小增加,我们最好只在测试时使用这个参数,最后发行版中将其去除。


 

例如:

$ gcc hash.c -o hash -ggdb

-Idir

: 要求gcc在搜寻头文件时除了默认的目录/usr/include,也要到指定的目录dir中去找。


 

比如,编译test.c时要用到/home/myname/test.h,我们可以:

$gcc -I/home/myname test.c -o test

-llibname

: 尝试链接指定名为liblibname.a的函数库。注意-l选项的顺序是有意义的。


 

比如,test.c中用到了数学函数库,我们要链接libm.a,可以:

$ gcc test.c -lm -o p

-Ldir

: 让gcc也要在指定的目录dir中去寻找-l指定的函数库。


 

例如我们要链接/home/myname/libtest.a,我们这样做:

$gcc -I/home/myname -L/home/myname -ltest test.c -o test

-On

: 最优化选项。后面的n越大最优化程度越大。一般最常用的是-O2。-O0是不优化,这也是默认的。

$gcc -O2 -o foo foo.c

-Wall/-w

: -Wall将打开所有警告,而-w将关闭所有警告。在编译C程序时,强烈建议你加上-Wall选项,因为gcc给出的很多警告都是相当有用的。当然还有折中的选择,具体请参阅man手册。



-lefence

: 将libefence.a函数库链接上,给程序加上电子篱笆,可以用来检查内存泄漏。挺有用的选项。



-S

:用来产生相应的汇编源文件,默认的文件名是

filename.s

-ansi/-std=standard

:standard可以是以下内容:c89,c9x,c99,gnu89,gnu9x,gnu99等。其中-std=c89就相当于-ansi。而gnuxx相当于cxx加上gnu扩展。     当然了,它也可以用来调试C++,Fortran等其它程序。 另外,即使你编译时使用了-Wall选项,也建议你使用lint检查一下你的C程序,它能给出很多有用的信息。Linux常用的lint工具是

splint

。[12]对GCC做了全面的介绍,绝对是首先参考。


 

二.管理工具make:

    make是一个通用的工程管理工具,它可以“自动化编译”,极大地提高了软件开发的效率,无怪乎它被誉为“Unix发展的一个中流砥柱”。make的使用是根据预先设定的规则来运行的,这些设定的规则记录在一个文件中,即

makefile


 

    make命令的格式是:

make [ -f makefile ] [ option ] ... target ...

-c dir

:将指定目录设为make开始运行后的工作目录。


-f filename

:将指定的文件作为makefile


-k

:使make尽可能地编译,即使命令调用返回一个错误,make也不会停止运行。这个功能很有用,例如,当需要移植的时候;你可以创建尽可能多的目标文件,然后可以在不用等待中间文件创建的同时,移植到不能创建的文件处。



 

下面简单地介绍一下makefile的基本书写规则。makefile基本书写规则如下:



 

    target ... : prerequisites ...


 

        command


 

        ...



 

target是目标文件,也就是你要产生的文件或一个标签; prerequisites是产生目标文件所需的文件列表;command是make需要执行的命令。第一个命令可以加“;”跟在倚赖文件列表后,也可以 另起一行书写。注意:当命令另起一行书写时一定要加tab键,否则make不认识。一行放不下时可以用“\”来续行,在一行开头用“#”开始注释。


 

    例如,有下面一个简单的工程,其倚赖关系如下:



 

上图中,工程example倚赖于test.o 和main.o,而test.o倚赖test.h和test.c,main.o倚赖main.c和main.h。我们将这样写makefile:


 

example:test.o main.o 
    gcc -o example test.o main.o 
main.o:main.c main.h
    gcc -c main.c
test.o:test.c test.h
    gcc -c test.c

但是,你会发现,我们编译完成后,test.o和main.o没用了,我们应该删除它们。 那么应该怎么做呢?我们可以加一个动作名字clean,让这个动作完成删除作用。


 

clean:
    rm -f test.o main.o

要执行删除只要运行


 

$make clean

就可以了。但是这还不够, 因为我们也有可能想产生一个目标文件叫做clean,这就会造成冲突。为了避免如此, 我们增加一行:


 

.PHONY: clean

.PHONY是一个伪指令,向make说明clean不是一个文件。当计算clean依赖关系时,make都会忽略任何名为clean的文件。 make除此之外还有大量的高级功能,比如:宏变量,流程控制等。 如果你想了解更多关于make的知识,请看[5]。


 

    其实,makefile并不好写,尤其是对于大型工程项目。我们可以使用imake,它是一个makefile的生成程序,利用了C的预处理程 序,你只要写一个imakefile文件,就能使用imake来产生一个makefile。当然,还有其它的makefile生成器,像

ICmake

automake

等,具体内容请阅读它们的相关文档。



 

三.调试器gdb:

    gdb是一款优秀的调试器,对它熟悉了你就会发现它真的很好用。在使用gdb调试程序前,你应该确认编译时加上了-ggdb选项,那样才能产生更多的调试信息。



提示:虽然你可以同时使用-ggdb和-O选项,但是不建议你那么做,因为一些最优化功能会让调试变得复杂。

 

下面将通过一个程序展示gdb的使用方法。有错误的程序是test.c。

$ gcc test.c -ggdb -o test
$ gdb test
GNU gdb Red Hat Linux (6.1post-1.20040607.41rh)
Copyright 2004 Free Software Foundation, Inc.
...
This GDB was configured as "i386-redhat-linux-gnu"...
(gdb)

好了,现在gdb已经准备好接收命令了。

(gdb) l
1 #include < stdio.h>
2 int main(void){
3     int i=0;
4     for(;i<=10;i++);
5         printf("Now the value of i is:%d\n",i);
6     return 0;
7     }

l/list

命令的功能是列出源代码。如果后面带上行号,则会列出指定行号附近的代码。如果有多个文件的话,你也可以通过指定文件名:行号来列出对应的代码。

(gdb) b main
Breakpoint 1 at 0x8048384: file test.c, line 3.

b/break

是插入断点,后面可以是函数名,也可以是行号。

(gdb) r
Starting program: /misc/myprogram/test

Breakpoint 1, main () at test.c:3
3 int i=0;

r/run

命令是运行程序,直到断点处或程序结束。

(gdb)watch i
Hardware watchpoint 2: i

watch

是用来观察变量的变化的,一旦指定变量发生变化就停止程序。

(gdb) n
4 for(;i<=10;i++);
(gdb) n
Hardware watchpoint 4: i

Old value = 7216256
New value = 1
0x08048396 in main () at test.c:4
4 for(;i<=10;i++);

n/next

是执行下一行程序。i的值发生了变化,程序停了下来。

(gdb) p i
$1 = 1

p/print

用来打印指定表达式的当前值。

...
(gdb) c
Continuing.
Now the value of i is:11

Program exited normally.

c/continue

的功能是继续执行程序直到下一个断点处或程序结束。

(gdb) q
$

q/quit

是退出gdb调试程序。 调试完毕,如果你想把调试信息从执行文件中去掉而又不重新编译,可以使用命令:strip

filename


 

使用gdb我们可以很快找出错误。gdb还有很多高级功能,比如进行汇编级别的调试,而且可以和emacs搭配使用,是非常优秀的调试工具。[8]中对它进行了更全面的介绍。



 

四.性能分析工具gprof:

    有一些程序设计工具,它可以告诉你程序执行的效率,整个程序的调用结构,函数调用关系等。这样的工具真的很有用。gprof就是这样的一个效率分 析工具,它能产生一份详细的列表,列出程序执行的一些统计值,其中包括每个函数被调用的频率,被谁调用,所花费时间等。要使用gprof,在用gcc编译 时要加上-pg参数。这个参数会在目的文件中加上gprof所需的信息,也会将执行文件连接上支持性能分析的函数库。


 

    你只要直接执行编译时加过

-pg

的程序即可,执行完毕后会在当前工作目录下产生一个gmon.out的文件,它记录了gprof所需的信息,我们可以用gprof来读取它。

$ gcc bintree.c -o bintree -pg
$ ./bintree
...
$ gprof bintree gmon.out
Flat profile:

Each sample counts as 0.01 seconds.
no time accumulated

  %   cumulative   self              self     total
 time   seconds   seconds    calls  Ts/call  Ts/call  name
  0.00      0.00     0.00       28     0.00     0.00  print
  0.00      0.00     0.00        7     0.00     0.00  btree_insert
...

gprof的输出信息相当冗长,上面省略了很多。由于上面的函数执行太快,而gprof的计时单位相当粗略,所以那些函数的执行时间都显示0.00秒。gprof能为我们提供不少有用的信息。


 

    另一个常用的计时工具是

time

命令,它可以精确地测量程序运行时间。使用它只需在其后面 加上要测量的命令即可。比如:


 

$time ./test>/dev/null

显示如下,分别是实际耗时,用户级别耗时和系统级别耗时:

real    0m0.056s
user    0m0.014s
sys     0m0.006s

它还能格式化输出,还能测量输入/输出,消息数目,非常方便。还有一个更简单的

calls

程序,它只显示源代码中函数调用的树状结构。而

strace

能够显示程序执行时用到的系统调用,具体请参阅相关手册。



 

五.链接器ld:

    ld是GNU的链接器,支持生成大量的可执行文件格式,它是一款复杂而快捷的工具。 ld有许多灵活的参数来控制其链接过程。这里只介绍主要几个参数。


 

    如果你希望用example.ld作为链接脚本,可以这样使用:



 

    ld -T example.ld



 

如果你想把一系列的.o文件链接到另外一个.o文件中,可以像这样用ld:



 

    ld -r foo.o bar.o -o example.o



 

如果你希望ld生成一个Linux上使用的共享库,可以用:



 

    ld -shared ...



 

如果你想去掉文件中的符号表等内容,加上-s选项。而-S选项则只去掉调试信息。



 

    ld -s ...



 

如果你想把

entry

作为程序入口,可以:



 

    ld -e entry ...



 

如果你想把.text段加载到0x0处,可以:



 

    ld -Ttext 0x0 ...



 

查看帮助信息可以用:



 

    ld --help



 

了解更多,请查看Steve Chamberlain的文章[6]。



 

六.Patch文件:

    如果你在维护包含很多源文件的一个大型项目,每次更新后都推出完整的源程序是不太可行的,所以最好就用patch程序来更新原来的程序。这样,当 你的程序升级时,你只要推出源文件对应的patch文件,其他人就能通过执行patch来使用那个patch文件,以获得最新版本的程序。


 

    比如我们有一个程序foo,我们把它更新为bar,我们可以用diff程序这样产生patch文件:

$diff -u foo bar > foo.patch

-u

参数指定使用特殊的diff输出格式,否则得到的patch格式怪异,一般人都没法看懂。

foo.patch

就包含了描述foo和bar不同的信息,我们将用这个文件来更新。



提示:一定要注意diff命令后面的文件名顺序,一定是旧的文件在前,新的文件在后!

 

接下来,我们用patch来更新:

$patch foo < foo.patch

使用过某个patch文件后,若要再次使用该patch文件,patch程序会产生警告信息:询问是否以-R方式执行,-R将还原文件。这样很好,防止进行了误操作。当然,有时你想更新的是整个目录中的所有源文件,比如foo和bar两个目录,我们可以:


 

$diff -cr foo bar >foo.patch
$patch -p0 < foo.patch

diff的

-c

参数表示输出上下文格式,

-r

参数表示递归的比较两个目录。

-p0

告诉patch程序被更新的文件的路径名并没改变。


 

    diffstat是一个很有用的工具,它可以列出patch 所引起的变更的统计(加入或移出的代码行)。输出关于patch的信息,执行:


 

$diffstat -p1 foo.patch

    更多选项请参阅patch和diff的使用手册。 和diff类似的工具还有比较三个文件的diff3,合并文件的sdiff,简单比较的cmp,分行比较的comm等,这里就不一一介绍了。

七.版本控制系统CVS:

    CVS是非常优秀的版本控制工具,比较适合大项目的版本控制。甚至可以说没有CVS就不会有今天GNU的流行。GNU绝大多数的项目都采用CVS来控制版本。熟悉CVS对Linux程序员来说是非常有必要的。


 

    CVS的使用很复杂,这里只做简单介绍。CVS管理着

repository

,里面存储着正式的源代码。每个在

repository

项目叫做

module

。开发者不能直接编辑

repository

中的文件,而是先要取出(

check out

),编辑完后再把源代码传送回

repository

commit

)。


 

    首先,我们应该把环境变量

CVSROOT

设成我们想要存储

repository

的目录:

$mkdir /usr/local/CVSROOT
$export CVSROOT=/usr/local/CVSROOT

提示:不要把CVS的repository建在和你想要加入repository之中的项目相同的目录中。这会导致一个死循环。

 

储存目录建好后,我们就可以建立

repository

了:

$cvs init

然后要在

repository

中建立项目。在CVS中建立项目有很多方法,我们可以用下面的命令:



 

  

cvs import dir manufacturer tag

 

其中

dir

是项目所属的目录,

manufacturer

是作者名(设成别的也可以),

tag

是版本编号,比如说是

initial

start

。例如:

$cvs import example myname initial

一旦在CVS中建立了项目,开发者们就可以开始各自的工作了,他们互不影响。



提示:避免直接编辑CVS repository中的文件,这不符合CVS设计者的本意。CVS控制的文件被设置为只读来避免这种情况的发生。

 

比如有一个程序有一个名为

example

的模块,可以这样“取出”它的本地目录:

$cvs checkout example

CVS会把

example

所有的文件和目录取出,这样你就可以编辑这些文件了。在编辑完成后,你可以这样将改变的文件写回

repository

中:

$cvs commit -m “description of your changes”

你也可以只写入某个文件:

$cvs commit example.c

当然,在你修改时,很可能你的同事已经将他编辑的文件写入

repository

中去了。这时,CVS不会让你写入,而是要求你先更新再写入。可以这样来更新:

$cvs update

有时候,你想在项目中加入或删除某个文件,你可以这样做:

$cvs add extra.c

这就把extra.c加入进去。删除文件有点麻烦,你必须先在你的工作目录中把你想删除的文件删除,然后在使用下面的命令:

$cvs remove

这样虽有点麻烦,但可以防止你误删,有必要时还可以进行恢复。


 

当然,开源项目几乎都是放在网上的。所以CVS也可以允许你远程使用:

$export CVSROOT=:cvsserver:[email protected]:/cvs
$cvs login
...
CVS password:
...
$cvs checkout example.c
...

很多大的开源项目都是使用CVS进行版本控制的,要参与开源项目,非常有必要熟悉它。关于CVS更详细的介绍见[7]第13章。另外,RCS,Subversion也是同样出色的版本控制工具,[7]中对它们也作了介绍。 gCVS是CVS的图形化前端,使用非常简单。

八.双节棍──objdump&objcopy:

    objdump是一个不错的工具,它有很多功能,反汇编,显示调试信息,显示目标文件中的符号表或段信息等等。 而objcopy可以根据你的需要复制,翻译目标文件。

九.三叉戟──autoconf+automake+libtool:

十.瑞士军刀──valgrind

参见《瑞士军刀──valgrind》一文

十一.ctags+cxref+cflow+indent

ctags

cxref

    cxref可以分析C源程序,为它输出一个交叉引用的列表文件,这个输出文件可以是普通文本格式, 也可以是HTML,Latex等格式,对阅读代码非常方便。cxref命令格式如下:

cxref filename [ ... filename] -options

不同版本的cxref选项不太相同,以我使用的1.5版为例,讲一下它的用法。其它版本请参考它自带的手册。

-Nbasename:指定输出文件的前一部分的名字,默认的是cxref。

-Rdirname:当要分析的C源程序分布在多个目录中时,用此来指定源代码树的根目录。

-Odirname:输出交叉引用文件到指定的目录。

-htmlxx:xx可以是20或者32,使用HTML2.0或3.2格式输出。
 

cflow

indent


 

结论

    除了上面这些之外,Linux还有很多其它优秀的工具, 像著名的

vi

[4]和

emacs

[3]文本编辑器,词法分析工具

flex

, 编译器的编译器

yacc

,排版工具

tex

groff

等。 有的发行版还带了图形化的IDE,像

Qt

KDevelop

等。 Linux自带了不少相关文档,而且篇幅有限,这里不再全面介绍。Linux真的是一个不错的开发平台。



 

进一步阅读:

[1]Programming with GNU Software

, Mike Loukides and Andy Oram, O'Reilly


[2]Sed & Awk, Second Edition

, Dale Dougherty and Arnold Robbins, 1997, ISBN 1-56592-225-5, O'Reilly.


[3]Learning GNU Emacs, Third Edition

, Debra Cameron, James Elliott and Marc Loy, ISBN 0-596-00648-9, O'Reilly


[4]Learning the vi Editor, Fifth Edition

, L.Lamb, 1990, O'Reilly


[5]Managing Projects with GNU make, Third Edition

, Robert Mecklenburg, 2004, ISBN 0-596-00610-1, O'Reilly.


[6]Using LD, the GNU linker

, Steve Chamberlain.


[7]Linux in a Nutshell, 5th Edition

, Ellen Siever, Stephen Figgins and Aaron Weber, 2003, ISBN 0-596-00482-6, O'Reilly & Associates.


[8]

《用GDB调试程序》,CSDN文档中心


[9]GNU Automake

, David MacKenzie and Tom Tromey, For version 1.3, 3 April 1998.


[10]Sed - An Introduction

, Bruce Barnett and General Electric Company, 2005.


[11]Getting started with awk

, HMC Computer Science Department.


[12]GCC: The Complete Reference

, Arthur Griffith, McGraw-Hill

你可能感兴趣的:(linux)