第 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用户决定共同完成一个项目时开始的 。 当开发工作完成后,该软件就被放到
Internet站点上,任何用户都可以访问和下载它 。 由于这个活跃的开发环境,新的以 Linux为基础的软件功能日益强大,而且呈现爆炸式的增长态势 。
大多数 Linux软件是经过自由软件基金会( Free
Software Foundation)提供的 GNU( GNU 即 GNU’s not
UNIX)公开认证授权的,因而通常被称作 GNU软件。 GNU
软件免费提供给用户使用,并被证明是非常可靠和高效的。许多流行的 Linux实用程序如 C编译器,shell和编辑器都是 GNU软件应用程序。
Linux程序需要首先转化为低级机器语言即所谓的二进制代码以后,才能被操作系统执行。例如编程时,先用普通的编程语言生成一系列指令,这些指令可被翻译为适当的可执行应用程序的二进制代码。这个翻译过程可由解释器一步步来完成,或者也可以立即由编译器明确地完成。
shell编程语言如 BASH,TCSH,GAWK,Perl,Tcl和 Tk都利用自己的解释器。用这些语言编制的程序尽管是应用程序文件,但可以直接运行。编译器则不同,它将生成一个独立的二进制代码文件然后才可以运行。
9.1.2 Linux编程风格
( 1)函数返回类型说明和函数名分两行放置,函数起始字符和函数开头左花括号放到最左边。
( 2)尽量不要让两个不同优先级的操作符出现在相同的对齐方式中,
应该附加额外的括号使得代码缩进可以表示出嵌套。
( 3)按照如下方式排版 do-while语句:
( 4)每个程序都应该以一段简短的说明其功能的注释开头。
( 5)请为每个函数书写注释,说明函数是做什么的,需要哪些入口参数,参数可能值的含义和用途。如果用了非常见的、非标准的东西,
或者可能导致函数不能工作的任何可能的值,应该进行特殊说明。如果存在重要的返回值,也需要说明。
( 6)不要声明多个变量时跨行,每一行都以一个新的声明开头。
( 7)当一个 if中嵌套了另一个 if-else时,应用花括号把 if-else括起来。
( 8)要在同一个声明中同时说明结构标识和变量或者结构标识和类型定义 (typedef)。先定义变量,再使用。
1,GNU风格
( 9)尽量避免在 if的条件中进行赋值。
( 10)请在名字中使用下划线以分割单词,尽量使用小写;把大写字母留给宏和枚举常量,以及根据统一惯例使用的前缀。例如,应该使用类似 ignore_space_change_flag的名字;不要使用类似
iCantReadThis的名字。
( 11)用于表明一个命令行选项是否给出的变量应该在选项含义的说明之后,而不是选项字符之后被命名。
2,Linux 内核编程风格
( 1) Linux内核缩进风格是 8个字符。
( 2) Linux内核风格采用 K&R标准,将开始的大括号放在一行的最后,
而将结束的大括号放在一行的第一位。
( 3)命名尽量简洁。不应该使用诸如
ThisVariableIsATemporaryCounter之类的名字。应该命名为 tmp,这样容易书写,也不难理解。但是命名全局变量,就应该用描述性命名方式,例如应该命名,count_active_users()‖,而不是,cntusr()‖。
本地变量应该避免过长。
( 4)函数最好短小精悍,一般来说不要让函数的参数多于10个,
否则应该尝试分解这个过于复杂的函数。
( 5)通常情况,注释说明代码的功能,而不是其实现原理。避免把注释插到函数体内,而写到函数前面,说明其功能,如果这个函数的确很复杂,其中需要有部分注释,可以写些简短的注释来说明那些重要的部分,但是不能过多。
9.2 IDE使用
9.2.1 VIM编辑器
1,VIM的简介
VI是 Linux世界里最常用的全屏编辑器,所有的 Linux机器都提供该编辑器,而 Linux里提供的是 VI的加强版 ——VIM,但同 VI是完全兼容。 VI的原意是,visual interface‖,即可视编辑器,用户键入的内容会立即被显示出来、而且其强大的编辑功能可以同任何一种最新的编辑器相媲美。它在 Linux上的地位就仿佛 Edit程序在 DOS上一样。它可以执行输出、删除、
查找、替换、块操作等众多文本操作,而且用户可以根据需要对其进行定制,这是其他编辑程序所没有的。 VI不是一个排版程序,不象 Word或
WPS那样可以对字体、格式、段落等其他属性进行编排,它只是一个文本编辑程序。
2,VIM 的基本观念
VIM有三种操作方式,分别是:
命令方式
插入方式
命令行方式
3,VIM的进入与离开在系统提示符,$‖下键入命令 VIM,后面跟上想要编辑(或者建立)
的文件名,VIM 可以自动载入所要编辑的文件或是开启一个新文件。
VIM的退出,可以在命令行方式下使用命令,,wq‖或者,,q!,,
前者的功能是写文件并从 VIM中退出,后者的功能是从 VIM中退出,但不保存所作的修改(注意冒号)。
4,VIM的命令方式
( 1)光标移动要对正文内容进行修改,必须先把光标移动到要修改的内容所在的位置,用户除了通过按键盘的上、下、左、右箭头键来移动光标,还可以利用 VIM提供的众多字符组合键,在正文中移动光标,
迅速达到指定的行或列,实现定位,常用的快捷键有:表 9-1
( 2)替换和删除将光标定位于文档中指定位置后,可以用其他字符替换光标所指向的字符,或从当前光标位置删除一个或多个字符,常用命令有:
表 9-2
( 3)粘贴和复制在 VIM编辑器中,与 Windows系统不同的是从正文中删除的内容
(如字符、字段或行)并没有真正丢失,而是被剪贴并复制到了一个内存缓冲区中,用户可将其粘贴到正文中的任意位置,完成这一操作的命令是:表 9-3
( 4)查找字符串为了方便文档的编辑,VIM提供了强大的字符串查找功能,要查找文件中指定字符或字段出现的位置,可以用该功能直接进行搜索,
搜索方法是:在命令行键入字符,/”,后面加上要搜索的字符串,
然后按回车键,编辑程序将执行正向搜索(从光标所在的位置向文件末尾方向),并在找到指定字符串后,将光标停在该字符串的开头;键入 n命令可以继续执行搜索,找出这一字符串下次出现的位置,
用字符,?”取代,/”,可以实现反向搜索(从光标所在的位置向文件开头方向),举例说明如下:表 9-5
( 5)撤销和重复在编辑文档的过程中,可以取消错误的编辑命令造成的后果,
另外,如果用户希望在新的光标位置再次执行先前的编辑命令,可用重复命令。表 9-6
5,VIM的插入方式
( 1)进入插入方式在命令方式下正确定位光标之后,可用一下命令切换到插入方式:表 9-7
如果用户想利用已有的文件内容,可以使用命令,,i
filename‖,则 VIM将指定文件的内容输入当前光标的下一行,且
VIM仍处于命令方式。
( 2)退出插入方式退出插入方式的方法是,按 ESC键或组合键 Ctrl+I
( 3)正文替换除了几种简单的切换到插入方式的方法外,还有一些命令允许用户在插入模式之前首先删去一段文字,从而实现正文的替换,这些命令包括:表 9-8
6,VIM的命令方式
( 1)行号与文件编辑中文档的每一行正文都有隐藏的行号,用下列命令可以移动光标到指定行:表 9-9
在命令方式下,用户可以规定命令操作的行号范围,数值用来指定绝对行号;字符,,,表示光标所在行的行号;字符,$‖表示正文最后一行的行号,示例如下:表 9-10
在命令方式下,用户还可以对文件进行操作,允许从文件中读取正文,或将正文写入文件,常用命令如下:表 9-11
9.2.1 VIM编辑器
1,emacs的简介
emacs文本编辑器可以用来编辑文本,剪辑和粘贴文本内容,提供个人日历和日记,阅读 Usenet新闻,发送电子邮件,同时还是一种程序语言解释器,可以编辑 C,Lisp,Tev源代码文件,以及 Linux的
Shell。
emacs是由 Richard Stallman发明的,这位发明者还创建了自由软件基金会( Free Software Foundation,简称 FSF)。最初的 emacs
是用来编辑宏命令的,现已进一步扩充为 UNIX用户中装机用户数量最大、功能最齐全的免费文本编辑器了。
emacs同 VI不一样,没有编辑状态和指令状态之分,其最重要的概念是其独特的缓冲区,emacs编辑的所有文件都是放在缓冲区中的,emacs支持同时编辑多个缓冲区,可以将一个文件在多个缓冲区中打开不同的拷贝,
甚至其所有的在线帮助和文档以及出错信息都是作为一个缓冲区来显示的,当然这些缓冲区是不可写的,用户可以在这些缓冲区之间拷贝和粘贴文本 。 并且一般所有的缓冲区在硬盘上都有一个以,#‖开头的备份文件,这样在系统突然崩溃的时候可以即时将用户的工作进行备份 。
在编辑文件时,如果用户在编辑一些特殊类型的文件,例如当用户编辑扩展名为,c的 C语言文件时,emacs会产生菜单选项 c,向用户提供一些针对编辑 c程序特别有用的一些命令 。 当用户编辑扩展名为,txt的文件则会多出菜单选项 tex,让用户在编辑完 tex文件后可以即时地观看输出并打印 。 首先介绍一下几个常见的键盘操作符号的意义:所有的 emacs的操作键都是由 Control键 ( 一般是键盘上的 Ctrl键 ) 和 META键 ( 一般是键盘上的 Alt键 ) 加上一些键的组合组成的,如果没有 Alt键,则可以用输入一个 Esc,再输入相应的键来代替 。 例如:
C-x:表示同时按住 Ctrl键和 x键 。
C x:表示先按住 Ctrl键,然后释放它,再按下 x键 。
M-x:表示同时按住 Alt键和 x键 。
M x:表示先按住 Alt键,释放它,再按下 x键 。
2,emacs的启动和退出
emacs可以用两种方法启动 。 第一种启动 emacs的方法是不装载任何文本文件启动 emacs,输入以下命令行:
# emacs
在屏幕上会出现无任何文本 emacs编辑窗口,如图 9-2所示。
如果用户是初学者,最好的学习方法是:按下 Ctrl-h键(即按住 Ctrl
键后不放,再按下 h字母键),就会自动进入 emacs的联机帮助,在屏幕底部 emacs命令行中会出现一个提示符,这时再按下字母键 t和回车键,便进入了简捷有效的 emacs文本编辑器的教程。参照此教程的步骤,用户将对如何使用 emacs有个概括的了解。
第二种启动 emacs的方法是通过装载某一个文本文件启动 emacs,输入以下命令行:
# emacs filename
如果装载的文件不在当前目录时必须输入该文件名的全称 ( 包括所在目录 ) 。 例如,当前目录下有一个文本文件 myfile.txt,用 emacs
对其编辑时,输入命令行启动 emacs:
# emacs myfile.txt
屏幕上将出现如图 9-3所示的 emacs编辑窗口。
3,emacs的基本操作
( 1) 光标的移动下面列出 emacs中的光标的移动情况及其键盘操作:
M-b:光标移动到光标左边的单词的开始处 。
M-f:光标移动到光标右边的单词的开始处 。
M-a:光标移动到当前句子的开始处 。
M-e:光标移动到当前句子的结束处 。
C-n:光标移动到下一行 。
C-p:光标移动到上一行 。
C-a:光标移动到行首 。
C-e:光标移动到行尾 。
M->:光标移动到文件尾 。
M-<:光标移动到文件头 。
( 2) 文本的操作
① 插入文本的操作
② 删除文本的操作
③ 取消操作
④ 粘贴操作
⑤ 查找和替换
( 3) 文件的操作
C-x C-f:在屏幕底部出列,Findfile,/_‖等待用尸输入文件名,如输入,/myfile.txt‖则提示( newfile),清屏后光标出现在左上角,等待用户输入文本的内容。
C-x C-s:当将文本输入完毕后选择存盘操作,屏幕底部提示出文本所在的目录及文件名,/myfile.txt‖,指示出该文件存放在磁盘何处。
C-x C-w:当对一个原有的文本文件继续编辑或修改后;需将改变后的文件重新保存。这时 emacs会提示,/myfile.txt‖exists;
overwrite?( y or n) _当回答,y‖后,提示信息,/myfile.txt‖
(重写该文件)。
C-x C-c:当确定结束对 emacs编辑器的使用,可选择 File菜单中的 Exitemacs选项退出 emacs。如果没有对输入或修改的内容存盘,
emacs会提醒用户别忘记做保存文件操作。
( 4)窗口的操作窗口就是屏幕区域,用户可以使用多个窗口来对一个缓冲区的不同部分进行操作,或对不同的缓冲区进行操作 。
当用户使用 C-x C-f来打开一个文件的时候,emacs将会创建一个缓冲区,用户在其中进行编辑操作。 emacs允许同时对多个缓冲区中的文本进行编辑,比如在缓冲区互相粘贴、剪辑等等。用户还可以直接输入快捷键( C-x C-b)查看所选择的是哪个缓冲区,如图 9-4所示。
用户可以使用两种方法在当前窗口的不同缓冲区间进行切换:
( 1) 使用 Buffers菜单,它包括当前时刻打开的所有的缓冲区,在其中选择,就能切换到想要编辑的文件 。
( 2)使用键盘对缓冲区进行操作,键入 C-x b命令,然后按下 RET
( RET,即键盘上的回车键 Enter。任何一个命令输入完毕时,必需紧跟着一个 Enter,它的作用是用来告诉系统,命令输入已经结束,可以开始执行相关的动作了),就能立刻切换到位于当前编辑缓冲区的前一个缓冲区,或按 Tab键,得到一个缓冲区的列表,然后输入需使用的缓冲区的名字(也可以用鼠标单击名字)。要关闭一个缓冲区,先切换到该缓冲区,键入 C-x k,最后按下回车键。
4.在 emacs中执行 Shell
在 emacs中有两种执行 shell的方法:一种是进入 shell command mode,
另一种是进入 shell mode。 二者都可以执行 shell,其最大不同之处是,
进入 shell mode的状态,执行 shell的同时,仍可以切换到其他模式处理别的工作,但如果使用 shell command mode,就必须等 shell执行完后才可以做其他的事 。
使用 shell command mode时,使用者在屏幕的最下方输入要执行的 shell
命令,emacs会开启一个名为,*shell command mode*‖的窗口,将 Shell
命令执行的结果显示在此窗口中 。 shell mode则是执行一个子 shell,其输入与输出都是通过同一个缓冲区,所以输入与输出是在同一个地方,它不像 shell command mode,命令输入与结果的显示在不同的地方 。
1) shell command mode
ESC-!( shell-command) 启动 shell command mode
ESC- ( shell-command-on-region)
2) shell mode
ESC-x shell\index ESC-x shell是启动 shell mode的命令
5.用 emacs进行程序的编辑、编译与测试
emacs是一个综合的环境,在提供程序编辑的同时,自然会提供一个可供程序执行的环境 。 以下就要谈谈 emacs可以为程序开发者提供那些服务 。 emacs针对不同的语言提供不同的编译摸式 。 emacs提供的服务有程序缩进的安排,括号对应的提示,程序注解的安排,光标移动的方式与程序的删除等等 。 基本上,emacs是提供一个编写程序的格式,只是此格式可根据使用者的需要而自行设计 。 emacs选择适合的语言模式,是根据所编辑的文件名称扩展名来判断的 。 像上面提到的那样,如果用户编辑扩展名为,c的 C语言程序,emacs会自动给予 C语言模式,而不需使用者自行处理 。 emacs提供的程序语言模式有 LISP,SCHEME,C,C++、
FORTRAN,MAKEFILE,AWK,PERL,ICON与 MUDDLE等 。
编辑好的程序可以直接进入 emacs的编译模式,不需离开 emacs到 Linux的
shell下进行编译 。 进入 emacs的编译模式很简单,只要输入,ESC –x
compile‖即可 。 emacs缺省的编译命今是 make,执行 ESC –x compile命令的结果如下所示:
compile command,make -k
如果要使用其他的编译器,只需在,compile command:,的后面加上对应的的编译命令即可,此命令与在 Linux shell下使用编译的方法完全相同。
除了编辑,编译之外,程序开发者还需要的功能是调试器,emacs
也提供了在编辑器内部调试程序的功能 。 emacs提供了四种调试器,
分别为 gdb,dbx,xdb与 sdb,使用者可根据需要来选择合适的调试器 。 下面是使用调试器的命令:
ESC–X gdb RET file RET
ESC–X dbx RET file RET
ESC–X xdb RET file RET
ESC –X sdb RET file RET
9.2.3 使用 Kdevelop开发 C程序
1,Kdevelop的简介
Kdevelop是一套功能强大的集成开发环境,其整合了开发程序所需的编译器、连接器、除错工具、版本控制工具等,可以用
Kdevelop快速地建立各式各样的应用程序,包括:
KDE程序 ;GNOME;Qt程序 ;终端程序 ;其它
2.启动 Kdevelop
如果是第一次使用 Kdevelop,Kdevelop会先启动,Kdevelop设置,进行 Kdevelop的环境设定,共需要完成 9个步骤的设置工作,如图 9-5所示。
单击,Kdevelop设置,欢迎画面对话框中的 【 下一步 】 按钮开始进行 Kdevelop的设定。,Kdevelop设置,的第二项设定为,选择语法高亮风格,,这里选择缺省的,Kdevelop 2.0 风格,,如图 9-6所示。
图 9-5 Kdevelop设置 图 9-6 选择语法高亮风格选择喜欢的语法高亮表示风格后,单击 【 下一步 】 按钮进入,用户交换界面模式,的选择窗口,如图 9-7所示。
同样选择好用户交换界面模式后,单击 【 下一步 】 按钮进入
,Kdevelop中所使用的工具检测窗口,,如图 9-8所示。
图 9-7 选择用户交换界面模式 图 9-8 Kdevelop所使用的工具检测窗口单击,工具程序检测窗口,对话框中的 【 下一步 】 按钮,进行下一个步骤。下一个步骤为,寻找 Qt文档,,并设定文件路径,一般而言这个步骤应该会成功完成,将见到如图 9-9所示对话框。
单击,寻找 Qt文档,对话框之中的 【 下一步 】 按钮,进入下一个步骤。下一个步骤为,寻找 KDE程序库文件,,同样,一般而言这个步骤也应该会成功完成。再缺省完成两步以后,Kdevelop设置将显示
,安装过程成功完成,的对话框,如图 9-10所示,此为 Kdevelop设置的最后步骤,单击 【 下一步 】 按钮,稍待一会 Kdevelop便会启动。
图 9-9 Qt文档查找 图 9-10 安装过程成功完成窗口
3.新建一个新项目在 Kdevelop中开发 C程序,需要用创建项目的方式进行。请执行
,项目 /新建,命令,打开应用程序向导对话框,创建过程如图 9-11
所示。
选择要创建程序的种类,这里选择 C程序,然后单击 【 下一步 】 按钮进行下一步骤。下一个步骤为关于项目资讯的设定,如图 9-12所示,
在此输入项目的名称、目录、版本号码、作者姓名、以及作者电子邮件地址。
图 9-11 选择创建程序的种类 图 9-12 设定项目信息单击 【 创建 】 按钮开始产生项目档案,结果如图 9-13所示,此处可能会看到一些警告信息,但是一般而言并不影响项目的建立。
4.修改项目创建项目后,在 Kdevelop窗口的左边选择源程序,开始编写程序。
Kdevelop会将程序加上一些默认的内容,可视需求自行修改,如图 9-14所示。
图 9-13 产生项目档案的过程 图 9-14修改项目
5.项目的编译与执行写好程序后,执行,建立 ->编译,命令进行程序的编译,如图 9-
15所示。如果程序没有出错,将看到 Kdevelop下方的信息框之中显示编译成功的信息。
接着便可以执行应用程序了,执行,建立 ->执行,命令进行程序的连接,如图 9-16所示。若没有发生问题,就会在另一个窗口中看到程序的执行结果,如图 9-17所示。
图 9-15 编译应用程序图 9-16 执行应用程序图 9-17 应用程序的执行结果
9.3 使用 GNU cc开发应用程序
9.3.1 使用 GNU cc
1,gcc的简介
gcc可以使程序员灵活地控制编译过程。编译过程一般可以分为下面四个阶段,每个阶段分别调用不同的工具进行处理,如图 9-18所示。
预处理 链接编译 组译源程序 (*.c) 可执行文件预处理器 编译器 组译器 连接器
2,gcc的版本信息一般来说,系统安装后就已经安装和设定好了 gcc。在 shell的提示符下键入 gcc v,屏幕上就会显示出目前正在使用的 gcc的版本,同时这可以确定系统所支持的是 ELF还是 a.out可执行文件格式。
Linux系统中可执行文件有两种格式 。 第一种格式是 a.out格式,这种格式用于早期的 Linux系统以及 Unix系统的原始格式 。 a.out来自于 Unix C编译程序默认的可执行文件名 。 当使用共享库时,
a.out格式就会发生问题 。 把 a.out格式调整为共享库是一种非常复杂的操作,由于这个原因,一种新的文件格式被引入 Unix系统 5的第四版本和 Solaris系统中 。 它被称为可执行和连接的格式 ( ELF) 。
这种格式很容易实现共享库 。
ELF格式已经被 Linux系统作为标准的格式采用。 gcc编译程序产生的所有的二进制文件都是 ELF格式的文件(即使可执行文件的默认名仍然是 a.out)。较旧的 a.out格式的程序仍然可以运行在支持 ELF格式的系统上。
3,gcc的使用
gcc的使用格式如下:
$ gcc [options][filenames]
其中 filenames为所要编译的程序源文件 。
当使用 gcc时,gcc会完成预处理、编译、汇编和连接。前三步分别生成目标文件,连接时,把生成的目标文件链接成可执行文件。 gcc可以针对支持不同的源程序文件进行不同处理,文件格式以文件的后缀来识别,常见的如表 9-1所示。
4.使用优化选项当用 gcc编译 C代码时,它会试着用最少的时间完成编译并且使编译后的代码易于调试,易于调试意味着编译后的代码与源代码有同样的执行次序,
编译后的代码没有经过优化。有很多选项可用于告诉 gcc,在耗费更多编译时间和牺牲易调试性的基础上,产生更小更快的可执行文件。这些选项中最典型的是 -O和 -O2选项。
-O选项告诉 gcc对源代码进行基本优化。这些优化在大多数情况下都会使程序执行的更快。
-O2选项告诉 gcc产生尽可能小和尽可能快的代码。 -O2选项将使编译的速度比使用 -O时慢。但通常产生的代码执行速度会更快。
5.使用调试和剖析选项
GCC 支持数种调试和剖析选项。在这些选项里最常用的是 -g和 -pg选项。
-g选项告诉 gcc产生能被 GNU调试器使用的调试信息以便调试程序。
gcc 提供了一个很多其他 C编译器里没有的特性,在 gcc里能使 -g和 -
O(产生优化代码 )连用。这一点非常有用,因为能在与最终产品尽可能相近的情况下调试代码。同时使用这两个选项时必须清楚所写的某些代码已经在优化时被 gcc作了改动。
-pg选项告诉 gcc在程序里加入额外的代码,执行时,产生 gprof用的剖析信息以显示程序的耗时情况。
9.3.2使用 GNU make编辑 makefile
1.准备工作要使用 make,必须编写一个叫做 Makefile的文件,这个文件描述了软件包中文件之间的关系,提供更新每个文件的命令 。 一般在一个软件包里,通常是可执行文件靠目标文件来更新,目标文件靠编译源文件来更新 。
Makefile写好之后,每次改变了某些源文件,只要执行 make命令:
# make
所有必要的重新编译将执行。 Make程序利用 makefile中的数据和每个文件的最后修改时间来确定那个文件需要更新,对于需要更新的文件,make程序执行 makefile数据中定义的命令来更新。
2,makefile文件的基本结构
GNU make的主要功能是读进一个文本文件 makefile并根据 makefile的内容执行一系列的工作。 makefile的默认文件名为 GNUmakefile,makefile
或 Makefile,当然也可以在 make的命令行中指定别的文件名。如果不特别指定,make命令在执行时将按顺序查找默认的 makefile文件。多数
Linux程序员使用第三种文件名 Makefile。因为第一个字母是大写,通常被列在一个目录的文件列表的最前面。
Makefile是一个文本形式的数据库文件,其中包含一些规则来告诉
make处理哪些文件以及如何处理这些文件 。 这些规则主要是描述哪些文件 ( 称为 target目标文件,不要和编译时产生的目标文件相混淆 )
是从哪些别的文件 ( 称为 dependency依赖文件 ) 中产生的,以及用什么命令 ( command) 来执行这个过程 。
依靠这些信息,make会对磁盘上的文件进行检查,如果目标文件的生成或被改动时的时间 ( 称为该文件时间戳 ) 至少比它的一个依赖文件还旧的话,make就执行相应的命令,以更新目标文件 。 目标文件不一定是最后的可执行文件,可以是任何一个中间文件并可以作为其他目标文件的依赖文件 。
一个 Makefile文件主要含有一系列的规则,每条规则包含以下内容 。
一个目标 ( target),即 make最终需要创建的文件,如可执行文件和目标文件;目标也可以是要执行的动作,如,clean‖。
一个或多个依赖文件 ( dependency) 列表,通常是编译目标文件所需要的其他文件 。
一系列命今 (command),是 make执行的动作,通常是把指定的相关文件编译成目标文件的编译命令,每个命令占一行,且每个命令行的起始字符必须为 TAB字符。
除非特别指定,否则 make的工作目录就是当前目录 。 target是需要创建的二进制文件或目标文件,dependency是在创建 target时需要用到的一个或多个文件的列表,命令序列是创建 target文件所需要执行的步骤,比如编译命令 。
Makefile规则的一般形式如下:
target,dependency dependency
(tab)<command>
例如,有以下的 Makefile文件:
# 一个简单的 Makefile的例子
# 以 #开头的为注释行
test,prog.o code.o
gcc –o test prog.o code.o
prog.o,prog.c prog.h code.h
gcc –c prog.c –o prog.o
code.o,code.c code.h
gcc –c code.c –o code.o
clean:
rm –f *.o
上面的 Makefile文件中共定义了四个目标,test,prog.o,code.o和
clean。 目标从每行的最左边开始写,后面跟一个冒号 (,),如果有与这个目标有依赖性的其他目标或文件,把它们列在冒号后面,并以空格隔开 。 然后另起一行开始写实现这个目标的一组命令 。 在
Makefile中,可使用续行号 ( \) 将一个单独的命令行延续成几行 。 但要注意在续行号 ( \) 后面不能跟任何字符 ( 包括空格和键 ) 。
一般情况下,调用 make命令可输入:
# make target
target是 Makefile文件中定义的目标之一,如果省略 target,make就将生成 Makefile文件中定义的第一个目标 。 对于上面 Makefile的例子,
单独的一个,make‖命令等价于:
# make test
因为 test是 Makefile文件中定义的第一个目标,make首先将其读入,然后从第一行开始执行,把第一个目标 test作为它的最终目标,所有后面的目标的更新都会影响到 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 prog.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按顺序读取 makefile中的规则,然后检查该规则中的依赖文件与目标文件的时间戳哪个更新,如果目标文件的时问戳比依赖文件还早,就按规则中定义的命令更新目标文件。如果该规则中的依赖文件又是其他规则中的目标文件,那么依照规则链不断执行这个过程,直到 Makefile文件的结束,至少可以找到一个不是规则生成的最终依赖文件,获得此文件的时间戳,然后从下到上依照规则链执行目标文件的时间戳比此文件时间戳旧的规则,直到最顶层的规则。
通过以上的分析过程,可以看到 make的优点,因为,o目标文件依赖,c源文件,源码文件里一个简单改变都会造成那个文件被重新编译,
并根据规则链依次由下到上执行编译过程,直到最终的可执行文件被重新连接。例如,当改变一个头文件的时候,由于所有的依赖关系都在 Makefile里,因此不再需要记住依赖此头文件的所有源码文件,make
可以自动的重新编译所有那些因依赖这个头文件而改变了的源码文件,
如果需要,再进行重新连接。
3,Makefile中的变量
Makefile里的变量就像一个环境变量。事实上,环境变量在 make中也被解释成 make的变量。这些变量对大小写敏感,一般使用大写宇母。几乎可以从任何地方引用定义的变量,变量的主要作用如下:
保存文件名列表。在前面的例子里,作为依赖文件的一些目标文件名出现在可执行文件的规则中,而在这个规则的命令行里同样包含这些文件并传递给 gcc做为命令参数。如果使用一个变量来保存所有的目标文件名,则可以方便地加入新的目标文件而且不易出错。
保存可执行命令名,如编译器。在不同的 Linux系统中存在着很多相似的编译器 系统,这些系统在某些地方会有细微的差别,如果项目被用在一个非 gcc的系统 里,则必须将所有出现编译器名的地方改成用新的编译器名。但是如果使用一个变量来代替编译器名,那么只需要改变该变量的值。其他所有地方的命令名就都改变了。
保存编译器的参数。在很多源代码编译时,gcc需要很长的参数选项,
在很多情况下,所有的编译命令使用一组相同的选项,如果把这组选项使用一个变量代表,那么可以把这个变量放在所有引用编译器的地方。当要改变选项的时候,只需改变一次这个变量的内容即可。
Makefile中的变量是用一个文本串在 Makefile中定义的,这个文本串就是变量的值 。 只要在一行的开始写下这个变量的名字,后面跟一个,=,
号,以及要设定这个变量的值即可定义变量,下面是定义变量的语法:
VARNAME=string
使用时,把变量用括号括起来,并在前面加上 $符号,就可以引用变量的值:
${VARNAME}
make解释规则时,VARNAME在等式右端展开为定义它的字符串。
变量一般都在 Makefile的头部定义。按照惯例,所有的 Makefile变量都应该是大写。如果变量的值发生变化,就只需要在一个地方修改,从而简化了 Makefile的维护。
现在利用变量把前面的 Makefile重写一遍:
OBJS=prog.o code.o
CC=gcc
test,${ OBJS }
${ CC } –o test ${ OBJS }
prog.o,prog.c prog.h code.h
${ CC } –c prog.c –o prog.o
code.o,code.c code.h
${ CC } –c code.c –o code.o
clean:
rm –f *.o
除用户自定义的变量外,make还允许使用环境变量、自动变量和预定义变量。使用环境变量的方法很简单,在 make启动时,make读取系统当前已定义的环境变量,并且创建与之同名同值的变量,因此用户可以像在 shell中一样在 Makefile中方便的引用环境变量。需要注意的是,
如果用户在 Makefile中定义了同名的变量,用户自定义变量将覆盖同名的环境变量。此外,Makefile中还有一些预定义变量和自动变量,但是看起来并不像自定义变量那样直观。表 9-3中给出了常见的自动变量。
除了自动变量外,Makefile中还有一些预定义的内部变量,用于定义编译命令名、编译参数等,如表 9-4所示:
4,Makefile的隐含规则在上面的例子中,几个产生目标文件的命令都是从,,c‖的 C语言源文件和相关文件通过编译产生,,o‖目标文件,这也是一般的步骤 。 实际上,make可以使工作更加自动化,也就是说,make知道一些默认的动作,它有一些称作隐含规则的内置的规则,这些规则告诉 make当用户没有完整地给出某些命令的时候,应该怎样执行 。
例如,把生成 prog.o和 code.o的命令从规则中删除,make将会查找隐含规则,然后会找到并执行一个适当的命令 。 由于这些命令会使用一些变量,因此可以通过改变这些变量来定制 make。 象在前面的例子中所定义的那样,make使用变量 CC来定义编译器,并且传递变量 CFLAGS
( 编译器参数 ),CPPFLAGS( C语言预处理器参数 ),TARGET_ARCH
( 目标机器的结构定义 ) 给编译器,然后加上参数 -c,后面跟变量 $<
( 第一个依赖文件名 ),然后是参数 -o加变量 $@( 目标文件名 ) 。 综上所述,一个 C编译的具体命令将会是:
$ {CC} $ {CFLAGS} $ {CPPFLAGS} $ {TARGET_ARCH} –c $< -o $@
在上面的例子中,利用隐含规则,可以简化为:
OBJS=prog.o code.o
CC=gcc
test,${ OBJS }
${ CC } –o $@ $^
prog.o,prog.c prog.h code.h
code.o,code.c code.h
clean:
rm –f *.o
5、常用的 make命令行选项
make命令有丰富的命令行选项。表 9-5中列出了常用的部分。
9.3.3 使用 automake和 autoconf产生 Makefile
在开始使用 Automake和 autoconf之前,请先确认系统已经安装以下的软件:
GNU Automake
GNU Autoconf
GNU m4
Perl
GNU Libtool (如果你需要产生 shared library)
Automake 所产生的 Makefile 除了可以做到程序的编译和连接,也已经把如何产生程序文件的操作,以及把安装程序都考虑进去了,所以源程序所存放的目录架构最好符合 GNU的标准惯例,下面用 hello.c 来作为例子进行说明 。
在工作目录下建立一个新的子目录 devel,再在 devel下建立一个 hello的子目录,这个目录将作为存放 hello 这个程序及其相关文件的地方:
用编辑器写个 hello.c文件:
#include <stdio.h>
int main(int argc,char** argv)
{
printf("Hello,GNU!\n");
return 0;
}
接下来就要使用 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,Automake 会根据 Makefile.am产生一些文件,包含最重要的 Makefile.in:
( 6) 最后执行,/configure,
现在你的目录下已经产生了一个 Makefile文件,执行 make命令就可以开始编译 hello.c 成执行文件,最后执行,/hello:
# make
gcc -DPACKAGE="hello" -DVERSION="1.0" -I,-I,-g -O2 -c hello.c
gcc -g -O2 -o hello hello.o
#,/hello
Hello! GNU!
9.4 调试工具 GDB
9.4.1 GDB调试器简介
Linux系统中包含了 GNU 调试程序 gdb,它是一个用来调试 C和 C++
程序的调试器。可以使程序开发者在程序运行时观察程序的内部结构和内存的使用情况。 gdb 所提供的一些功能如下所示:
运行程序,设置所有的能影响程序运行的参数和环境;
控制程序在指定的条件下停止运行;
当程序停止时,可以检查程序的状态;
修改程序的错误,并重新运行程序;
动态监视程序中变量的值;
可以单步执行代码,观察程序的运行状态。
gdb的功能非常强大,到目前为止,gdb已能够支持 Moduls-2,Chill、
Pascal和 FORTRAN程序的调试,但是调试这些语言的源程序时有一些功能还不能使用。例如调试 FORTRAN程序时还不支持表达式的输入、
输出变量或类 FORTRAN的词法。
gdb程序调试的对象是可执行文件,而不是程序的源代码文件。然而,并不是所有的可执行文件都可以用 gdb调试。如果要让产生的可执行文件可以用来调试,需在执行 gcc指令编译程序时,加上 -g参数,
指定程序在编译时包含调试信息。调试信息包含程序里的每个变量的类型和在可执行文件里的地址映射以及源代码的行号。 gdb 利用这些信息使源代码和机器码相关联。
在命令行上输入 gdb并按回车键就可以运行 gdb了,如果一切正常的话,将启动 gdb,可以在屏幕上看到以下的内容:
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 Free Software Foundation,Inc.
GDB is free software,covered by the GNU General Public License,and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB,Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu".
(gdb)
启动 gdb后,可以在命令行上指定很多的选项 。 输入:
help
可以获得 gdb的帮助信息 。 如果想要了解某个具体命令 ( 比如 break)
的帮助信息,在 gdb提示符下输入下面的命令:
break
屏幕上会显示关于 break的帮助信息 。 从返回的信息可知,break是用于设置断点的命令 。 另一个获得 gdb帮助的方法是浏览 gdb的手册页 。
在 Linux Shell提示符输入:
man gdb
可以看到 man的手册页。
9.4.2 GDB命令的基本使用和应用
1,gdb基本命令还可以用下面的方式来运行 gdb:
gdb filename
其中,filename是要调试的可执行文件 。 用这种方式运行 gdb可以直接指定想要调试的程序 。 这和启动 gdb后执行 file filename命令效果完全一样 。 也可以用 gdb去检查一个因程序异常终止而产生的 core文件,或者与一个正在运行的程序相连 。
gdb支持很多的命令且能实现不同的功能。这些命令从简单的文件装入到允许你检查所调用的堆栈内容的复杂命令,下面列出了在使用
gdb 调试时会用到的一些命令。
1) file命令:装入想要调试的可执行文件。
2) cd命令:改变工作目录。
3) pwd命令:返回当前工作日录。
4) run命令:执行当前被调试的程序。
5) kill命令:停止正在调试的应用程序。
6) list命令:列出正在调试的应用程序的源代码。
7) break命令:设置断点。
8) Tbreak命令;设置临时断点。它的语法与 break相同。区别在于用
tbreak设置的断点执行一次之后立即消失。
9) watch命令:设置监视点,监视表达式的变化。
10) awatch命令:设置读写监视点。当要监视的表达式被读或写时将应用程序挂起。它的语法与 watch命令相同。
11) rwatch命令:设置读监视点,当监视表达式被读时将程序挂起,
等侍调试。此命令的语法与 watch相同。
12) next命令:执行下一条源代码,但是不进入函数内部。也就是说,
将一条函数调用作为一条语句执行。执行这个命令的前提是已经 run,
开始了代码的执行。
13) step命令:执行下一条源代码,进入函数内部。如果调用了某个函数,会跳到函数所在的代码中等候一步步执行。执行这个命令的前提是已经用 run开始执行代码。
14) display命令:在应用程序每次停止运行时显示表达式的值。
15) info break命令:显示当前断点列表,包括每个断点到达的次数。
16) info files命令:显示调试文件的信息。
17) info func命令:显示所有的函数名。
18) info local命令:显示当前函数的所有局部变量的信息。
19) info prog命令:显示调试程序的执行状态。
20) print命令;显示表达式的值。
21) delete命令:删除断点。指定一个断点号码,则删除指定断点。
不指定参数则删除所有的断点。
22) Shell命令:执行 Linux Shell命令。
23) make命令:不退出 gdb而重新编译生成可执行文件。
24) Quit命令:退出 gdb。
2,gdb应用实例下面使用 gdb调试程序来调试一个实例。被调试的程序相当的简单,但展示了 gdb的典型应用。下面列出了将被调试的程序代码,这个程序被称为 greeting.c,功能是显示一个简单的问候,再用反序将它列出。
9.5 使用 RCS/CVS来管理源代码
9.5.1 RCS的使用
RCS( Revision Control System) 即程序改版控制系统,主要功能是用来管理文件的版本,可以节省空间和时间 。 这样就不需要在每个程序开发到某一个阶段就将数据拷贝到其他的地方备份起来了 。
RCS提供了如下几个最重要的指令的:
ci指令:将文件放入 RCS目录下的控制系统
co指令:从 RCS目录下将文件取出
rcs指令:用来对 RCS文件进行参数的设置
1.基本操作方式一般而言,RCS所产生出来的文件会放在 RCS目录中 。 所以第一步必须要在当前的目录下制作一个文件:
[root@wyh linux]# mkdir RCS
接下来只要使用 ci指令 。 就可以把文件备份到 RCS改版控制系统中:
[root@wyh linux]# ci test.c
若要将文件取出,可以使用下列指令:
[root@wyh linux]# co test.c
取出来的文件是只读文件,若要取出可以写入的工作文件,可以加上
-l参数来锁定它:
[root@wyh linux]# co -l test.c
此外将文件放入 RCS控制系统时,可以使用 -l参数锁定文件,那么目录下的文件依然存在:
[root@wyh linux]# ci -l test.c
若要比较当前的文件和 RCS中最新版本的文件,可以使用下列指令:
[root@wyh linux]# rcsdiff test.c
2.指定版本若不指定版本编号时,co会从 RCS取得最新的版本 。 如果要以特定的版本号码写入 RCS或读出,可以使用 -r参数选项 。
[root@wyh linux]# ci -l -r3.25 test.c
<--以 3.25作为版本编号
[root@wyh linux]# co -l -r1.2 test.c
<--将 RCS中 1.2版的 test.c读出此外,rcsdiff也可以用来指定任何一个版本和当前程序代码进行比较 。
[root@wyh linux]# rcsdiff -r3.25 test.c
<--取出 3.25版与 test.c进行比较
3.关键词的使用在 RCS中可以将关键词变量放入程序代码中 。 这些变量经过 RCS会变成版本的注解 。 用户可以将这些关键词说明当作是程序中的批注 。
常用的关键词如下:
$Author$:将版本放入 RCS的用户名称 。
$Data$:记录程序代码放入 RCS时的日期和时间 。
$Header$:记录文件的标头,包括 RCS路径名称,版本号码,日期,
作者等 。
$ID$:和 $Header$相同,但不包括 RCS路径名称 。
$Locker$:记录锁定本版本的用户名称 。
$Log$:记录将 RCS锁住的时间,所输入的文本语句 。
$RCSfiles$:记录 RCS文件名称 。
$Rivision$:指定版本号码 。
$Source$,RCS文件名称,包括其路径 。
$State$:使用 -s选项所指定的特殊状态 。
使用关键词的步骤如下所示:
( 1) 在程序代码中加入任一关键词
[root@wyh linux]#vi test.c
( 2) 将程序代码放入 RCS版本控制系统
[root@wyh linux]# ci -l test.c
( 3) 将文件再次取出 。 在取出的过程中,co会将每个关键词展开成其对应的值
[root@wyh linux]# co -l test.c
[root@wyh linux]# cat test.c
9.5.2 CVS的使用
CVS( Concurrent Version System) 是个版本控制系统,利用该系统可以记录源代码文件的历史 。 例如,当软件修改时会产生 Bug,并且可能在做这次修改后很长时间不会发现这些问题 。 使用 CVS就可以容易地回顾老的代码版本去发现哪一次的修改导致这些问题 。
如果 CVS保留每一次的代码版本,会浪费很多的空间 。 因此 CVS使用一种比较聪明的办法保存多个版本在一个文件中 。 它仅仅保留版本间的不同内容 。 如果很多人在同一个项目上工作,则 CVS使用让不同开发者独立工作的方式解决了这个问题 。 每一个开发者的工作都在他自己的目录内,并且 CVS将在每个开发者的工作完成后进行合并工作 。
在 Linux下,CVS的使用一般是以命令行方式 。 通常,CVS有两种使用方式,一是本机方式,一是远程执行方式 。 CVS的命令格式是:
cvs [cvs的选项 ] cvs的动作 [选项 ]
读者可以用 cvs –H command列出命令 command的使用方法。
1.开始项目用 CVS管理代码,首先要创建一个,信息仓库,。,信息仓库,简单来说包含一个目录结构。它包括要管理的源代码和用于管理源代码的各种管理文件。
先设置环境变量 CVSROOT,指向信息仓库的绝对路径,然后调用
CVS的 init命令:
# CVSROOT=/usr/local/cvsroot;export CVSROOT
# cvs init
# ls -l $CVSROOT
2.添加项目的文件、目录到信息仓库要将需要管理的项目的文件加入到信息仓库,并做上标志。如果从头开始一个新的项目,就需要创建一个单独的目录,并把所有要使用的文件做一个有效的组织。而如果在开始使用源文件之前该目录就已经存在,则只需进入该目录就行了。
然后,就可以输入源文件目录:
# cvs import -m "Create Source Dir" cvstest/c wu cvstest
这样会生成 $CVSROOT/cvstest/c 目录。 其中 -m用来指定注释信息,
如果后面在命令行不指定注释信息,则会启动缺省编辑器( vi)要求输入注释信息; cvstest/c是项目名称(实际上是仓库名,在 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/modules
中加以指定,
① # cvs checkout CVSROOT/modules
② 在 modules文件中加入下面一行:
SOURCE cvstest/c
③ 然后执行:
# cvs commit –m ―Add SOURCE‖
以后就可以使用下面的命令在当前路径下生成 cvstest/c目录
# cvs checkout SOURCE
在当前路径下生成的这个目录就被称为工作目录,对源文件的所有修改都应该在这个目录下完成,而绝对不允许去改动在文件仓库中
$CVSROOT 目录下的文件 。
( 2) 删除,增加,重命名文件和目录
cvs add [-k kflags][-m message] files...
-k 指定以后该文件的缺省导出目录
-m 对文件的描述上述命令会加入一个新的文件到文件仓库里,但直到使用了提交命令它才会真正更新文件仓库 。
cvs remove [options] files
上述命令会从文件仓库中删除文件,但也要到提交之后才有作用。
( 3) 提交源文件
cvs commit [-Rl][-m mesg] files
-R 连子目录一起提交
-l 只提交本地目录 ( 不提交子目录 )
-m 注释信息在导出源文件之后,在工作目录中对源文件进行的所有修改都必须在提交之后才能对文件仓库中的源文件起作用,并且新的文件才能够被分配一个新的版本号 。
( 4) 释放工作目录
cvs release –d SOURCE
这个命令会删除工作目录 cvstest/c(建议在提交了修改的模块后执行这一步),比使用 rm –rf cvstest 要好。
4.多用户开发在多用户的情况下,如果不同用户修改的是同一个文件的不同部分,
则使用下面的命令就能进行版本合并 ( 把检出的文件与当前的最新版本合并 ),
# cvs update
( 1) 冲突解决在有多个用户对同一个文件进行修改时,如果修改了其中的相同部分,而修改后的内容如果有不同的话,出现冲突是不可避免的。
如果在 CVS 文件仓库中有一个文件 test.c,它的版本是 1.4,用户 A 先检出该文件进行修改,而稍后有用户 B检出该文件进行修改,并提前提交成 1.5,而在用户 A再提交时就会出现冲突(如果文件内容不同的话),这时 CVS会提示需要手工解决。
( 2) 文件版本管理
cvs log [-lR][-r rev][-d date][-w login][files… ]
-l 不处理子目录
-R 对子目录做同样处理
-r 指定版本号
-d 指定时间
-w 指定登录名使用上面的命令可以参看当前模块或指定文件的所有历史版本信息 。
cvs annotate [-lR][-r rev|-D date] files
-l 不处理子目录
-R 对子目录做同样处理
-r 指定版本号使用上面的命令可以参看指定文件(检出之后)的所有修改信息。
使用下面的命令可以生成相对于一个指定主版本的分支版本:
cvs rtag –b –r rev_root rev_branch file_name
-b 指定生成一个分支版本
-r 指定该分支的主干节点版本号
rev_root 主干版本号
rev_branch 分支版本号
file_name 指定文件,使用,,‖表示当前目录下所有文件使用上面的命令可以生成一个对应版本号的分支版本,由于 CVS 版本号是用数字表示的,而且在同一个模块下不同文件的版本完全可能是不同的,所以使用标识会更方便。
例:
# cvs rtag –b –r 1.2 tlb-1 SOURCE
以后要访问该分支版本,可以使用,-r‖选项
# cvs checkout –r tlb-1 SOURCE
从当前检出的版本切换到一个分支版本:
# cvs update –r tlb-1 SOURCE
使用下面的命令可以看版本信息:
cvs status [–vlR] files
-v 显示所有信息
-l 不显示子目录信息
-R 显示子目录信息
cvs update –j rev module
把当前所做的修改与指定版本的文件进行合并。
如果在不同版本之间模块的文件有增减,则可以:
# cvs update –A
# cvs updata –jbranch_name
5.在远程机器上使用 CVS
通过网络使用 CVS 有很多种方式,但在这里只介绍比较简单的一种:
通过 rsh 执行 cvs 命令 。
1) 在远程机器的,rhosts中加入对本地机的访问许可:
tom huang
2) 使用下面的命令检出模块 ESMSTRG
# cvs –d,ext:huang@wyhlinux:/work/cvsroot checkout SOURCE
其中,ext 指明了连接方式为 rsh,huang指明了本地用户,wyhlinux
指明了远地主机,/work/cvsroot 指明了在远地主机上的 $CVSROOT路径,可以在本地设置 CVS_SERVER环境变量指明这个目录。
9.6 将软件打包
9.6.1 RMP简介
RPM是 Redhat Package Manager的缩写,是由 Red Hat公司开发的软件包安装和管理程序,同 Windows平台上的 Uninstaller和 Cleansweep比较类似。使用 RPM,用户可以自行安装和管理 Linux上的应用程序和系统工具。
RPM可以让用户直接以 binary方式安装软件包,并且可替用户查询是否已经安装了有关的库文件;在用 RPM删除程序时,会询问用户是否要删除有关的程序 。 如果使用 RPM来升级软件,RPM会保留原先的配置文件,
这样用户就不用重新配置新的软件了 。 RPM保留一个数据库,这个数据库中包含了所有的软件包的资料,通过这个数据库,用户可以进行软件包的查询 。 RPM虽然是为 Linux而设计的,但是已经移值到 SunOS、
Solaris,AIX,Irix等其它 UNIX系统上了 。 RPM遵循 GPL版权协议,用户可以在符合 GPL协议的条件下自由使用及传播 RPM。
RPM设计的目的有下面几点:
方便的升级功能,RPM让用户不用重新安装整个系统就可以对单个软件包进行升级,当一个新的发行版本问世的时侯,用户也不用重新安装,RPM会替用户全面、自动、智能地升级系统,并且保留用户原先的配置文件,这就大大减少了用户维护系统的工作量。
强大的查询功能:用户可以针对整个软件包的数据,或是某些特定的文件进行查询,也可以轻松地查出其个文件是属于哪个软件包,或是从哪里来的。 RPM文件本身是经过压缩的,但用户还是可以很容易地快速查询每个软件包的内容,因为在 RPM软件包里,已经加入一些特殊的 binary header,记录了全部查询时所需要的数据,这一点大大加快了查询速度。
系统校验:当用户不小心删除了某个重要的文件,但是又不知道是哪些软件包需要这个文件,这时侯就可以用 RPM来查询已经安装的软体包中缺了哪些文件,是需否要重新安装。并且用户可以校验出安装的软件包是否已经被别人更改过。
允许用户能够使用,纯净,的源代码:让用户取得,未经处理过的源代码,,同时再附上一份,补丁,程序,用户可凭借这些来完成程序编译工作。这样的做法带来不少好处。例如,如果某个程序的新版本问世了,用户可能没有必要再重头开始做全部的编译工作,先观察,补丁,程序的内容,看看有哪些部分是用户需要做的。这样就能让用户更清楚地知道新版本有哪些改进的地方。
下面简单介绍一下 RPM的使用。
1.用 RPM安装软件包最简单的安装命令如下:
# rpm -ivh xwpe-1.5.29a-1.i386.rpm
RPM会输出该软件包的名称,并且显示一个状态条 。 安装软件很简单,
但是有时会给出一些出错消息:
Package Alrealy Installed!
表示该软件包已经安装,也可使用 --replcepkgs选项强制 RPM重新安装这个软件 。
Conflicting files
表 示 该 软 件 包包 含 某 些其 他 软 件包 安 装 过的 文 件,可加 —
replacepkgs选项覆盖原先的文件 。
Unresolved Dependency
表示正确运行该软件需要其他哪些软件包,因为 RPM的软件包能够查询该软件的,依赖,关系,所以 RPM在安装之前会先查询,如果系统没有安装需要的软件包,就会出现错误提示信息 。
但是,如要继续安装,就必须先安装相应的软件包,或使用 -nodeps
选项强制安装,但不推荐使用,因为这样安装的结果一般不能运行。
2.用 RPM删除安装软件包要删除已安装的软件包,只需要执行下面的命令:
# rpm -e xwpe
3.用 RPM升级软件升级软件类似于安装软件,执行如下命令:
# rpm -Uvh xwpe-1.5.31a-1.i386.rpm
RPM会自动删除相应软件包的老版本,如果配置文件同新版本不兼容,则会自动将其保存为另一 oldconfig.rpmsave文件。这样,用户就可以自已手工去更改相应的配置文件。
4.查询软件包用户可以用 RPM –q来在 RPM的数据库中查询相应的软件,RPM会给出软件包的名称,版本和发布版本号,例如:
# rpm -q xwpe
xwpe-1.5.29a-1
5.用 RPM校验软件包用户可以用 RPM来校验已经安装的软件包,RPM可以校验文件大小,MD5校验码、文件权限、类型及属主等信息。
6.实际使用技巧
1) 可以通过 FTP来安装软件包 。 如果用户能够连上网络,想安装某个新的软件包时,可以直接用它的 URL地址来安装,例如:
# rpm –I ftp://ftp.whpu.edu.cn/linux/xwpe-X11-1.5.29a-1.i386.rpm
也可以用网络来查询 。
2) 假如用户不小心误删了几个文件,但不确定到底是哪些文件,通对整个系统进行校验,以了解哪些部份可能己经损坏,应这样做:
# rpm –Va
3) 如果用户碰到一个认不出来的文件,想要知道是属于哪一个软件包的话,可以这样做:
# rpm –qf /usr/bin/cdplay
4) 如果用户得到一个新的 RPM文件,却不清楚它的内容,可以这样做:
# rpm -qpi /xwpe-X11-1.5.29a-1.i386.rpm
5) 如果用户想了解某个 RPM软件包会在系统里安装哪些文件,可以这样做:
# rpm -qpl /xwpe-X11-1.5.29a-1.i386.rpm
9.6.2 制作 RMP
RPM目前最新的版本 4.2同以前的 3.x系列是不兼容的 。 一般来说,为
RedHat6.2,Mandrake,TurboLinux,Suse打包就需要 3.x系列,而为 RedHat7.0以上的版本,打包就需要 4.0以上的 RPM版本 。 具体软件可以到 www.rpm.org下载 。
制作 RPM的基本步骤如下:
1) 取回软件 source code和相关的 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/redhat/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.编辑 macros
RPM的配置文件中 /usr/lib/rpm/macros是一个全局的配置文件,可以在其中设定一些全局变量,如:
% distribution 发行版本名
% packager 打包者姓名和 email
% vendor 软件包作者除了上述的 macro设定外,还有许多其他的设定方式,可以使用下面的命令来查看系统的 tag与可供使用的 flag有哪些:
# rpm –showrc
3.编辑 spec文件制作一个软件包时,需要使用到 spec文件,其内容为该程序软件包的说明,还包括一些指令,用以执行整个 build的过程,还有一份文件列表,用以表示程序软件包中的文件,分别被安装在里面 。
spec文件的命名方式,最好是遵循一个通用的格式,其格式应该为
,软件包名版本号,spec‖。
4.编译 RPM
在 SPEC文件编译完成后,再开始编译工作:
# rpm –ba helloworld-1.0-1.spec
编译完成后一定要进行测试工作,最好是在另外一台干净的系统上进行测试安装和卸载过程。如果一切无误那么就可以发布这个 RPM包了。
本章小结
Linux编程风格
Linux下 IDE的使用
Linux下使用 GNU cc开发应用程序
Linux程序的调试
Linux下使用 RCS/CVS来管理源程序
Linux下软件打包
9.1 概述
9.1.1 Linux编程
Linux软件开发一直在 Internet环境下讲行 。 这个环境是全球性的,编程人员来自世界各地 。 只要能够访问 Web
站点,就可以启动一个以 Linux为基础的软件项目 。
Linux开发工作经常是在 Linux用户决定共同完成一个项目时开始的 。 当开发工作完成后,该软件就被放到
Internet站点上,任何用户都可以访问和下载它 。 由于这个活跃的开发环境,新的以 Linux为基础的软件功能日益强大,而且呈现爆炸式的增长态势 。
大多数 Linux软件是经过自由软件基金会( Free
Software Foundation)提供的 GNU( GNU 即 GNU’s not
UNIX)公开认证授权的,因而通常被称作 GNU软件。 GNU
软件免费提供给用户使用,并被证明是非常可靠和高效的。许多流行的 Linux实用程序如 C编译器,shell和编辑器都是 GNU软件应用程序。
Linux程序需要首先转化为低级机器语言即所谓的二进制代码以后,才能被操作系统执行。例如编程时,先用普通的编程语言生成一系列指令,这些指令可被翻译为适当的可执行应用程序的二进制代码。这个翻译过程可由解释器一步步来完成,或者也可以立即由编译器明确地完成。
shell编程语言如 BASH,TCSH,GAWK,Perl,Tcl和 Tk都利用自己的解释器。用这些语言编制的程序尽管是应用程序文件,但可以直接运行。编译器则不同,它将生成一个独立的二进制代码文件然后才可以运行。
9.1.2 Linux编程风格
( 1)函数返回类型说明和函数名分两行放置,函数起始字符和函数开头左花括号放到最左边。
( 2)尽量不要让两个不同优先级的操作符出现在相同的对齐方式中,
应该附加额外的括号使得代码缩进可以表示出嵌套。
( 3)按照如下方式排版 do-while语句:
( 4)每个程序都应该以一段简短的说明其功能的注释开头。
( 5)请为每个函数书写注释,说明函数是做什么的,需要哪些入口参数,参数可能值的含义和用途。如果用了非常见的、非标准的东西,
或者可能导致函数不能工作的任何可能的值,应该进行特殊说明。如果存在重要的返回值,也需要说明。
( 6)不要声明多个变量时跨行,每一行都以一个新的声明开头。
( 7)当一个 if中嵌套了另一个 if-else时,应用花括号把 if-else括起来。
( 8)要在同一个声明中同时说明结构标识和变量或者结构标识和类型定义 (typedef)。先定义变量,再使用。
1,GNU风格
( 9)尽量避免在 if的条件中进行赋值。
( 10)请在名字中使用下划线以分割单词,尽量使用小写;把大写字母留给宏和枚举常量,以及根据统一惯例使用的前缀。例如,应该使用类似 ignore_space_change_flag的名字;不要使用类似
iCantReadThis的名字。
( 11)用于表明一个命令行选项是否给出的变量应该在选项含义的说明之后,而不是选项字符之后被命名。
2,Linux 内核编程风格
( 1) Linux内核缩进风格是 8个字符。
( 2) Linux内核风格采用 K&R标准,将开始的大括号放在一行的最后,
而将结束的大括号放在一行的第一位。
( 3)命名尽量简洁。不应该使用诸如
ThisVariableIsATemporaryCounter之类的名字。应该命名为 tmp,这样容易书写,也不难理解。但是命名全局变量,就应该用描述性命名方式,例如应该命名,count_active_users()‖,而不是,cntusr()‖。
本地变量应该避免过长。
( 4)函数最好短小精悍,一般来说不要让函数的参数多于10个,
否则应该尝试分解这个过于复杂的函数。
( 5)通常情况,注释说明代码的功能,而不是其实现原理。避免把注释插到函数体内,而写到函数前面,说明其功能,如果这个函数的确很复杂,其中需要有部分注释,可以写些简短的注释来说明那些重要的部分,但是不能过多。
9.2 IDE使用
9.2.1 VIM编辑器
1,VIM的简介
VI是 Linux世界里最常用的全屏编辑器,所有的 Linux机器都提供该编辑器,而 Linux里提供的是 VI的加强版 ——VIM,但同 VI是完全兼容。 VI的原意是,visual interface‖,即可视编辑器,用户键入的内容会立即被显示出来、而且其强大的编辑功能可以同任何一种最新的编辑器相媲美。它在 Linux上的地位就仿佛 Edit程序在 DOS上一样。它可以执行输出、删除、
查找、替换、块操作等众多文本操作,而且用户可以根据需要对其进行定制,这是其他编辑程序所没有的。 VI不是一个排版程序,不象 Word或
WPS那样可以对字体、格式、段落等其他属性进行编排,它只是一个文本编辑程序。
2,VIM 的基本观念
VIM有三种操作方式,分别是:
命令方式
插入方式
命令行方式
3,VIM的进入与离开在系统提示符,$‖下键入命令 VIM,后面跟上想要编辑(或者建立)
的文件名,VIM 可以自动载入所要编辑的文件或是开启一个新文件。
VIM的退出,可以在命令行方式下使用命令,,wq‖或者,,q!,,
前者的功能是写文件并从 VIM中退出,后者的功能是从 VIM中退出,但不保存所作的修改(注意冒号)。
4,VIM的命令方式
( 1)光标移动要对正文内容进行修改,必须先把光标移动到要修改的内容所在的位置,用户除了通过按键盘的上、下、左、右箭头键来移动光标,还可以利用 VIM提供的众多字符组合键,在正文中移动光标,
迅速达到指定的行或列,实现定位,常用的快捷键有:表 9-1
( 2)替换和删除将光标定位于文档中指定位置后,可以用其他字符替换光标所指向的字符,或从当前光标位置删除一个或多个字符,常用命令有:
表 9-2
( 3)粘贴和复制在 VIM编辑器中,与 Windows系统不同的是从正文中删除的内容
(如字符、字段或行)并没有真正丢失,而是被剪贴并复制到了一个内存缓冲区中,用户可将其粘贴到正文中的任意位置,完成这一操作的命令是:表 9-3
( 4)查找字符串为了方便文档的编辑,VIM提供了强大的字符串查找功能,要查找文件中指定字符或字段出现的位置,可以用该功能直接进行搜索,
搜索方法是:在命令行键入字符,/”,后面加上要搜索的字符串,
然后按回车键,编辑程序将执行正向搜索(从光标所在的位置向文件末尾方向),并在找到指定字符串后,将光标停在该字符串的开头;键入 n命令可以继续执行搜索,找出这一字符串下次出现的位置,
用字符,?”取代,/”,可以实现反向搜索(从光标所在的位置向文件开头方向),举例说明如下:表 9-5
( 5)撤销和重复在编辑文档的过程中,可以取消错误的编辑命令造成的后果,
另外,如果用户希望在新的光标位置再次执行先前的编辑命令,可用重复命令。表 9-6
5,VIM的插入方式
( 1)进入插入方式在命令方式下正确定位光标之后,可用一下命令切换到插入方式:表 9-7
如果用户想利用已有的文件内容,可以使用命令,,i
filename‖,则 VIM将指定文件的内容输入当前光标的下一行,且
VIM仍处于命令方式。
( 2)退出插入方式退出插入方式的方法是,按 ESC键或组合键 Ctrl+I
( 3)正文替换除了几种简单的切换到插入方式的方法外,还有一些命令允许用户在插入模式之前首先删去一段文字,从而实现正文的替换,这些命令包括:表 9-8
6,VIM的命令方式
( 1)行号与文件编辑中文档的每一行正文都有隐藏的行号,用下列命令可以移动光标到指定行:表 9-9
在命令方式下,用户可以规定命令操作的行号范围,数值用来指定绝对行号;字符,,,表示光标所在行的行号;字符,$‖表示正文最后一行的行号,示例如下:表 9-10
在命令方式下,用户还可以对文件进行操作,允许从文件中读取正文,或将正文写入文件,常用命令如下:表 9-11
9.2.1 VIM编辑器
1,emacs的简介
emacs文本编辑器可以用来编辑文本,剪辑和粘贴文本内容,提供个人日历和日记,阅读 Usenet新闻,发送电子邮件,同时还是一种程序语言解释器,可以编辑 C,Lisp,Tev源代码文件,以及 Linux的
Shell。
emacs是由 Richard Stallman发明的,这位发明者还创建了自由软件基金会( Free Software Foundation,简称 FSF)。最初的 emacs
是用来编辑宏命令的,现已进一步扩充为 UNIX用户中装机用户数量最大、功能最齐全的免费文本编辑器了。
emacs同 VI不一样,没有编辑状态和指令状态之分,其最重要的概念是其独特的缓冲区,emacs编辑的所有文件都是放在缓冲区中的,emacs支持同时编辑多个缓冲区,可以将一个文件在多个缓冲区中打开不同的拷贝,
甚至其所有的在线帮助和文档以及出错信息都是作为一个缓冲区来显示的,当然这些缓冲区是不可写的,用户可以在这些缓冲区之间拷贝和粘贴文本 。 并且一般所有的缓冲区在硬盘上都有一个以,#‖开头的备份文件,这样在系统突然崩溃的时候可以即时将用户的工作进行备份 。
在编辑文件时,如果用户在编辑一些特殊类型的文件,例如当用户编辑扩展名为,c的 C语言文件时,emacs会产生菜单选项 c,向用户提供一些针对编辑 c程序特别有用的一些命令 。 当用户编辑扩展名为,txt的文件则会多出菜单选项 tex,让用户在编辑完 tex文件后可以即时地观看输出并打印 。 首先介绍一下几个常见的键盘操作符号的意义:所有的 emacs的操作键都是由 Control键 ( 一般是键盘上的 Ctrl键 ) 和 META键 ( 一般是键盘上的 Alt键 ) 加上一些键的组合组成的,如果没有 Alt键,则可以用输入一个 Esc,再输入相应的键来代替 。 例如:
C-x:表示同时按住 Ctrl键和 x键 。
C x:表示先按住 Ctrl键,然后释放它,再按下 x键 。
M-x:表示同时按住 Alt键和 x键 。
M x:表示先按住 Alt键,释放它,再按下 x键 。
2,emacs的启动和退出
emacs可以用两种方法启动 。 第一种启动 emacs的方法是不装载任何文本文件启动 emacs,输入以下命令行:
# emacs
在屏幕上会出现无任何文本 emacs编辑窗口,如图 9-2所示。
如果用户是初学者,最好的学习方法是:按下 Ctrl-h键(即按住 Ctrl
键后不放,再按下 h字母键),就会自动进入 emacs的联机帮助,在屏幕底部 emacs命令行中会出现一个提示符,这时再按下字母键 t和回车键,便进入了简捷有效的 emacs文本编辑器的教程。参照此教程的步骤,用户将对如何使用 emacs有个概括的了解。
第二种启动 emacs的方法是通过装载某一个文本文件启动 emacs,输入以下命令行:
# emacs filename
如果装载的文件不在当前目录时必须输入该文件名的全称 ( 包括所在目录 ) 。 例如,当前目录下有一个文本文件 myfile.txt,用 emacs
对其编辑时,输入命令行启动 emacs:
# emacs myfile.txt
屏幕上将出现如图 9-3所示的 emacs编辑窗口。
3,emacs的基本操作
( 1) 光标的移动下面列出 emacs中的光标的移动情况及其键盘操作:
M-b:光标移动到光标左边的单词的开始处 。
M-f:光标移动到光标右边的单词的开始处 。
M-a:光标移动到当前句子的开始处 。
M-e:光标移动到当前句子的结束处 。
C-n:光标移动到下一行 。
C-p:光标移动到上一行 。
C-a:光标移动到行首 。
C-e:光标移动到行尾 。
M->:光标移动到文件尾 。
M-<:光标移动到文件头 。
( 2) 文本的操作
① 插入文本的操作
② 删除文本的操作
③ 取消操作
④ 粘贴操作
⑤ 查找和替换
( 3) 文件的操作
C-x C-f:在屏幕底部出列,Findfile,/_‖等待用尸输入文件名,如输入,/myfile.txt‖则提示( newfile),清屏后光标出现在左上角,等待用户输入文本的内容。
C-x C-s:当将文本输入完毕后选择存盘操作,屏幕底部提示出文本所在的目录及文件名,/myfile.txt‖,指示出该文件存放在磁盘何处。
C-x C-w:当对一个原有的文本文件继续编辑或修改后;需将改变后的文件重新保存。这时 emacs会提示,/myfile.txt‖exists;
overwrite?( y or n) _当回答,y‖后,提示信息,/myfile.txt‖
(重写该文件)。
C-x C-c:当确定结束对 emacs编辑器的使用,可选择 File菜单中的 Exitemacs选项退出 emacs。如果没有对输入或修改的内容存盘,
emacs会提醒用户别忘记做保存文件操作。
( 4)窗口的操作窗口就是屏幕区域,用户可以使用多个窗口来对一个缓冲区的不同部分进行操作,或对不同的缓冲区进行操作 。
当用户使用 C-x C-f来打开一个文件的时候,emacs将会创建一个缓冲区,用户在其中进行编辑操作。 emacs允许同时对多个缓冲区中的文本进行编辑,比如在缓冲区互相粘贴、剪辑等等。用户还可以直接输入快捷键( C-x C-b)查看所选择的是哪个缓冲区,如图 9-4所示。
用户可以使用两种方法在当前窗口的不同缓冲区间进行切换:
( 1) 使用 Buffers菜单,它包括当前时刻打开的所有的缓冲区,在其中选择,就能切换到想要编辑的文件 。
( 2)使用键盘对缓冲区进行操作,键入 C-x b命令,然后按下 RET
( RET,即键盘上的回车键 Enter。任何一个命令输入完毕时,必需紧跟着一个 Enter,它的作用是用来告诉系统,命令输入已经结束,可以开始执行相关的动作了),就能立刻切换到位于当前编辑缓冲区的前一个缓冲区,或按 Tab键,得到一个缓冲区的列表,然后输入需使用的缓冲区的名字(也可以用鼠标单击名字)。要关闭一个缓冲区,先切换到该缓冲区,键入 C-x k,最后按下回车键。
4.在 emacs中执行 Shell
在 emacs中有两种执行 shell的方法:一种是进入 shell command mode,
另一种是进入 shell mode。 二者都可以执行 shell,其最大不同之处是,
进入 shell mode的状态,执行 shell的同时,仍可以切换到其他模式处理别的工作,但如果使用 shell command mode,就必须等 shell执行完后才可以做其他的事 。
使用 shell command mode时,使用者在屏幕的最下方输入要执行的 shell
命令,emacs会开启一个名为,*shell command mode*‖的窗口,将 Shell
命令执行的结果显示在此窗口中 。 shell mode则是执行一个子 shell,其输入与输出都是通过同一个缓冲区,所以输入与输出是在同一个地方,它不像 shell command mode,命令输入与结果的显示在不同的地方 。
1) shell command mode
ESC-!( shell-command) 启动 shell command mode
ESC- ( shell-command-on-region)
2) shell mode
ESC-x shell\index ESC-x shell是启动 shell mode的命令
5.用 emacs进行程序的编辑、编译与测试
emacs是一个综合的环境,在提供程序编辑的同时,自然会提供一个可供程序执行的环境 。 以下就要谈谈 emacs可以为程序开发者提供那些服务 。 emacs针对不同的语言提供不同的编译摸式 。 emacs提供的服务有程序缩进的安排,括号对应的提示,程序注解的安排,光标移动的方式与程序的删除等等 。 基本上,emacs是提供一个编写程序的格式,只是此格式可根据使用者的需要而自行设计 。 emacs选择适合的语言模式,是根据所编辑的文件名称扩展名来判断的 。 像上面提到的那样,如果用户编辑扩展名为,c的 C语言程序,emacs会自动给予 C语言模式,而不需使用者自行处理 。 emacs提供的程序语言模式有 LISP,SCHEME,C,C++、
FORTRAN,MAKEFILE,AWK,PERL,ICON与 MUDDLE等 。
编辑好的程序可以直接进入 emacs的编译模式,不需离开 emacs到 Linux的
shell下进行编译 。 进入 emacs的编译模式很简单,只要输入,ESC –x
compile‖即可 。 emacs缺省的编译命今是 make,执行 ESC –x compile命令的结果如下所示:
compile command,make -k
如果要使用其他的编译器,只需在,compile command:,的后面加上对应的的编译命令即可,此命令与在 Linux shell下使用编译的方法完全相同。
除了编辑,编译之外,程序开发者还需要的功能是调试器,emacs
也提供了在编辑器内部调试程序的功能 。 emacs提供了四种调试器,
分别为 gdb,dbx,xdb与 sdb,使用者可根据需要来选择合适的调试器 。 下面是使用调试器的命令:
ESC–X gdb RET file RET
ESC–X dbx RET file RET
ESC–X xdb RET file RET
ESC –X sdb RET file RET
9.2.3 使用 Kdevelop开发 C程序
1,Kdevelop的简介
Kdevelop是一套功能强大的集成开发环境,其整合了开发程序所需的编译器、连接器、除错工具、版本控制工具等,可以用
Kdevelop快速地建立各式各样的应用程序,包括:
KDE程序 ;GNOME;Qt程序 ;终端程序 ;其它
2.启动 Kdevelop
如果是第一次使用 Kdevelop,Kdevelop会先启动,Kdevelop设置,进行 Kdevelop的环境设定,共需要完成 9个步骤的设置工作,如图 9-5所示。
单击,Kdevelop设置,欢迎画面对话框中的 【 下一步 】 按钮开始进行 Kdevelop的设定。,Kdevelop设置,的第二项设定为,选择语法高亮风格,,这里选择缺省的,Kdevelop 2.0 风格,,如图 9-6所示。
图 9-5 Kdevelop设置 图 9-6 选择语法高亮风格选择喜欢的语法高亮表示风格后,单击 【 下一步 】 按钮进入,用户交换界面模式,的选择窗口,如图 9-7所示。
同样选择好用户交换界面模式后,单击 【 下一步 】 按钮进入
,Kdevelop中所使用的工具检测窗口,,如图 9-8所示。
图 9-7 选择用户交换界面模式 图 9-8 Kdevelop所使用的工具检测窗口单击,工具程序检测窗口,对话框中的 【 下一步 】 按钮,进行下一个步骤。下一个步骤为,寻找 Qt文档,,并设定文件路径,一般而言这个步骤应该会成功完成,将见到如图 9-9所示对话框。
单击,寻找 Qt文档,对话框之中的 【 下一步 】 按钮,进入下一个步骤。下一个步骤为,寻找 KDE程序库文件,,同样,一般而言这个步骤也应该会成功完成。再缺省完成两步以后,Kdevelop设置将显示
,安装过程成功完成,的对话框,如图 9-10所示,此为 Kdevelop设置的最后步骤,单击 【 下一步 】 按钮,稍待一会 Kdevelop便会启动。
图 9-9 Qt文档查找 图 9-10 安装过程成功完成窗口
3.新建一个新项目在 Kdevelop中开发 C程序,需要用创建项目的方式进行。请执行
,项目 /新建,命令,打开应用程序向导对话框,创建过程如图 9-11
所示。
选择要创建程序的种类,这里选择 C程序,然后单击 【 下一步 】 按钮进行下一步骤。下一个步骤为关于项目资讯的设定,如图 9-12所示,
在此输入项目的名称、目录、版本号码、作者姓名、以及作者电子邮件地址。
图 9-11 选择创建程序的种类 图 9-12 设定项目信息单击 【 创建 】 按钮开始产生项目档案,结果如图 9-13所示,此处可能会看到一些警告信息,但是一般而言并不影响项目的建立。
4.修改项目创建项目后,在 Kdevelop窗口的左边选择源程序,开始编写程序。
Kdevelop会将程序加上一些默认的内容,可视需求自行修改,如图 9-14所示。
图 9-13 产生项目档案的过程 图 9-14修改项目
5.项目的编译与执行写好程序后,执行,建立 ->编译,命令进行程序的编译,如图 9-
15所示。如果程序没有出错,将看到 Kdevelop下方的信息框之中显示编译成功的信息。
接着便可以执行应用程序了,执行,建立 ->执行,命令进行程序的连接,如图 9-16所示。若没有发生问题,就会在另一个窗口中看到程序的执行结果,如图 9-17所示。
图 9-15 编译应用程序图 9-16 执行应用程序图 9-17 应用程序的执行结果
9.3 使用 GNU cc开发应用程序
9.3.1 使用 GNU cc
1,gcc的简介
gcc可以使程序员灵活地控制编译过程。编译过程一般可以分为下面四个阶段,每个阶段分别调用不同的工具进行处理,如图 9-18所示。
预处理 链接编译 组译源程序 (*.c) 可执行文件预处理器 编译器 组译器 连接器
2,gcc的版本信息一般来说,系统安装后就已经安装和设定好了 gcc。在 shell的提示符下键入 gcc v,屏幕上就会显示出目前正在使用的 gcc的版本,同时这可以确定系统所支持的是 ELF还是 a.out可执行文件格式。
Linux系统中可执行文件有两种格式 。 第一种格式是 a.out格式,这种格式用于早期的 Linux系统以及 Unix系统的原始格式 。 a.out来自于 Unix C编译程序默认的可执行文件名 。 当使用共享库时,
a.out格式就会发生问题 。 把 a.out格式调整为共享库是一种非常复杂的操作,由于这个原因,一种新的文件格式被引入 Unix系统 5的第四版本和 Solaris系统中 。 它被称为可执行和连接的格式 ( ELF) 。
这种格式很容易实现共享库 。
ELF格式已经被 Linux系统作为标准的格式采用。 gcc编译程序产生的所有的二进制文件都是 ELF格式的文件(即使可执行文件的默认名仍然是 a.out)。较旧的 a.out格式的程序仍然可以运行在支持 ELF格式的系统上。
3,gcc的使用
gcc的使用格式如下:
$ gcc [options][filenames]
其中 filenames为所要编译的程序源文件 。
当使用 gcc时,gcc会完成预处理、编译、汇编和连接。前三步分别生成目标文件,连接时,把生成的目标文件链接成可执行文件。 gcc可以针对支持不同的源程序文件进行不同处理,文件格式以文件的后缀来识别,常见的如表 9-1所示。
4.使用优化选项当用 gcc编译 C代码时,它会试着用最少的时间完成编译并且使编译后的代码易于调试,易于调试意味着编译后的代码与源代码有同样的执行次序,
编译后的代码没有经过优化。有很多选项可用于告诉 gcc,在耗费更多编译时间和牺牲易调试性的基础上,产生更小更快的可执行文件。这些选项中最典型的是 -O和 -O2选项。
-O选项告诉 gcc对源代码进行基本优化。这些优化在大多数情况下都会使程序执行的更快。
-O2选项告诉 gcc产生尽可能小和尽可能快的代码。 -O2选项将使编译的速度比使用 -O时慢。但通常产生的代码执行速度会更快。
5.使用调试和剖析选项
GCC 支持数种调试和剖析选项。在这些选项里最常用的是 -g和 -pg选项。
-g选项告诉 gcc产生能被 GNU调试器使用的调试信息以便调试程序。
gcc 提供了一个很多其他 C编译器里没有的特性,在 gcc里能使 -g和 -
O(产生优化代码 )连用。这一点非常有用,因为能在与最终产品尽可能相近的情况下调试代码。同时使用这两个选项时必须清楚所写的某些代码已经在优化时被 gcc作了改动。
-pg选项告诉 gcc在程序里加入额外的代码,执行时,产生 gprof用的剖析信息以显示程序的耗时情况。
9.3.2使用 GNU make编辑 makefile
1.准备工作要使用 make,必须编写一个叫做 Makefile的文件,这个文件描述了软件包中文件之间的关系,提供更新每个文件的命令 。 一般在一个软件包里,通常是可执行文件靠目标文件来更新,目标文件靠编译源文件来更新 。
Makefile写好之后,每次改变了某些源文件,只要执行 make命令:
# make
所有必要的重新编译将执行。 Make程序利用 makefile中的数据和每个文件的最后修改时间来确定那个文件需要更新,对于需要更新的文件,make程序执行 makefile数据中定义的命令来更新。
2,makefile文件的基本结构
GNU make的主要功能是读进一个文本文件 makefile并根据 makefile的内容执行一系列的工作。 makefile的默认文件名为 GNUmakefile,makefile
或 Makefile,当然也可以在 make的命令行中指定别的文件名。如果不特别指定,make命令在执行时将按顺序查找默认的 makefile文件。多数
Linux程序员使用第三种文件名 Makefile。因为第一个字母是大写,通常被列在一个目录的文件列表的最前面。
Makefile是一个文本形式的数据库文件,其中包含一些规则来告诉
make处理哪些文件以及如何处理这些文件 。 这些规则主要是描述哪些文件 ( 称为 target目标文件,不要和编译时产生的目标文件相混淆 )
是从哪些别的文件 ( 称为 dependency依赖文件 ) 中产生的,以及用什么命令 ( command) 来执行这个过程 。
依靠这些信息,make会对磁盘上的文件进行检查,如果目标文件的生成或被改动时的时间 ( 称为该文件时间戳 ) 至少比它的一个依赖文件还旧的话,make就执行相应的命令,以更新目标文件 。 目标文件不一定是最后的可执行文件,可以是任何一个中间文件并可以作为其他目标文件的依赖文件 。
一个 Makefile文件主要含有一系列的规则,每条规则包含以下内容 。
一个目标 ( target),即 make最终需要创建的文件,如可执行文件和目标文件;目标也可以是要执行的动作,如,clean‖。
一个或多个依赖文件 ( dependency) 列表,通常是编译目标文件所需要的其他文件 。
一系列命今 (command),是 make执行的动作,通常是把指定的相关文件编译成目标文件的编译命令,每个命令占一行,且每个命令行的起始字符必须为 TAB字符。
除非特别指定,否则 make的工作目录就是当前目录 。 target是需要创建的二进制文件或目标文件,dependency是在创建 target时需要用到的一个或多个文件的列表,命令序列是创建 target文件所需要执行的步骤,比如编译命令 。
Makefile规则的一般形式如下:
target,dependency dependency
(tab)<command>
例如,有以下的 Makefile文件:
# 一个简单的 Makefile的例子
# 以 #开头的为注释行
test,prog.o code.o
gcc –o test prog.o code.o
prog.o,prog.c prog.h code.h
gcc –c prog.c –o prog.o
code.o,code.c code.h
gcc –c code.c –o code.o
clean:
rm –f *.o
上面的 Makefile文件中共定义了四个目标,test,prog.o,code.o和
clean。 目标从每行的最左边开始写,后面跟一个冒号 (,),如果有与这个目标有依赖性的其他目标或文件,把它们列在冒号后面,并以空格隔开 。 然后另起一行开始写实现这个目标的一组命令 。 在
Makefile中,可使用续行号 ( \) 将一个单独的命令行延续成几行 。 但要注意在续行号 ( \) 后面不能跟任何字符 ( 包括空格和键 ) 。
一般情况下,调用 make命令可输入:
# make target
target是 Makefile文件中定义的目标之一,如果省略 target,make就将生成 Makefile文件中定义的第一个目标 。 对于上面 Makefile的例子,
单独的一个,make‖命令等价于:
# make test
因为 test是 Makefile文件中定义的第一个目标,make首先将其读入,然后从第一行开始执行,把第一个目标 test作为它的最终目标,所有后面的目标的更新都会影响到 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 prog.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按顺序读取 makefile中的规则,然后检查该规则中的依赖文件与目标文件的时间戳哪个更新,如果目标文件的时问戳比依赖文件还早,就按规则中定义的命令更新目标文件。如果该规则中的依赖文件又是其他规则中的目标文件,那么依照规则链不断执行这个过程,直到 Makefile文件的结束,至少可以找到一个不是规则生成的最终依赖文件,获得此文件的时间戳,然后从下到上依照规则链执行目标文件的时间戳比此文件时间戳旧的规则,直到最顶层的规则。
通过以上的分析过程,可以看到 make的优点,因为,o目标文件依赖,c源文件,源码文件里一个简单改变都会造成那个文件被重新编译,
并根据规则链依次由下到上执行编译过程,直到最终的可执行文件被重新连接。例如,当改变一个头文件的时候,由于所有的依赖关系都在 Makefile里,因此不再需要记住依赖此头文件的所有源码文件,make
可以自动的重新编译所有那些因依赖这个头文件而改变了的源码文件,
如果需要,再进行重新连接。
3,Makefile中的变量
Makefile里的变量就像一个环境变量。事实上,环境变量在 make中也被解释成 make的变量。这些变量对大小写敏感,一般使用大写宇母。几乎可以从任何地方引用定义的变量,变量的主要作用如下:
保存文件名列表。在前面的例子里,作为依赖文件的一些目标文件名出现在可执行文件的规则中,而在这个规则的命令行里同样包含这些文件并传递给 gcc做为命令参数。如果使用一个变量来保存所有的目标文件名,则可以方便地加入新的目标文件而且不易出错。
保存可执行命令名,如编译器。在不同的 Linux系统中存在着很多相似的编译器 系统,这些系统在某些地方会有细微的差别,如果项目被用在一个非 gcc的系统 里,则必须将所有出现编译器名的地方改成用新的编译器名。但是如果使用一个变量来代替编译器名,那么只需要改变该变量的值。其他所有地方的命令名就都改变了。
保存编译器的参数。在很多源代码编译时,gcc需要很长的参数选项,
在很多情况下,所有的编译命令使用一组相同的选项,如果把这组选项使用一个变量代表,那么可以把这个变量放在所有引用编译器的地方。当要改变选项的时候,只需改变一次这个变量的内容即可。
Makefile中的变量是用一个文本串在 Makefile中定义的,这个文本串就是变量的值 。 只要在一行的开始写下这个变量的名字,后面跟一个,=,
号,以及要设定这个变量的值即可定义变量,下面是定义变量的语法:
VARNAME=string
使用时,把变量用括号括起来,并在前面加上 $符号,就可以引用变量的值:
${VARNAME}
make解释规则时,VARNAME在等式右端展开为定义它的字符串。
变量一般都在 Makefile的头部定义。按照惯例,所有的 Makefile变量都应该是大写。如果变量的值发生变化,就只需要在一个地方修改,从而简化了 Makefile的维护。
现在利用变量把前面的 Makefile重写一遍:
OBJS=prog.o code.o
CC=gcc
test,${ OBJS }
${ CC } –o test ${ OBJS }
prog.o,prog.c prog.h code.h
${ CC } –c prog.c –o prog.o
code.o,code.c code.h
${ CC } –c code.c –o code.o
clean:
rm –f *.o
除用户自定义的变量外,make还允许使用环境变量、自动变量和预定义变量。使用环境变量的方法很简单,在 make启动时,make读取系统当前已定义的环境变量,并且创建与之同名同值的变量,因此用户可以像在 shell中一样在 Makefile中方便的引用环境变量。需要注意的是,
如果用户在 Makefile中定义了同名的变量,用户自定义变量将覆盖同名的环境变量。此外,Makefile中还有一些预定义变量和自动变量,但是看起来并不像自定义变量那样直观。表 9-3中给出了常见的自动变量。
除了自动变量外,Makefile中还有一些预定义的内部变量,用于定义编译命令名、编译参数等,如表 9-4所示:
4,Makefile的隐含规则在上面的例子中,几个产生目标文件的命令都是从,,c‖的 C语言源文件和相关文件通过编译产生,,o‖目标文件,这也是一般的步骤 。 实际上,make可以使工作更加自动化,也就是说,make知道一些默认的动作,它有一些称作隐含规则的内置的规则,这些规则告诉 make当用户没有完整地给出某些命令的时候,应该怎样执行 。
例如,把生成 prog.o和 code.o的命令从规则中删除,make将会查找隐含规则,然后会找到并执行一个适当的命令 。 由于这些命令会使用一些变量,因此可以通过改变这些变量来定制 make。 象在前面的例子中所定义的那样,make使用变量 CC来定义编译器,并且传递变量 CFLAGS
( 编译器参数 ),CPPFLAGS( C语言预处理器参数 ),TARGET_ARCH
( 目标机器的结构定义 ) 给编译器,然后加上参数 -c,后面跟变量 $<
( 第一个依赖文件名 ),然后是参数 -o加变量 $@( 目标文件名 ) 。 综上所述,一个 C编译的具体命令将会是:
$ {CC} $ {CFLAGS} $ {CPPFLAGS} $ {TARGET_ARCH} –c $< -o $@
在上面的例子中,利用隐含规则,可以简化为:
OBJS=prog.o code.o
CC=gcc
test,${ OBJS }
${ CC } –o $@ $^
prog.o,prog.c prog.h code.h
code.o,code.c code.h
clean:
rm –f *.o
5、常用的 make命令行选项
make命令有丰富的命令行选项。表 9-5中列出了常用的部分。
9.3.3 使用 automake和 autoconf产生 Makefile
在开始使用 Automake和 autoconf之前,请先确认系统已经安装以下的软件:
GNU Automake
GNU Autoconf
GNU m4
Perl
GNU Libtool (如果你需要产生 shared library)
Automake 所产生的 Makefile 除了可以做到程序的编译和连接,也已经把如何产生程序文件的操作,以及把安装程序都考虑进去了,所以源程序所存放的目录架构最好符合 GNU的标准惯例,下面用 hello.c 来作为例子进行说明 。
在工作目录下建立一个新的子目录 devel,再在 devel下建立一个 hello的子目录,这个目录将作为存放 hello 这个程序及其相关文件的地方:
用编辑器写个 hello.c文件:
#include <stdio.h>
int main(int argc,char** argv)
{
printf("Hello,GNU!\n");
return 0;
}
接下来就要使用 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,Automake 会根据 Makefile.am产生一些文件,包含最重要的 Makefile.in:
( 6) 最后执行,/configure,
现在你的目录下已经产生了一个 Makefile文件,执行 make命令就可以开始编译 hello.c 成执行文件,最后执行,/hello:
# make
gcc -DPACKAGE="hello" -DVERSION="1.0" -I,-I,-g -O2 -c hello.c
gcc -g -O2 -o hello hello.o
#,/hello
Hello! GNU!
9.4 调试工具 GDB
9.4.1 GDB调试器简介
Linux系统中包含了 GNU 调试程序 gdb,它是一个用来调试 C和 C++
程序的调试器。可以使程序开发者在程序运行时观察程序的内部结构和内存的使用情况。 gdb 所提供的一些功能如下所示:
运行程序,设置所有的能影响程序运行的参数和环境;
控制程序在指定的条件下停止运行;
当程序停止时,可以检查程序的状态;
修改程序的错误,并重新运行程序;
动态监视程序中变量的值;
可以单步执行代码,观察程序的运行状态。
gdb的功能非常强大,到目前为止,gdb已能够支持 Moduls-2,Chill、
Pascal和 FORTRAN程序的调试,但是调试这些语言的源程序时有一些功能还不能使用。例如调试 FORTRAN程序时还不支持表达式的输入、
输出变量或类 FORTRAN的词法。
gdb程序调试的对象是可执行文件,而不是程序的源代码文件。然而,并不是所有的可执行文件都可以用 gdb调试。如果要让产生的可执行文件可以用来调试,需在执行 gcc指令编译程序时,加上 -g参数,
指定程序在编译时包含调试信息。调试信息包含程序里的每个变量的类型和在可执行文件里的地址映射以及源代码的行号。 gdb 利用这些信息使源代码和机器码相关联。
在命令行上输入 gdb并按回车键就可以运行 gdb了,如果一切正常的话,将启动 gdb,可以在屏幕上看到以下的内容:
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 Free Software Foundation,Inc.
GDB is free software,covered by the GNU General Public License,and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB,Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu".
(gdb)
启动 gdb后,可以在命令行上指定很多的选项 。 输入:
help
可以获得 gdb的帮助信息 。 如果想要了解某个具体命令 ( 比如 break)
的帮助信息,在 gdb提示符下输入下面的命令:
break
屏幕上会显示关于 break的帮助信息 。 从返回的信息可知,break是用于设置断点的命令 。 另一个获得 gdb帮助的方法是浏览 gdb的手册页 。
在 Linux Shell提示符输入:
man gdb
可以看到 man的手册页。
9.4.2 GDB命令的基本使用和应用
1,gdb基本命令还可以用下面的方式来运行 gdb:
gdb filename
其中,filename是要调试的可执行文件 。 用这种方式运行 gdb可以直接指定想要调试的程序 。 这和启动 gdb后执行 file filename命令效果完全一样 。 也可以用 gdb去检查一个因程序异常终止而产生的 core文件,或者与一个正在运行的程序相连 。
gdb支持很多的命令且能实现不同的功能。这些命令从简单的文件装入到允许你检查所调用的堆栈内容的复杂命令,下面列出了在使用
gdb 调试时会用到的一些命令。
1) file命令:装入想要调试的可执行文件。
2) cd命令:改变工作目录。
3) pwd命令:返回当前工作日录。
4) run命令:执行当前被调试的程序。
5) kill命令:停止正在调试的应用程序。
6) list命令:列出正在调试的应用程序的源代码。
7) break命令:设置断点。
8) Tbreak命令;设置临时断点。它的语法与 break相同。区别在于用
tbreak设置的断点执行一次之后立即消失。
9) watch命令:设置监视点,监视表达式的变化。
10) awatch命令:设置读写监视点。当要监视的表达式被读或写时将应用程序挂起。它的语法与 watch命令相同。
11) rwatch命令:设置读监视点,当监视表达式被读时将程序挂起,
等侍调试。此命令的语法与 watch相同。
12) next命令:执行下一条源代码,但是不进入函数内部。也就是说,
将一条函数调用作为一条语句执行。执行这个命令的前提是已经 run,
开始了代码的执行。
13) step命令:执行下一条源代码,进入函数内部。如果调用了某个函数,会跳到函数所在的代码中等候一步步执行。执行这个命令的前提是已经用 run开始执行代码。
14) display命令:在应用程序每次停止运行时显示表达式的值。
15) info break命令:显示当前断点列表,包括每个断点到达的次数。
16) info files命令:显示调试文件的信息。
17) info func命令:显示所有的函数名。
18) info local命令:显示当前函数的所有局部变量的信息。
19) info prog命令:显示调试程序的执行状态。
20) print命令;显示表达式的值。
21) delete命令:删除断点。指定一个断点号码,则删除指定断点。
不指定参数则删除所有的断点。
22) Shell命令:执行 Linux Shell命令。
23) make命令:不退出 gdb而重新编译生成可执行文件。
24) Quit命令:退出 gdb。
2,gdb应用实例下面使用 gdb调试程序来调试一个实例。被调试的程序相当的简单,但展示了 gdb的典型应用。下面列出了将被调试的程序代码,这个程序被称为 greeting.c,功能是显示一个简单的问候,再用反序将它列出。
9.5 使用 RCS/CVS来管理源代码
9.5.1 RCS的使用
RCS( Revision Control System) 即程序改版控制系统,主要功能是用来管理文件的版本,可以节省空间和时间 。 这样就不需要在每个程序开发到某一个阶段就将数据拷贝到其他的地方备份起来了 。
RCS提供了如下几个最重要的指令的:
ci指令:将文件放入 RCS目录下的控制系统
co指令:从 RCS目录下将文件取出
rcs指令:用来对 RCS文件进行参数的设置
1.基本操作方式一般而言,RCS所产生出来的文件会放在 RCS目录中 。 所以第一步必须要在当前的目录下制作一个文件:
[root@wyh linux]# mkdir RCS
接下来只要使用 ci指令 。 就可以把文件备份到 RCS改版控制系统中:
[root@wyh linux]# ci test.c
若要将文件取出,可以使用下列指令:
[root@wyh linux]# co test.c
取出来的文件是只读文件,若要取出可以写入的工作文件,可以加上
-l参数来锁定它:
[root@wyh linux]# co -l test.c
此外将文件放入 RCS控制系统时,可以使用 -l参数锁定文件,那么目录下的文件依然存在:
[root@wyh linux]# ci -l test.c
若要比较当前的文件和 RCS中最新版本的文件,可以使用下列指令:
[root@wyh linux]# rcsdiff test.c
2.指定版本若不指定版本编号时,co会从 RCS取得最新的版本 。 如果要以特定的版本号码写入 RCS或读出,可以使用 -r参数选项 。
[root@wyh linux]# ci -l -r3.25 test.c
<--以 3.25作为版本编号
[root@wyh linux]# co -l -r1.2 test.c
<--将 RCS中 1.2版的 test.c读出此外,rcsdiff也可以用来指定任何一个版本和当前程序代码进行比较 。
[root@wyh linux]# rcsdiff -r3.25 test.c
<--取出 3.25版与 test.c进行比较
3.关键词的使用在 RCS中可以将关键词变量放入程序代码中 。 这些变量经过 RCS会变成版本的注解 。 用户可以将这些关键词说明当作是程序中的批注 。
常用的关键词如下:
$Author$:将版本放入 RCS的用户名称 。
$Data$:记录程序代码放入 RCS时的日期和时间 。
$Header$:记录文件的标头,包括 RCS路径名称,版本号码,日期,
作者等 。
$ID$:和 $Header$相同,但不包括 RCS路径名称 。
$Locker$:记录锁定本版本的用户名称 。
$Log$:记录将 RCS锁住的时间,所输入的文本语句 。
$RCSfiles$:记录 RCS文件名称 。
$Rivision$:指定版本号码 。
$Source$,RCS文件名称,包括其路径 。
$State$:使用 -s选项所指定的特殊状态 。
使用关键词的步骤如下所示:
( 1) 在程序代码中加入任一关键词
[root@wyh linux]#vi test.c
( 2) 将程序代码放入 RCS版本控制系统
[root@wyh linux]# ci -l test.c
( 3) 将文件再次取出 。 在取出的过程中,co会将每个关键词展开成其对应的值
[root@wyh linux]# co -l test.c
[root@wyh linux]# cat test.c
9.5.2 CVS的使用
CVS( Concurrent Version System) 是个版本控制系统,利用该系统可以记录源代码文件的历史 。 例如,当软件修改时会产生 Bug,并且可能在做这次修改后很长时间不会发现这些问题 。 使用 CVS就可以容易地回顾老的代码版本去发现哪一次的修改导致这些问题 。
如果 CVS保留每一次的代码版本,会浪费很多的空间 。 因此 CVS使用一种比较聪明的办法保存多个版本在一个文件中 。 它仅仅保留版本间的不同内容 。 如果很多人在同一个项目上工作,则 CVS使用让不同开发者独立工作的方式解决了这个问题 。 每一个开发者的工作都在他自己的目录内,并且 CVS将在每个开发者的工作完成后进行合并工作 。
在 Linux下,CVS的使用一般是以命令行方式 。 通常,CVS有两种使用方式,一是本机方式,一是远程执行方式 。 CVS的命令格式是:
cvs [cvs的选项 ] cvs的动作 [选项 ]
读者可以用 cvs –H command列出命令 command的使用方法。
1.开始项目用 CVS管理代码,首先要创建一个,信息仓库,。,信息仓库,简单来说包含一个目录结构。它包括要管理的源代码和用于管理源代码的各种管理文件。
先设置环境变量 CVSROOT,指向信息仓库的绝对路径,然后调用
CVS的 init命令:
# CVSROOT=/usr/local/cvsroot;export CVSROOT
# cvs init
# ls -l $CVSROOT
2.添加项目的文件、目录到信息仓库要将需要管理的项目的文件加入到信息仓库,并做上标志。如果从头开始一个新的项目,就需要创建一个单独的目录,并把所有要使用的文件做一个有效的组织。而如果在开始使用源文件之前该目录就已经存在,则只需进入该目录就行了。
然后,就可以输入源文件目录:
# cvs import -m "Create Source Dir" cvstest/c wu cvstest
这样会生成 $CVSROOT/cvstest/c 目录。 其中 -m用来指定注释信息,
如果后面在命令行不指定注释信息,则会启动缺省编辑器( vi)要求输入注释信息; cvstest/c是项目名称(实际上是仓库名,在 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/modules
中加以指定,
① # cvs checkout CVSROOT/modules
② 在 modules文件中加入下面一行:
SOURCE cvstest/c
③ 然后执行:
# cvs commit –m ―Add SOURCE‖
以后就可以使用下面的命令在当前路径下生成 cvstest/c目录
# cvs checkout SOURCE
在当前路径下生成的这个目录就被称为工作目录,对源文件的所有修改都应该在这个目录下完成,而绝对不允许去改动在文件仓库中
$CVSROOT 目录下的文件 。
( 2) 删除,增加,重命名文件和目录
cvs add [-k kflags][-m message] files...
-k 指定以后该文件的缺省导出目录
-m 对文件的描述上述命令会加入一个新的文件到文件仓库里,但直到使用了提交命令它才会真正更新文件仓库 。
cvs remove [options] files
上述命令会从文件仓库中删除文件,但也要到提交之后才有作用。
( 3) 提交源文件
cvs commit [-Rl][-m mesg] files
-R 连子目录一起提交
-l 只提交本地目录 ( 不提交子目录 )
-m 注释信息在导出源文件之后,在工作目录中对源文件进行的所有修改都必须在提交之后才能对文件仓库中的源文件起作用,并且新的文件才能够被分配一个新的版本号 。
( 4) 释放工作目录
cvs release –d SOURCE
这个命令会删除工作目录 cvstest/c(建议在提交了修改的模块后执行这一步),比使用 rm –rf cvstest 要好。
4.多用户开发在多用户的情况下,如果不同用户修改的是同一个文件的不同部分,
则使用下面的命令就能进行版本合并 ( 把检出的文件与当前的最新版本合并 ),
# cvs update
( 1) 冲突解决在有多个用户对同一个文件进行修改时,如果修改了其中的相同部分,而修改后的内容如果有不同的话,出现冲突是不可避免的。
如果在 CVS 文件仓库中有一个文件 test.c,它的版本是 1.4,用户 A 先检出该文件进行修改,而稍后有用户 B检出该文件进行修改,并提前提交成 1.5,而在用户 A再提交时就会出现冲突(如果文件内容不同的话),这时 CVS会提示需要手工解决。
( 2) 文件版本管理
cvs log [-lR][-r rev][-d date][-w login][files… ]
-l 不处理子目录
-R 对子目录做同样处理
-r 指定版本号
-d 指定时间
-w 指定登录名使用上面的命令可以参看当前模块或指定文件的所有历史版本信息 。
cvs annotate [-lR][-r rev|-D date] files
-l 不处理子目录
-R 对子目录做同样处理
-r 指定版本号使用上面的命令可以参看指定文件(检出之后)的所有修改信息。
使用下面的命令可以生成相对于一个指定主版本的分支版本:
cvs rtag –b –r rev_root rev_branch file_name
-b 指定生成一个分支版本
-r 指定该分支的主干节点版本号
rev_root 主干版本号
rev_branch 分支版本号
file_name 指定文件,使用,,‖表示当前目录下所有文件使用上面的命令可以生成一个对应版本号的分支版本,由于 CVS 版本号是用数字表示的,而且在同一个模块下不同文件的版本完全可能是不同的,所以使用标识会更方便。
例:
# cvs rtag –b –r 1.2 tlb-1 SOURCE
以后要访问该分支版本,可以使用,-r‖选项
# cvs checkout –r tlb-1 SOURCE
从当前检出的版本切换到一个分支版本:
# cvs update –r tlb-1 SOURCE
使用下面的命令可以看版本信息:
cvs status [–vlR] files
-v 显示所有信息
-l 不显示子目录信息
-R 显示子目录信息
cvs update –j rev module
把当前所做的修改与指定版本的文件进行合并。
如果在不同版本之间模块的文件有增减,则可以:
# cvs update –A
# cvs updata –jbranch_name
5.在远程机器上使用 CVS
通过网络使用 CVS 有很多种方式,但在这里只介绍比较简单的一种:
通过 rsh 执行 cvs 命令 。
1) 在远程机器的,rhosts中加入对本地机的访问许可:
tom huang
2) 使用下面的命令检出模块 ESMSTRG
# cvs –d,ext:huang@wyhlinux:/work/cvsroot checkout SOURCE
其中,ext 指明了连接方式为 rsh,huang指明了本地用户,wyhlinux
指明了远地主机,/work/cvsroot 指明了在远地主机上的 $CVSROOT路径,可以在本地设置 CVS_SERVER环境变量指明这个目录。
9.6 将软件打包
9.6.1 RMP简介
RPM是 Redhat Package Manager的缩写,是由 Red Hat公司开发的软件包安装和管理程序,同 Windows平台上的 Uninstaller和 Cleansweep比较类似。使用 RPM,用户可以自行安装和管理 Linux上的应用程序和系统工具。
RPM可以让用户直接以 binary方式安装软件包,并且可替用户查询是否已经安装了有关的库文件;在用 RPM删除程序时,会询问用户是否要删除有关的程序 。 如果使用 RPM来升级软件,RPM会保留原先的配置文件,
这样用户就不用重新配置新的软件了 。 RPM保留一个数据库,这个数据库中包含了所有的软件包的资料,通过这个数据库,用户可以进行软件包的查询 。 RPM虽然是为 Linux而设计的,但是已经移值到 SunOS、
Solaris,AIX,Irix等其它 UNIX系统上了 。 RPM遵循 GPL版权协议,用户可以在符合 GPL协议的条件下自由使用及传播 RPM。
RPM设计的目的有下面几点:
方便的升级功能,RPM让用户不用重新安装整个系统就可以对单个软件包进行升级,当一个新的发行版本问世的时侯,用户也不用重新安装,RPM会替用户全面、自动、智能地升级系统,并且保留用户原先的配置文件,这就大大减少了用户维护系统的工作量。
强大的查询功能:用户可以针对整个软件包的数据,或是某些特定的文件进行查询,也可以轻松地查出其个文件是属于哪个软件包,或是从哪里来的。 RPM文件本身是经过压缩的,但用户还是可以很容易地快速查询每个软件包的内容,因为在 RPM软件包里,已经加入一些特殊的 binary header,记录了全部查询时所需要的数据,这一点大大加快了查询速度。
系统校验:当用户不小心删除了某个重要的文件,但是又不知道是哪些软件包需要这个文件,这时侯就可以用 RPM来查询已经安装的软体包中缺了哪些文件,是需否要重新安装。并且用户可以校验出安装的软件包是否已经被别人更改过。
允许用户能够使用,纯净,的源代码:让用户取得,未经处理过的源代码,,同时再附上一份,补丁,程序,用户可凭借这些来完成程序编译工作。这样的做法带来不少好处。例如,如果某个程序的新版本问世了,用户可能没有必要再重头开始做全部的编译工作,先观察,补丁,程序的内容,看看有哪些部分是用户需要做的。这样就能让用户更清楚地知道新版本有哪些改进的地方。
下面简单介绍一下 RPM的使用。
1.用 RPM安装软件包最简单的安装命令如下:
# rpm -ivh xwpe-1.5.29a-1.i386.rpm
RPM会输出该软件包的名称,并且显示一个状态条 。 安装软件很简单,
但是有时会给出一些出错消息:
Package Alrealy Installed!
表示该软件包已经安装,也可使用 --replcepkgs选项强制 RPM重新安装这个软件 。
Conflicting files
表 示 该 软 件 包包 含 某 些其 他 软 件包 安 装 过的 文 件,可加 —
replacepkgs选项覆盖原先的文件 。
Unresolved Dependency
表示正确运行该软件需要其他哪些软件包,因为 RPM的软件包能够查询该软件的,依赖,关系,所以 RPM在安装之前会先查询,如果系统没有安装需要的软件包,就会出现错误提示信息 。
但是,如要继续安装,就必须先安装相应的软件包,或使用 -nodeps
选项强制安装,但不推荐使用,因为这样安装的结果一般不能运行。
2.用 RPM删除安装软件包要删除已安装的软件包,只需要执行下面的命令:
# rpm -e xwpe
3.用 RPM升级软件升级软件类似于安装软件,执行如下命令:
# rpm -Uvh xwpe-1.5.31a-1.i386.rpm
RPM会自动删除相应软件包的老版本,如果配置文件同新版本不兼容,则会自动将其保存为另一 oldconfig.rpmsave文件。这样,用户就可以自已手工去更改相应的配置文件。
4.查询软件包用户可以用 RPM –q来在 RPM的数据库中查询相应的软件,RPM会给出软件包的名称,版本和发布版本号,例如:
# rpm -q xwpe
xwpe-1.5.29a-1
5.用 RPM校验软件包用户可以用 RPM来校验已经安装的软件包,RPM可以校验文件大小,MD5校验码、文件权限、类型及属主等信息。
6.实际使用技巧
1) 可以通过 FTP来安装软件包 。 如果用户能够连上网络,想安装某个新的软件包时,可以直接用它的 URL地址来安装,例如:
# rpm –I ftp://ftp.whpu.edu.cn/linux/xwpe-X11-1.5.29a-1.i386.rpm
也可以用网络来查询 。
2) 假如用户不小心误删了几个文件,但不确定到底是哪些文件,通对整个系统进行校验,以了解哪些部份可能己经损坏,应这样做:
# rpm –Va
3) 如果用户碰到一个认不出来的文件,想要知道是属于哪一个软件包的话,可以这样做:
# rpm –qf /usr/bin/cdplay
4) 如果用户得到一个新的 RPM文件,却不清楚它的内容,可以这样做:
# rpm -qpi /xwpe-X11-1.5.29a-1.i386.rpm
5) 如果用户想了解某个 RPM软件包会在系统里安装哪些文件,可以这样做:
# rpm -qpl /xwpe-X11-1.5.29a-1.i386.rpm
9.6.2 制作 RMP
RPM目前最新的版本 4.2同以前的 3.x系列是不兼容的 。 一般来说,为
RedHat6.2,Mandrake,TurboLinux,Suse打包就需要 3.x系列,而为 RedHat7.0以上的版本,打包就需要 4.0以上的 RPM版本 。 具体软件可以到 www.rpm.org下载 。
制作 RPM的基本步骤如下:
1) 取回软件 source code和相关的 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/redhat/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.编辑 macros
RPM的配置文件中 /usr/lib/rpm/macros是一个全局的配置文件,可以在其中设定一些全局变量,如:
% distribution 发行版本名
% packager 打包者姓名和 email
% vendor 软件包作者除了上述的 macro设定外,还有许多其他的设定方式,可以使用下面的命令来查看系统的 tag与可供使用的 flag有哪些:
# rpm –showrc
3.编辑 spec文件制作一个软件包时,需要使用到 spec文件,其内容为该程序软件包的说明,还包括一些指令,用以执行整个 build的过程,还有一份文件列表,用以表示程序软件包中的文件,分别被安装在里面 。
spec文件的命名方式,最好是遵循一个通用的格式,其格式应该为
,软件包名版本号,spec‖。
4.编译 RPM
在 SPEC文件编译完成后,再开始编译工作:
# rpm –ba helloworld-1.0-1.spec
编译完成后一定要进行测试工作,最好是在另外一台干净的系统上进行测试安装和卸载过程。如果一切无误那么就可以发布这个 RPM包了。
本章小结