《Linux编程基础详细》由会员分享,可在线阅读,更多相关《Linux编程基础详细(71页珍藏版)》请在金锄头文库上搜索。
1、第9章Linux程序设计基础本章学习目标本章学习目标通过对本章的学习,读者应该掌握以下主要内容: Linux编程风格 Linux下IDE的使用 Linux下使用GNU cc开发应用程序 Linux程序的调试 Linux下使用RCS/CVS来管理源程序 Linux下软件打包9.1 概述概述 9.1.1 Linux编程编程 Linux软件开发一直在Internet环境下讲行。这个环境是全球性的,编程人员来自世界各地。只要能够访问Web站点,就可以启动一个以Linux为基础的软件项目。Linux开发工作经常是在Linux用户决定共同完成一个项目时开始的。当开发工作完成后,该软件就被放到Interne
2、t站点上,任何用户都可以访问和下载它。由于这个活跃的开发环境,新的以Linux为基础的软件功能日益强大,而且呈现爆炸式的增长态势。 大多数Linux软件是经过自由软件基金会(Free Software Foundation)提供的GNU(GNU 即 GNUs not UNIX)公开认证授权的,因而通常被称作GNU软件。GNU软件免费提供给用户使用,并被证明是非常可靠和高效的。许多流行的Linux实用程序如C编译器、shell和编辑器都是GNU软件应用程序。 Linux程序需要首先转化为低级机器语言即所谓的二进制代码以后,才能被操作系统执行。例如编程时,先用普通的编程语言生成一系列指令,这些指令
3、可被翻译为适当的可执行应用程序的二进制代码。这个翻译过程可由解释器一步步来完成,或者也可以立即由编译器明确地完成。shell编程语言如BASH、TCSH、GAWK、Perl、Tcl和Tk都利用自己的解释器。用这些语言编制的程序尽管是应用程序文件,但可以直接运行。编译器则不同,它将生成一个独立的二进制代码文件然后才可以运行。9.1.2 Linux编程风格编程风格(1)函数返回类型说明和函数名分两行放置,函数起始字符和函数开头左花括号放到最左边。(2)尽量不要让两个不同优先级的操作符出现在相同的对齐方式中,应该附加额外的括号使得代码缩进可以表示出嵌套。 (3)按照如下方式排版do-while语句:
4、 (4)每个程序都应该以一段简短的说明其功能的注释开头。(5)请为每个函数书写注释,说明函数是做什么的,需要哪些入口参数,参数可能值的含义和用途。如果用了非常见的、非标准的东西,或者可能导致函数不能工作的任何可能的值,应该进行特殊说明。如果存在重要的返回值,也需要说明。(6)不要声明多个变量时跨行,每一行都以一个新的声明开头。(7)当一个if中嵌套了另一个if-else时,应用花括号把if-else括起来。(8)要在同一个声明中同时说明结构标识和变量或者结构标识和类型定义(typedef)。先定义变量,再使用。1 1GNUGNU风格风格 (9)尽量避免在if的条件中进行赋值。 (10)请在名字
5、中使用下划线以分割单词,尽量使用小写;把大写字母留给宏和枚举常量,以及根据统一惯例使用的前缀。例如,应该使用类似ignore_space_change_flag的名字;不要使用类似iCantReadThis的名字。(11)用于表明一个命令行选项是否给出的变量应该在选项含义的说明之后,而不是选项字符之后被命名。 2 2Linux Linux 内核编程风格内核编程风格 (1)Linux内核缩进风格是8个字符。 (2)Linux内核风格采用K&R标准,将开始的大括号放在一行的最后,而将结束的大括号放在一行的第一位。(3)命名尽量简洁。不应该使用诸如ThisVariableIsATemporaryCo
6、unter之类的名字。应该命名为tmp,这样容易书写,也不难理解。但是命名全局变量,就应该用描述性命名方式,例如应该命名“count_active_users()”,而不是“cntusr()”。本地变量应该避免过长。 (4)函数最好短小精悍,一般来说不要让函数的参数多于个,否则应该尝试分解这个过于复杂的函数。 (5)通常情况,注释说明代码的功能,而不是其实现原理。避免把注释插到函数体内,而写到函数前面,说明其功能,如果这个函数的确很复杂,其中需要有部分注释,可以写些简短的注释来说明那些重要的部分,但是不能过多。 9.2 IDE使用使用 9.2.1 VIM编辑器编辑器 1 1VIMVIM的简介的
7、简介 VI是Linux世界里最常用的全屏编辑器,所有的Linux机器都提供该编辑器,而Linux里提供的是VI的加强版VIM,但同VI是完全兼容。VI的原意是“visualinterface”,即可视编辑器,用户键入的内容会立即被显示出来、而且其强大的编辑功能可以同任何一种最新的编辑器相媲美。它在Linux上的地位就仿佛Edit程序在DOS上一样。它可以执行输出、删除、查找、替换、块操作等众多文本操作,而且用户可以根据需要对其进行定制,这是其他编辑程序所没有的。VI不是一个排版程序,不象Word或WPS那样可以对字体、格式、段落等其他属性进行编排,它只是一个文本编辑程序。2 2VIM VIM
8、的基本观念的基本观念 VIM有三种操作方式,分别是:命令方式插入方式命令行方式3 3VIMVIM的进入与离开的进入与离开 在系统提示符“$”下键入命令VIM,后面跟上想要编辑(或者建立)的文件名,VIM 可以自动载入所要编辑的文件或是开启一个新文件。 VIM的退出,可以在命令行方式下使用命令“:wq”或者“:q!”,前者的功能是写文件并从VIM中退出,后者的功能是从VIM中退出,但不保存所作的修改(注意冒号)。 4 4VIMVIM的命令方式的命令方式 (1)光标移动要对正文内容进行修改,必须先把光标移动到要修改的内容所在的位置,用户除了通过按键盘的上、下、左、右箭头键来移动光标,还可以利用VI
9、M提供的众多字符组合键,在正文中移动光标,迅速达到指定的行或列,实现定位,常用的快捷键有:表9-1(2)替换和删除将光标定位于文档中指定位置后,可以用其他字符替换光标所指向的字符,或从当前光标位置删除一个或多个字符,常用命令有:表9-2(3)粘贴和复制 在VIM编辑器中,与Windows系统不同的是从正文中删除的内容(如字符、字段或行)并没有真正丢失,而是被剪贴并复制到了一个内存缓冲区中,用户可将其粘贴到正文中的任意位置,完成这一操作的命令是:表9-3 (4)查找字符串 为了方便文档的编辑,VIM提供了强大的字符串查找功能,要查找文件中指定字符或字段出现的位置,可以用该功能直接进行搜索,搜索方
10、法是:在命令行键入字符“/”,后面加上要搜索的字符串,然后按回车键,编辑程序将执行正向搜索(从光标所在的位置向文件末尾方向),并在找到指定字符串后,将光标停在该字符串的开头;键入n命令可以继续执行搜索,找出这一字符串下次出现的位置,用字符“?”取代“/”,可以实现反向搜索(从光标所在的位置向文件开头方向),举例说明如下:表9-5 (5)撤销和重复 在编辑文档的过程中,可以取消错误的编辑命令造成的后果,另外,如果用户希望在新的光标位置再次执行先前的编辑命令,可用重复命令。表9-6 5 5VIMVIM的插入方式的插入方式 (1)进入插入方式 在命令方式下正确定位光标之后,可用一下命令切换到插入方式
11、:表9-7 如果用户想利用已有的文件内容,可以使用命令“:ifilename”,则VIM将指定文件的内容输入当前光标的下一行,且VIM仍处于命令方式。 (2)退出插入方式 退出插入方式的方法是,按ESC键或组合键Ctrl+I (3)正文替换 除了几种简单的切换到插入方式的方法外,还有一些命令允许用户在插入模式之前首先删去一段文字,从而实现正文的替换,这些命令包括:表9-8 6 6VIMVIM的命令方式的命令方式 (1)行号与文件 编辑中文档的每一行正文都有隐藏的行号,用下列命令可以移动光标到指定行:表9-9 在命令方式下,用户可以规定命令操作的行号范围,数值用来指定绝对行号;字符“,”表示光标
12、所在行的行号;字符“$”表示正文最后一行的行号,示例如下:表9-10 在命令方式下,用户还可以对文件进行操作,允许从文件中读取正文,或将正文写入文件,常用命令如下:表9-119.2.1 VIM编辑器编辑器 1 1emacsemacs的简介的简介 emacs文本编辑器可以用来编辑文本、剪辑和粘贴文本内容、提供个人日历和日记,阅读Usenet新闻、发送电子邮件,同时还是一种程序语言解释器,可以编辑C、Lisp、Tev源代码文件、以及Linux的Shell。 emacs是由 Richard Stallman发明的,这位发明者还创建了自由软件基金会(Free Software Foundation,简
13、称FSF)。最初的emacs是用来编辑宏命令的,现已进一步扩充为 UNIX用户中装机用户数量最大、功能最齐全的免费文本编辑器了。 emacs同VI不一样,没有编辑状态和指令状态之分,其最重要的概念是其独特的缓冲区,emacs编辑的所有文件都是放在缓冲区中的,emacs支持同时编辑多个缓冲区,可以将一个文件在多个缓冲区中打开不同的拷贝,甚至其所有的在线帮助和文档以及出错信息都是作为一个缓冲区来显示的,当然这些缓冲区是不可写的,用户可以在这些缓冲区之间拷贝和粘贴文本。并且一般所有的缓冲区在硬盘上都有一个以“#”开头的备份文件,这样在系统突然崩溃的时候可以即时将用户的工作进行备份。在编辑文件时,如果
14、用户在编辑一些特殊类型的文件,例如当用户编辑扩展名为.c的C语言文件时,emacs会产生菜单选项c,向用户提供一些针对编辑c程序特别有用的一些命令。当用户编辑扩展名为.txt的文件则会多出菜单选项tex,让用户在编辑完tex文件后可以即时地观看输出并打印。首先介绍一下几个常见的键盘操作符号的意义:所有的emacs的操作键都是由Control键(一般是键盘上的Ctrl键)和META键(一般是键盘上的Alt键)加上一些键的组合组成的,如果没有Alt键,则可以用输入一个Esc,再输入相应的键来代替。例如: C-x:表示同时按住Ctrl键和x键。 C x:表示先按住Ctrl键,然后释放它,再按下x键。
15、 M-x:表示同时按住Alt键和x键。 M x:表示先按住Alt键,释放它,再按下x键。 2emacs的启动和退出的启动和退出emacs可以用两种方法启动。第一种启动emacs的方法是不装载任何文本文件启动emacs,输入以下命令行:#emacs在屏幕上会出现无任何文本emacs编辑窗口,如图9-2所示。 如果用户是初学者,最好的学习方法是:按下Ctrl-h键(即按住Ctrl键后不放,再按下h字母键),就会自动进入emacs的联机帮助,在屏幕底部emacs命令行中会出现一个提示符,这时再按下字母键t和回车键,便进入了简捷有效的emacs文本编辑器的教程。参照此教程的步骤,用户将对如何使用ema
16、cs有个概括的了解。第二种启动emacs的方法是通过装载某一个文本文件启动emacs,输入以下命令行:# emacs filename如果装载的文件不在当前目录时必须输入该文件名的全称(包括所在目录)。例如,当前目录下有一个文本文件myfile.txt,用emacs对其编辑时,输入命令行启动emacs:# emacs myfile.txt 屏幕上将出现如图9-3所示的emacs编辑窗口。 3emacs的基本操作的基本操作 (1)光标的移动下面列出emacs中的光标的移动情况及其键盘操作: M-b:光标移动到光标左边的单词的开始处。 M-f:光标移动到光标右边的单词的开始处。 M-a:光标移动到
17、当前句子的开始处。 M-e:光标移动到当前句子的结束处。 C-n:光标移动到下一行。 C-p:光标移动到上一行。 C-a:光标移动到行首。 C-e:光标移动到行尾。 M-:光标移动到文件尾。 M-编译”命令进行程序的编译,如图9-15所示。如果程序没有出错,将看到Kdevelop下方的信息框之中显示编译成功的信息。 接着便可以执行应用程序了,执行“建立-执行”命令进行程序的连接,如图9-16所示。若没有发生问题,就会在另一个窗口中看到程序的执行结果,如图9-17所示。图9-15编译应用程序图9-16执行应用程序图9-17应用程序的执行结果9.3 使用使用GNU cc开发应用程序开发应用程序9.
18、3.1 使用使用GNU cc 1gcc的简介的简介 gcc可以使程序员灵活地控制编译过程。编译过程一般可以分为下面四个阶段,每个阶段分别调用不同的工具进行处理,如图9-18所示。预处理链接编译组译源程序(*.c)可执行文件预处理器编译器组译器连接器2gcc的版本信息的版本信息 一般来说,系统安装后就已经安装和设定好了gcc。在shell的提示符下键入gccv,屏幕上就会显示出目前正在使用的gcc的版本,同时这可以确定系统所支持的是ELF还是a.out可执行文件格式。Linux系统中可执行文件有两种格式。第一种格式是a.out格式,这种格式用于早期的Linux系统以及 Unix系统的原始格式。a
19、.out来自于Unix C编译程序默认的可执行文件名。当使用共享库时,a.out格式就会发生问题。把a.out格式调整为共享库是一种非常复杂的操作,由于这个原因,一种新的文件格式被引入Unix系统5的第四版本和Solaris系统中。它被称为可执行和连接的格式(ELF)。这种格式很容易实现共享库。ELF格式已经被Linux系统作为标准的格式采用。gcc编译程序产生的所有的二进制文件都是ELF格式的文件(即使可执行文件的默认名仍然是a.out)。较旧的a.out格式的程序仍然可以运行在支持ELF格式的系统上。3gcc的使用的使用 gcc的使用格式如下:$ gcc optionsfilenames其
20、中filenames为所要编译的程序源文件。 当使用gcc时,gcc会完成预处理、编译、汇编和连接。前三步分别生成目标文件,连接时,把生成的目标文件链接成可执行文件。gcc可以针对支持不同的源程序文件进行不同处理,文件格式以文件的后缀来识别,常见的如表9-1所示。4使用优化选项使用优化选项 当用gcc编译C代码时,它会试着用最少的时间完成编译并且使编译后的代码易于调试.易于调试意味着编译后的代码与源代码有同样的执行次序,编译后的代码没有经过优化。有很多选项可用于告诉gcc,在耗费更多编译时间和牺牲易调试性的基础上,产生更小更快的可执行文件。这些选项中最典型的是-O和-O2选项。-O选项告诉gc
21、c对源代码进行基本优化。这些优化在大多数情况下都会使程序执行的更快。-O2选项告诉gcc产生尽可能小和尽可能快的代码。-O2选项将使编译的速度比使用-O时慢。但通常产生的代码执行速度会更快。5使用调试使用调试和剖析和剖析选项选项GCC支持数种调试和剖析选项。在这些选项里最常用的是-g和-pg选项。-g选项告诉gcc产生能被GNU调试器使用的调试信息以便调试程序。gcc 提供了一个很多其他C编译器里没有的特性,在gcc里能使-g和-O(产生优化代码)连用。这一点非常有用,因为能在与最终产品尽可能相近的情况下调试代码。同时使用这两个选项时必须清楚所写的某些代码已经在优化时被gcc作了改动。 -pg
22、选项告诉gcc在程序里加入额外的代码,执行时,产生gprof用的剖析信息以显示程序的耗时情况。 9.3.2使用使用GNU make编辑编辑makefile1准备工作准备工作 要使用make,必须编写一个叫做Makefile的文件,这个文件描述了软件包中文件之间的关系,提供更新每个文件的命令。一般在一个软件包里,通常是可执行文件靠目标文件来更新,目标文件靠编译源文件来更新。Makefile写好之后,每次改变了某些源文件,只要执行make命令:# make 所有必要的重新编译将执行。Make程序利用makefile中的数据和每个文件的最后修改时间来确定那个文件需要更新,对于需要更新的文件,make
23、程序执行makefile数据中定义的命令来更新。 2makefile文件的基本结构文件的基本结构GNUmake的主要功能是读进一个文本文件makefile并根据makefile的内容执行一系列的工作。makefile的默认文件名为GNUmakefile、makefile或Makefile,当然也可以在make的命令行中指定别的文件名。如果不特别指定,make命令在执行时将按顺序查找默认的makefile文件。多数Linux程序员使用第三种文件名Makefile。因为第一个字母是大写,通常被列在一个目录的文件列表的最前面。Makefile是一个文本形式的数据库文件,其中包含一些规则来告诉make
24、处理哪些文件以及如何处理这些文件。这些规则主要是描述哪些文件(称为target目标文件,不要和编译时产生的目标文件相混淆)是从哪些别的文件(称为dependency依赖文件)中产生的,以及用什么命令(command)来执行这个过程。依靠这些信息,make会对磁盘上的文件进行检查,如果目标文件的生成或被改动时的时间(称为该文件时间戳)至少比它的一个依赖文件还旧的话,make就执行相应的命令,以更新目标文件。目标文件不一定是最后的可执行文件,可以是任何一个中间文件并可以作为其他目标文件的依赖文件。一个Makefile文件主要含有一系列的规则,每条规则包含以下内容。一个目标(target),即mak
25、e最终需要创建的文件,如可执行文件和目标文件;目标也可以是要执行的动作,如“clean”。一个或多个依赖文件(dependency)列表,通常是编译目标文件所需要的其他文件。 一系列命今(command),是make执行的动作,通常是把指定的相关文件编译成目标文件的编译命令,每个命令占一行,且每个命令行的起始字符必须为TAB字符。 除非特别指定,否则make的工作目录就是当前目录。target是需要创建的二进制文件或目标文件,dependency是在创建target时需要用到的一个或多个文件的列表,命令序列是创建target文件所需要执行的步骤,比如编译命令。Makefile规则的一般形式如下
26、:target:dependencydependency(tab)例如,有以下的Makefile文件:#一个简单的Makefile的例子#以#开头的为注释行test:prog.ocode.ogccotestprog.ocode.oprog.o:prog.cprog.hcode.hgcccprog.coprog.ocode.o:code.ccode.hgccccode.cocode.oclean:rmf*.o上面的Makefile文件中共定义了四个目标:test、prog.o、code.o和clean。目标从每行的最左边开始写,后面跟一个冒号(:),如果有与这个目标有依赖性的其他目标或文件,把它
27、们列在冒号后面,并以空格隔开。然后另起一行开始写实现这个目标的一组命令。在Makefile中,可使用续行号()将一个单独的命令行延续成几行。但要注意在续行号()后面不能跟任何字符(包括空格和键)。一般情况下,调用make命令可输入:# make targettarget是Makefile文件中定义的目标之一,如果省略target,make就将生成Makefile文件中定义的第一个目标。对于上面Makefile的例子,单独的一个“make”命令等价于:# make test 因为test是Makefile文件中定义的第一个目标,make首先将其读入,然后从第一行开始执行,把第一个目标test作为
28、它的最终目标,所有后面的目标的更新都会影响到test的更新。第一条规则说明只要文件test的时间戳比文件prog.o或code.o中的任何一个旧,下一行的编译命令将会被执行。但是,在检查文件prog.o和code.o的时间戳之前,make会在下面的行中寻找以prog.o和code.o为目标的规则,在第三行中找到了关于prog.o的规则,该文件的依赖文件是prog.c、prog.h和code.h。同样,make会在后面的规则行中继续查找这些依赖文件的规则,如果找不到,则开始检查这些依赖文件的时间戳,如果这些文件中任何一个的时间戳比prog.o的新,make将执行“gcc c prog.c o p
29、rog.o”命令,更新prog.o文件。以同样的方法,接下来对文件code.o做类似的检查,依赖文件是code.c和code.h。当make执行完所有这些套嵌的规则后,make将处理最顶层的test规则。如果关于prog.o和code.o的两个规则中的任何一个被执行,至少其中一个.o目标文件就会比test新,那么就要执行test规则中的命令,因此make去执行gcc命令将prog.o和code.o连接成目标文件test。在上面Makefile的例子中,还定义了一个目标clean,它是Makefile中常用的一种专用目标,即删除所有的目标模块。 现在来看一下make做的工作:首先make按顺序读
30、取makefile中的规则,然后检查该规则中的依赖文件与目标文件的时间戳哪个更新,如果目标文件的时问戳比依赖文件还早,就按规则中定义的命令更新目标文件。如果该规则中的依赖文件又是其他规则中的目标文件,那么依照规则链不断执行这个过程,直到Makefile文件的结束,至少可以找到一个不是规则生成的最终依赖文件,获得此文件的时间戳,然后从下到上依照规则链执行目标文件的时间戳比此文件时间戳旧的规则,直到最顶层的规则。 通过以上的分析过程,可以看到make的优点,因为.o目标文件依赖.c源文件,源码文件里一个简单改变都会造成那个文件被重新编译,并根据规则链依次由下到上执行编译过程,直到最终的可执行文件被
31、重新连接。例如,当改变一个头文件的时候,由于所有的依赖关系都在Makefile里,因此不再需要记住依赖此头文件的所有源码文件,make可以自动的重新编译所有那些因依赖这个头文件而改变了的源码文件,如果需要,再进行重新连接。3Makefile中的变量中的变量 Makefile里的变量就像一个环境变量。事实上,环境变量在make中也被解释成make的变量。这些变量对大小写敏感,一般使用大写宇母。几乎可以从任何地方引用定义的变量,变量的主要作用如下:保存文件名列表。在前面的例子里,作为依赖文件的一些目标文件名出现在可执行文件的规则中,而在这个规则的命令行里同样包含这些文件并传递给gcc做为命令参数。
32、如果使用一个变量来保存所有的目标文件名,则可以方便地加入新的目标文件而且不易出错。保存可执行命令名,如编译器。在不同的Linux系统中存在着很多相似的编译器系统,这些系统在某些地方会有细微的差别,如果项目被用在一个非gcc的系统里,则必须将所有出现编译器名的地方改成用新的编译器名。但是如果使用一个变量来代替编译器名,那么只需要改变该变量的值。其他所有地方的命令名就都改变了。保存编译器的参数。在很多源代码编译时,gcc需要很长的参数选项,在很多情况下,所有的编译命令使用一组相同的选项,如果把这组选项使用一个变量代表,那么可以把这个变量放在所有引用编译器的地方。当要改变选项的时候,只需改变一次这个
33、变量的内容即可。Makefile中的变量是用一个文本串在Makefile中定义的,这个文本串就是变量的值。只要在一行的开始写下这个变量的名字,后面跟一个“”号,以及要设定这个变量的值即可定义变量,下面是定义变量的语法:VARNAME=string使用时,把变量用括号括起来,并在前面加上$符号,就可以引用变量的值:$VARNAMEmake解释规则时,VARNAME在等式右端展开为定义它的字符串。变量一般都在Makefile的头部定义。按照惯例,所有的Makefile变量都应该是大写。如果变量的值发生变化,就只需要在一个地方修改,从而简化了Makefile的维护。现在利用变量把前面的Makefil
34、e重写一遍:OBJS=prog.ocode.oCC=gcctest:$OBJS$CCotest$OBJSprog.o:prog.cprog.hcode.h$CCcprog.coprog.ocode.o:code.ccode.h$CCccode.cocode.oclean:rmf*.o 除用户自定义的变量外,make还允许使用环境变量、自动变量和预定义变量。使用环境变量的方法很简单,在make启动时,make读取系统当前已定义的环境变量,并且创建与之同名同值的变量,因此用户可以像在shell中一样在Makefile中方便的引用环境变量。需要注意的是,如果用户在Makefile中定义了同名的变量,
35、用户自定义变量将覆盖同名的环境变量。此外,Makefile中还有一些预定义变量和自动变量,但是看起来并不像自定义变量那样直观。表9-3中给出了常见的自动变量。 除了自动变量外,Makefile中还有一些预定义的内部变量,用于定义编译命令名、编译参数等,如表9-4所示:4Makefile的隐含规则的隐含规则 在上面的例子中,几个产生目标文件的命令都是从“.c”的C语言源文件和相关文件通过编译产生“.o”目标文件,这也是一般的步骤。实际上,make可以使工作更加自动化,也就是说,make知道一些默认的动作,它有一些称作隐含规则的内置的规则,这些规则告诉make当用户没有完整地给出某些命令的时候,应
36、该怎样执行。例如,把生成prog.o和code.o的命令从规则中删除,make将会查找隐含规则,然后会找到并执行一个适当的命令。由于这些命令会使用一些变量,因此可以通过改变这些变量来定制make。象在前面的例子中所定义的那样,make使用变量CC来定义编译器,并且传递变量CFLAGS( 编 译 器 参 数 ) 、 CPPFLAGS( C语 言 预 处 理 器 参 数 ) 、TARGET_ARCH(目标机器的结构定义)给编译器,然后加上参数-c,后面跟变量$(第一个依赖文件名),然后是参数-o加变量$(目标文件名)。综上所述,一个C编译的具体命令将会是:$ CC $ CFLAGS $ CPPFL
37、AGS $ TARGET_ARCH c $ -o $在上面的例子中,利用隐含规则,可以简化为:OBJS=prog.ocode.oCC=gcctest:$OBJS$CCo$prog.o:prog.cprog.hcode.hcode.o:code.ccode.hclean:rmf*.o5、常用的、常用的make命令行选项命令行选项make命令有丰富的命令行选项。表9-5中列出了常用的部分。9.3.3 使用使用automake和和autoconf产生产生Makefile在开始使用Automake和autoconf之前,请先确认系统已经安装以下的软件: GNU Automake GNU Autocon
38、f GNU m4 Perl GNU Libtool (如果你需要产生 shared library) Automake所产生的Makefile除了可以做到程序的编译和连接,也已经把如何产生程序文件的操作,以及把安装程序都考虑进去了,所以源程序所存放的目录架构最好符合GNU的标准惯例,下面用hello.c来作为例子进行说明。 在工作目录下建立一个新的子目录devel,再在devel下建立一个hello的子目录,这个目录将作为存放hello这个程序及其相关文件的地方:用编辑器写个hello.c文件:#includeintmain(intargc,char*argv)printf(Hello,GNU
39、!n);return0;接下来就要使用Autoconf及Automake来产生Makefile文件,步骤如下:(1)autoscan产生一个configure.in的模板,执行autoscan后会产生一个configure.scan的文件,可以用它做为configure.in文件的模板:(2)编辑configure.scan文件,如下所示,并且把文件名改成configure.in (3)执行aclocal和autoconf ,分别会产生 aclocal.m4 及 configure 两个文件:(4)编辑Makefile.am文件,内容如下: (5)执行automake -add-missing
40、 ,Automake 会根据 Makefile.am产生一些文件,包含最重要的Makefile.in: (6)最后执行 ./configure :现在你的目录下已经产生了一个Makefile文件,执行make命令就可以开始编译hello.c成执行文件,最后执行./hello:#makegcc-DPACKAGE=hello-DVERSION=1.0-I.-I.-g-O2-chello.cgcc-g-O2-ohellohello.o#./helloHello! GNU! 9.4 调试工具调试工具GDB 9.4.1 GDB调试器简介调试器简介 Linux系统中包含了GNU调试程序gdb,它是一个用来
41、调试C和C+程序的调试器。可以使程序开发者在程序运行时观察程序的内部结构和内存的使用情况。gdb所提供的一些功能如下所示:运行程序,设置所有的能影响程序运行的参数和环境;控制程序在指定的条件下停止运行; 当程序停止时,可以检查程序的状态;修改程序的错误,并重新运行程序; 动态监视程序中变量的值; 可以单步执行代码,观察程序的运行状态。 gdb的功能非常强大,到目前为止,gdb已能够支持Moduls-2、Chill、Pascal和FORTRAN程序的调试,但是调试这些语言的源程序时有一些功能还不能使用。例如调试FORTRAN程序时还不支持表达式的输入、输出变量或类FORTRAN的词法。gdb程序
42、调试的对象是可执行文件,而不是程序的源代码文件。然而,并不是所有的可执行文件都可以用gdb调试。如果要让产生的可执行文件可以用来调试,需在执行gcc指令编译程序时,加上-g参数,指定程序在编译时包含调试信息。调试信息包含程序里的每个变量的类型和在可执行文件里的地址映射以及源代码的行号。gdb利用这些信息使源代码和机器码相关联。 在命令行上输入gdb并按回车键就可以运行gdb了,如果一切正常的话,将启动gdb,可以在屏幕上看到以下的内容:GNUgdbRedHatLinux(5.3post-0.20021129.18rh)Copyright2003FreeSoftwareFoundation,In
43、c.GDBisfreesoftware,coveredbytheGNUGeneralPublicLicense,andyouarewelcometochangeitand/ordistributecopiesofitundercertainconditions.Typeshowcopyingtoseetheconditions.ThereisabsolutelynowarrantyforGDB.Typeshowwarrantyfordetails.ThisGDBwasconfiguredasi386-redhat-linux-gnu.(gdb)启动gdb后,可以在命令行上指定很多的选项。输入:
44、help可以获得gdb的帮助信息。如果想要了解某个具体命令(比如break)的帮助信息,在gdb提示符下输入下面的命令:break屏幕上会显示关于break的帮助信息。从返回的信息可知,break是用于设置断点的命令。另一个获得gdb帮助的方法是浏览gdb的手册页。在LinuxShell提示符输入:mangdb 可以看到man的手册页。9.4.2 GDB命令的基本使用和应用命令的基本使用和应用 1gdb基本命令基本命令 还可以用下面的方式来运行gdb:gdb filename其中,filename是要调试的可执行文件。用这种方式运行gdb可以直接指定想要调试的程序。这和启动gdb后执行file
45、 filename命令效果完全一样。也可以用gdb去检查一个因程序异常终止而产生的core文件,或者与一个正在运行的程序相连。gdb支持很多的命令且能实现不同的功能。这些命令从简单的文件装入到允许你检查所调用的堆栈内容的复杂命令,下面列出了在使用gdb调试时会用到的一些命令。1)file命令:装入想要调试的可执行文件。 2)cd命令:改变工作目录。 3)pwd命令:返回当前工作日录。 4)run命令:执行当前被调试的程序。 5)kill命令:停止正在调试的应用程序。 6)list命令:列出正在调试的应用程序的源代码。 7)break命令:设置断点。 8)Tbreak命令;设置临时断点。它的语法
46、与break相同。区别在于用tbreak设置的断点执行一次之后立即消失。 9)watch命令:设置监视点,监视表达式的变化。 10)awatch命令:设置读写监视点。当要监视的表达式被读或写时将应用程序挂起。它的语法与watch命令相同。 11)rwatch命令:设置读监视点,当监视表达式被读时将程序挂起,等侍调试。此命令的语法与watch相同。 12)next命令:执行下一条源代码,但是不进入函数内部。也就是说,将一条函数调用作为一条语句执行。执行这个命令的前提是已经run,开始了代码的执行。 13)step命令:执行下一条源代码,进入函数内部。如果调用了某个函数,会跳到函数所在的代码中等候
47、一步步执行。执行这个命令的前提是已经用run开始执行代码。 14)display命令:在应用程序每次停止运行时显示表达式的值。15)info break命令:显示当前断点列表,包括每个断点到达的次数。 16)info files命令:显示调试文件的信息。 17)info func命令:显示所有的函数名。 18)info local命令:显示当前函数的所有局部变量的信息。 19)info prog命令:显示调试程序的执行状态。 20)print命令;显示表达式的值。 21)delete命令:删除断点。指定一个断点号码,则删除指定断点。不指定参数则删除所有的断点。 22)Shell命令:执行Lin
48、ux Shell命令。 23)make命令:不退出gdb而重新编译生成可执行文件。 24)Quit命令:退出gdb。 2gdb应用实例应用实例 下面使用gdb调试程序来调试一个实例。被调试的程序相当的简单,但展示了gdb的典型应用。下面列出了将被调试的程序代码,这个程序被称为greeting.c,功能是显示一个简单的问候,再用反序将它列出。9.5 使用使用RCS/CVS来管理源代码来管理源代码 9.5.1 RCS的使用的使用 RCS(Revision Control System)即程序改版控制系统,主要功能是用来管理文件的版本,可以节省空间和时间。这样就不需要在每个程序开发到某一个阶段就将数
49、据拷贝到其他的地方备份起来了。RCS提供了如下几个最重要的指令的: ci指令:将文件放入RCS目录下的控制系统 co指令:从RCS目录下将文件取出 rcs指令:用来对RCS文件进行参数的设置1基本操作方式基本操作方式 一般而言,RCS所产生出来的文件会放在RCS目录中。所以第一步必须要在当前的目录下制作一个文件:rootwyhlinux#mkdirRCS接下来只要使用ci指令。就可以把文件备份到RCS改版控制系统中:rootwyhlinux#citest.c若要将文件取出,可以使用下列指令:rootwyhlinux#cotest.c取出来的文件是只读文件,若要取出可以写入的工作文件,可以加上-
50、l参数来锁定它:rootwyhlinux#co-ltest.c此外将文件放入RCS控制系统时,可以使用-l参数锁定文件,那么目录下的文件依然存在:rootwyhlinux#ci-ltest.c若要比较当前的文件和RCS中最新版本的文件,可以使用下列指令:rootwyhlinux#rcsdifftest.c2指定版本指定版本 若不指定版本编号时,co会从RCS取得最新的版本。如果要以特定的版本号码写入RCS或读出,可以使用-r参数选项。rootwyhlinux#ci-l-r3.25test.c-以3.25作为版本编号rootwyhlinux#co-l-r1.2test.c-将RCS中1.2版的t
51、est.c读出此外,rcsdiff也可以用来指定任何一个版本和当前程序代码进行比较。rootwyhlinux#rcsdiff-r3.25test.c-取出3.25版与test.c进行比较3关键词的使用关键词的使用 在RCS中可以将关键词变量放入程序代码中。这些变量经过RCS会变成版本的注解。用户可以将这些关键词说明当作是程序中的批注。常用的关键词如下:$Author$:将版本放入RCS的用户名称。$Data$:记录程序代码放入RCS时的日期和时间。$Header$:记录文件的标头,包括RCS路径名称、版本号码、日期、作者等。$ID$:和$Header$相同,但不包括RCS路径名称。$Locke
52、r$:记录锁定本版本的用户名称。$Log$:记录将RCS锁住的时间,所输入的文本语句。$RCSfiles$:记录RCS文件名称。$Rivision$:指定版本号码。$Source$:RCS文件名称,包括其路径。$State$:使用-s选项所指定的特殊状态。 使用关键词的步骤如下所示:(1)在程序代码中加入任一关键词rootwyhlinux#vitest.c(2)将程序代码放入RCS版本控制系统rootwyhlinux#ci-ltest.c(3)将文件再次取出。在取出的过程中,co会将每个关键词展开成其对应的值rootwyhlinux#co-ltest.crootwyhlinux#cattest
53、.c9.5.2 CVS的使用的使用 CVS(Concurrent Version System)是个版本控制系统,利用该系统可以记录源代码文件的历史。例如,当软件修改时会产生Bug,并且可能在做这次修改后很长时间不会发现这些问题。使用CVS就可以容易地回顾老的代码版本去发现哪一次的修改导致这些问题。如果CVS保留每一次的代码版本,会浪费很多的空间。因此CVS使用一种比较聪明的办法保存多个版本在一个文件中。它仅仅保留版本间的不同内容。如果很多人在同一个项目上工作,则CVS使用让不同开发者独立工作的方式解决了这个问题。每一个开发者的工作都在他自己的目录内,并且CVS将在每个开发者的工作完成后进行合
54、并工作。在Linux下,CVS的使用一般是以命令行方式。通常,CVS有两种使用方式,一是本机方式,一是远程执行方式。CVS的命令格式是:cvs cvs的选项 cvs的动作 选项 读者可以用cvs H command列出命令command的使用方法。1开始项目开始项目 用CVS管理代码,首先要创建一个“信息仓库”。“信息仓库”简单来说包含一个目录结构。它包括要管理的源代码和用于管理源代码的各种管理文件。先设置环境变量CVSROOT,指向信息仓库的绝对路径,然后调用CVS的init命令:#CVSROOT=/usr/local/cvsroot;exportCVSROOT#cvsinit#ls-l$C
55、VSROOT2添加项目的文件、目录到信息仓库添加项目的文件、目录到信息仓库 要将需要管理的项目的文件加入到信息仓库,并做上标志。如果从头开始一个新的项目,就需要创建一个单独的目录,并把所有要使用的文件做一个有效的组织。而如果在开始使用源文件之前该目录就已经存在,则只需进入该目录就行了。然后,就可以输入源文件目录: # cvs import -m Create Source Dir cvstest/c wu cvstest 这样会生成$CVSROOT/cvstest/c目录。其中-m用来指定注释信息,如果后面在命令行不指定注释信息,则会启动缺省编辑器(vi)要求输入注释信息;cvstest/c是
56、项目名称(实际上是仓库名,在CVS服务器上会存储在以这个名字命名的仓库里);wu,cvstest分别标识了作者和发行标识。3命令简介命令简介(1)导出源文件 cvs checkout -r rev-D date-d dir-j merg1 -j merg2 modules -r 导出指定版本的模块 -D 导出指定日期的模块 -d 导出指定目录而不是模块 -j 合并当前版本和指定版本 使用下面的命令会导出刚才生成的模块,并在当前目录下生成与文件仓库中完全一样的目录结构: # cvs checkout cvstest/c 对于目录结构比较复杂的模块可以在$CVSROOT/CVSROOT/modul
57、es中加以指定:#cvscheckoutCVSROOT/modules在modules文件中加入下面一行:SOURCEcvstest/c然后执行:#cvscommitm“AddSOURCE”以后就可以使用下面的命令在当前路径下生成cvstest/c目录#cvscheckoutSOURCE在当前路径下生成的这个目录就被称为工作目录,对源文件的所有修改都应该在这个目录下完成,而绝对不允许去改动在文件仓库中$CVSROOT目录下的文件。(2)删除、增加、重命名文件和目录cvsadd-kkflags-mmessagefiles.-k指定以后该文件的缺省导出目录-m对文件的描述上述命令会加入一个新的文件
58、到文件仓库里,但直到使用了提交命令它才会真正更新文件仓库。cvsremoveoptionsfiles 上述命令会从文件仓库中删除文件,但也要到提交之后才有作用。(3)提交源文件cvscommit-Rl-mmesgfiles-R连子目录一起提交-l只提交本地目录(不提交子目录)-m注释信息在导出源文件之后,在工作目录中对源文件进行的所有修改都必须在提交之后才能对文件仓库中的源文件起作用,并且新的文件才能够被分配一个新的版本号。(4)释放工作目录cvsreleasedSOURCE 这个命令会删除工作目录cvstest/c(建议在提交了修改的模块后执行这一步),比使用rmrfcvstest要好。4多
59、用户开发多用户开发 在多用户的情况下,如果不同用户修改的是同一个文件的不同部分,则使用下面的命令就能进行版本合并(把检出的文件与当前的最新版本合并):#cvsupdate(1)冲突解决 在有多个用户对同一个文件进行修改时,如果修改了其中的相同部分,而修改后的内容如果有不同的话,出现冲突是不可避免的。如果在CVS文件仓库中有一个文件test.c,它的版本是1.4,用户A先检出该文件进行修改,而稍后有用户B检出该文件进行修改,并提前提交成1.5,而在用户A再提交时就会出现冲突(如果文件内容不同的话),这时CVS会提示需要手工解决。(2)文件版本管理cvslog-lR-rrev-ddate-wlog
60、infiles-l不处理子目录-R对子目录做同样处理-r指定版本号-d指定时间-w指定登录名使用上面的命令可以参看当前模块或指定文件的所有历史版本信息。cvsannotate-lR-rrev|-Ddatefiles-l不处理子目录-R对子目录做同样处理-r指定版本号使用上面的命令可以参看指定文件(检出之后)的所有修改信息。使用下面的命令可以生成相对于一个指定主版本的分支版本:cvsrtagbrrev_rootrev_branchfile_name-b指定生成一个分支版本-r指定该分支的主干节点版本号rev_root主干版本号rev_branch分支版本号file_name指定文件,使用“.”表
61、示当前目录下所有文件使用上面的命令可以生成一个对应版本号的分支版本,由于CVS 版本号是用数字表示的,而且在同一个模块下不同文件的版本完全可能是不同的,所以使用标识会更方便。 例:#cvsrtagbr1.2tlb-1SOURCE以后要访问该分支版本,可以使用“-r”选项#cvscheckoutrtlb-1SOURCE从当前检出的版本切换到一个分支版本:#cvsupdatertlb-1SOURCE使用下面的命令可以看版本信息:cvsstatusvlRfiles-v显示所有信息-l不显示子目录信息-R显示子目录信息cvsupdatejrevmodule 把当前所做的修改与指定版本的文件进行合并。如
62、果在不同版本之间模块的文件有增减,则可以:# cvs update A # cvs updata jbranch_name 5在远程机器上使用在远程机器上使用CVS 通过网络使用CVS 有很多种方式,但在这里只介绍比较简单的一种:通过rsh 执行cvs 命令。 1) 在远程机器的.rhosts中加入对本地机的访问许可:tom huang 2) 使用下面的命令检出模块ESMSTRG# cvs d :ext:huangwyhlinux:/work/cvsroot checkout SOURCE 其中,ext指明了连接方式为rsh,huang指明了本地用户,wyhlinux指明了远地主机,/work
63、/cvsroot指明了在远地主机上的$CVSROOT路径,可以在本地设置CVS_SERVER环境变量指明这个目录。9.6 将软件打包将软件打包 9.6.1 RPM简介简介 RPM是RedhatPackageManager的缩写,是由RedHat公司开发的软件包安装和管理程序,同Windows平台上的Uninstaller和Cleansweep比较类似。使用RPM,用户可以自行安装和管理Linux上的应用程序和系统工具。RPM可以让用户直接以binary方式安装软件包,并且可替用户查询是否已经安装了有关的库文件;在用RPM删除程序时,会询问用户是否要删除有关的程序。如果使用RPM来升级软件,RP
64、M会保留原先的配置文件,这样用户就不用重新配置新的软件了。RPM保留一个数据库,这个数据库中包含了所有的软件包的资料,通过这个数据库,用户可以进行软件包的查询。RPM虽然是为Linux而设计的,但是已经移值到SunOS、Solaris、AIX、Irix等其它UNIX系统上了。RPM遵循GPL版权协议,用户可以在符合GPL协议的条件下自由使用及传播RPM。RPM设计的目的有下面几点:方便的升级功能:RPM让用户不用重新安装整个系统就可以对单个软件包进行升级,当一个新的发行版本问世的时侯,用户也不用重新安装,RPM会替用户全面、自动、智能地升级系统,并且保留用户原先的配置文件,这就大大减少了用户维
65、护系统的工作量。强大的查询功能:用户可以针对整个软件包的数据,或是某些特定的文件进行查询,也可以轻松地查出其个文件是属于哪个软件包,或是从哪里来的。RPM文件本身是经过压缩的,但用户还是可以很容易地快速查询每个软件包的内容,因为在RPM软件包里,已经加入一些特殊的binary header,记录了全部查询时所需要的数据,这一点大大加快了查询速度。 系统校验:当用户不小心删除了某个重要的文件,但是又不知道是哪些软件包需要这个文件,这时侯就可以用RPM来查询已经安装的软体包中缺了哪些文件,是需否要重新安装。并且用户可以校验出安装的软件包是否已经被别人更改过。 允许用户能够使用“纯净”的源代码:让用
66、户取得“未经处理过的源代码”,同时再附上一份“补丁”程序,用户可凭借这些来完成程序编译工作。这样的做法带来不少好处。例如,如果某个程序的新版本问世了,用户可能没有必要再重头开始做全部的编译工作,先观察“补丁”程序的内容,看看有哪些部分是用户需要做的。这样就能让用户更清楚地知道新版本有哪些改进的地方。下面简单介绍一下RPM的使用。 1用用RPM安装软件包安装软件包 最简单的安装命令如下:#rpm-ivhxwpe-1.5.29a-1.i386.rpmRPM会输出该软件包的名称,并且显示一个状态条。安装软件很简单,但是有时会给出一些出错消息:PackageAlrealyInstalled!表示该软件
67、包已经安装,也可使用-replcepkgs选项强制RPM重新安装这个软件。Conflicting files表 示 该 软 件 包 包 含 某 些 其 他 软 件 包 安 装 过 的 文 件 , 可 加 replacepkgs选项覆盖原先的文件。Unresolved Dependency 表示正确运行该软件需要其他哪些软件包,因为RPM的软件包能够查询该软件的“依赖”关系,所以RPM在安装之前会先查询,如果系统没有安装需要的软件包,就会出现错误提示信息。 但是,如要继续安装,就必须先安装相应的软件包,或使用-nodeps选项强制安装,但不推荐使用,因为这样安装的结果一般不能运行。2用用RPM删
68、除安装软件包删除安装软件包要删除已安装的软件包,只需要执行下面的命令:#rpm-exwpe3用用RPM升级软件升级软件升级软件类似于安装软件,执行如下命令:# rpm -Uvh xwpe-1.5.31a-1.i386.rpmRPM会自动删除相应软件包的老版本,如果配置文件同新版本不兼容,则会自动将其保存为另一oldconfig.rpmsave文件。这样,用户就可以自已手工去更改相应的配置文件。4查询软件包查询软件包 用户可以用RPMq来在RPM的数据库中查询相应的软件,RPM会给出软件包的名称、版本和发布版本号,例如:#rpm-qxwpexwpe-1.5.29a-15用用RPM校验软件包校验软
69、件包 用户可以用RPM来校验已经安装的软件包,RPM可以校验文件大小、MD5校验码、文件权限、类型及属主等信息。6实际使用技巧实际使用技巧1)可以通过FTP来安装软件包。如果用户能够连上网络,想安装某个新的软件包时,可以直接用它的URL地址来安装,例如:#rpmIftp:/ 制作制作RPMRPM目前最新的版本4.2同以前的3.x系列是不兼容的。一般来说,为RedHat6.2、Mandrake、TurboLinux、Suse打包就需要3.x系列,而为RedHat7.0以上的版本,打包就需要4.0以上的RPM版本。具体软件可以到www.rpm.org下载。制作RPM的基本步骤如下:1)取回软件so
70、urcecode和相关的patch。2)测试所需的patch,以使得能顺利地build整个程序。3)为程序软件包撰写一份spec文件。4)确认每个文件都在正确的目录位置。5)使用RPM来build整个包。 如果一切操作正确,RPM是能顺利build完成binary与source程序软件包。1目录结构目录结构RPM打包工作必须在系统的特定目录下面进行,例如RedHat就是如下的目录:/usr/src/redhat/BUILD编译时的临时目录/usr/src/redhat/RPMS编译好的RPM的存放位置/usr/src/redhat/RPMS/athlon各种硬件平台的RPM/usr/src/r
71、edhat/RPMS/i386/usr/src/redhat/RPMS/i486/usr/src/redhat/RPMS/i586/usr/src/redhat/RPMS/i686/usr/src/redhat/RPMS/noarch不依赖于硬件的RPM/usr/src/redhat/SOURCES打包需要的源代码和补丁/usr/src/redhat/SPECS打包需要的控制文件/usr/src/redhat/SRPMS编译好的源代码包首先将所有的源代码文件拷贝到/usr/src/redhat/SOURCE下面,再到/usr/src/redhat/SPECS下面编辑相应的.spec控制文件。2
72、编辑编辑macros RPM的配置文件中/usr/lib/rpm/macros是一个全局的配置文件,可以在其中设定一些全局变量,如:%distribution发行版本名%packager打包者姓名和email%vendor软件包作者除了上述的macro设定外,还有许多其他的设定方式,可以使用下面的命令来查看系统的tag与可供使用的flag有哪些:#rpmshowrc3编辑编辑spec文件文件 制作一个软件包时,需要使用到spec文件,其内容为该程序软件包的说明,还包括一些指令,用以执行整个build的过程,还有一份文件列表,用以表示程序软件包中的文件,分别被安装在里面。spec文件的命名方式,最好是遵循一个通用的格式,其格式应该为“软件包名版本号.spec”。4编译编译RPM 在SPEC文件编译完成后,再开始编译工作:#rpmbahelloworld-1.0-1.spec 编译完成后一定要进行测试工作,最好是在另外一台干净的系统上进行测试安装和卸载过程。如果一切无误那么就可以发布这个RPM包了。本章小结