`
lxy2330
  • 浏览: 459765 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Linux下C++开发初探

 
阅读更多

1.开发工具

    Windows下,开发工具多以集成开发环境IDE的形式展现给最终用户。例如,VS2008集成了编辑器,宏汇编ml,C /C++编译器cl,资源编译器rc,调试器,文档生成工具, nmake。它们以集成方式提供给最终用户,对于初学者而言十分方便。但是,这种商业模式,直接导致用户可定制性差,不利于自动化,集成第三方工具的能力弱。例如,无法定制一些宏来处理一些重复操作;体会不到自动化makefile一步到位快感;无法远程登录到服务器上进行开发;无法使用某种”粘合剂”来把第三方工具(例如,文本工具,字符串工具)有效地调用起来。可以说,良好的商业支持和傻瓜式开发,是它们主要的优点。
    在linux下,开发工具被切割成一个个独立的小工具。各自处理不同的问题。例如,编辑器(emacs, vim)用来进行编辑程序的,调试器(gdb)用来调试程序,编译器(GCC)用来编译和链接程序的,性能分析工具(gcov, gprof)用来优化程序的,文档生成器(doxygen)用来生成文档的。同时,还有一些系统工具和系统知识,我们是很有必要了解的:程序自动化机制 makefile,系统粘合剂shell,系统查找工具grep, locate, find。其它的工具(例如ctags, OCI公司的MPC等等),一旦熟练掌握,它们将成为你手中的利器。

   主要是一些针对LINUX下开发工具使用的经验之谈。由于,工具品种繁多,我们没有能力也没有必要一一介绍。对于LINUX下IDE工具,例如 eclipse, anjuta等,它们虽然也很实用,但是使用起来比较简单,而且目前还算不上主流。所以,它们将不被着重介绍。同时,本文也不打算写成各个工具的操作手册,只着眼于介绍各个工具的想要解决的问题、运行机理和主要特性。

1.1编辑器

    要进行开发,第一件事情就是选择一个合适的编辑器。编辑器选择有几个要素:
    1)减少不必要的编辑动作,减少编辑的时间。
    一切能够无二义性描述出来的编辑任务,都可以而且应该能被自动化。例如,每一个C++程序都会有一个main函数;我们在定义.h文件时,都希望加入一些预处理指令#define来帮我们解决重复引用同一个头文件而带来的麻烦。鼠标操作总是比键盘操作要慢的。这方面EMACS做得可算是到了极致。所以, EMACS用户经常会吹嘘:他们编辑的速度等同于他们思考的速度。
    2)可扩展性高。
    程序员预期的编辑器应该能提供一些编程的帮助,例如,语法高亮,自动补齐,自动排版,语法检查等等。留心观察一下gedit, vim, emacs, ultraEdit,就会发现它们提供的远不是windows 记事本,写字板提供的那么简陋的功能。对于一种新的语言,新的语法,它们应该能很方便地提供支持,而不停留在一种或几种固定的语言上。
    3)用户可定制性高。
    如果想长期从事研发, 特别是linux/unix下研发的话,那么你很有必要学好一个功能足够的编辑器。有这么一句话:Linux下程序员分为三种,使用emacs的,使用vi的,还有其它。EMACS是Stallman用lisp语言写的一个GPL的编辑器。我们这里所说的emacs指的是GNU emacs,而非Xemacs。由于它的开放性,我们可以把它打造成一个功能强大的IDE。我们在安装好CGYwin之后,也可以在Windows系统下使用 EMACS。CGYwin和MINGW是第三方写的一个在Windows系统上模拟POSIX系统的工具。
    EMACS与其说的是一个编辑器,倒不如说它是一个操作系统。我们可以用它来写编程,写wiki,收发邮件等等。EMACS主要是通过两种方式来进行扩展:el脚本(elisp是lisp的一种方言)和第三方扩展包。EMACS的入门成本很高。由于是纯键盘操作,所以需要记忆大量的快捷键;功能强大是通过用户添加一些扩展包,lisp脚本来实现的。如何正确配置和修改是很需要耐心和技巧的。

1.2编译器

    编译器首选GCC(GNU COMPILER COLLECTION)。原因有两个,它是GNU开源的,同时它对标准C++的支持度高达96.15%。而VC++6.0的支持度只有83.43%。 GCC不仅是通常意义上的C或C++的编译器,它还可以编译java等其它语言。gcc是gnu c的编译器,g++是gnu c++的编译器, 而EGCS(Enhanced GNU Compiler Suite)可以认为是gcc的改进版。
    编译语言从源程序到目标代码会经过如下几个阶段:源程序->汇编程序->编译成obj程序->链接成最终可执行程序。我们可以通过一条编译指令来完成所有步骤。也可以分步执行。gcc有三个重要选项-E(只进行预处理), -S(生成汇编代码), -g(生成带原代码调试符号的可执行文件,如果想用gdb调试的话,就应该在编译时打开这个选项)。
    GCC可以看作一个软件包,除了编译工具,它还集成了调试器gdb,性能分析工具gcov, gprof。只要我们装好了GCC,这些强大工具就可以直接使用了。
    通过gcov,我们可以查看一个程序,源代码中每行代码的运行次数。我们优化运行次数最多的代码,那么就可以大大优化程序。使用gcov时,需要打开 GCC的fprofile-arcs和ftest-coverage两个选项。gcov中常用的选项有-b分支统计信息。
    通过gprof工具,我们可以查看函数之间的调用顺序,及各个函数运行的时间。我们可以将gprof理解为linux/unix自带工具time的加强版。使用gprof时,需要打开GCC的pg选项。
    gcov和 gprof的共同点是在编译程序时,加入自己的一些辅助信息,由此来进行程序诊断。除了,这些优化手段,我们还可以使用一些内存泄漏工具,来减少野指针,未释放的内存空间。
调试器
    GDB即GNU的调试器,它是GCC附带的一个性能优质的调试器。通过GDB和脚本结合,我们可以很好的实现回归测试。
    GDB可以运行于CLI和GUI两种模式。默认GDB是CLI模式的,我们可以去下载和安装GUI模式的GDB,例如xxgdb, ddd等。一个更好的方式是在 EMACS中使用GDB。GDB包括visual studio工具的所有调试功能,还包括它没有的功能。它除了支持,我们一般的设置断点,单步跟踪,step in, step out, step over等,还有一些强大的功能。在gdb中,我们可以有以下几种暂停方式:断点(BreakPoint)、观察点(WatchPoint)、捕捉点(CatchPoint)、信号(Signals)、线程停止(Thread Stops)。
下面列举几个让我印象深刻的功能。1)通过 watch指令,可以让程序在某个变量的值发生变化时,暂停下来。2)通过print指令,在程序运行时,设置变量的值,运行一个程序自身支持的一个方法。3)通过until指令,我们可以让程序在运行到某个程序时暂停下来。4)通过break.. if指令,使得程序在满足某个bool表达式时,暂停下来。

1.3粘合剂

    我想通过“粘合剂”这个词来表达将多个工具粘合起来的“胶水”。例如,通过shell脚本,我们可以把OS命令,sed指令,awk指令,其它脚本文件等串联起来,发挥它们的合力。在linux C++编程中,我们不可避免地会使用makefile文件。通过,它我们可以把编译指令,生成文档操作,清除操作等等串联起来。从某种意义上来看,它也相当于一个粘合剂。
makefile的出发点是,维护好一个项目中众多文件的依赖关系,由此得到一个源程序的拓扑图。当我们只修改图中某个结点时,重新编译时就只需要将拓扑图中关联的链路进行编译就好了。由此,大大缩短了编译的时间。make有两大概念:dependencies和rules。规则rule即针对每一个依赖关系 dependency定义一个操作规则。这个细粒度的分离,就可以使我们可以定制软件构建的行为。例如,修改使用的编译器,修改includepath, 修改libpath, 修改编译选项等等。我们常见的VC中的nmake,功能和make是类似的。
    make使用的重点和难点是编写Makefile文件。Makefile的语法相对其它语言来说是很不一样的,我们要特别注意TAB键和空格键的区别。有很多工具可以用来帮助我们生成Makefile。最出名的就是GNU的autoconf了。一个GNU程序的编写,需要autoscan, aclocal, autoconf, automake这四个工具。
我们知道GNU软件安装的三步曲是:./configure, make, make install。其中./configure就是根据autoconf, alocal等工具生成一个makefile文件。make指令就是调用make指令来根据makefile文件的规则来编译源程序。而make install就是执行makefile中的install规则指出的操作(一般是copy操作)。而make clean就是执行makefile中的clean规则指出的操作(一般是rm操作)。我们用Eclispe+CDT开发Managed C++ Project时,它就是通过objects.mk,subdir.mk,sources.mk三个文件来生成Makefile。我们注意观察编译时的输出信息,就可以看到显示的Makefile文件的内容。
可以说,如果想编译出跨平台的C++程序,那么makefile是一种最方便的机制。OCI公司为Douglas C.Schmidt的ACE,TAO开源社区编写了一段伟大的perl脚本--MPC。它由平台信息,一个规则文件,源代码,生成用户想要的工程文件,例如Make, Nmake, Visual C++ 6, Visual C++ 7等等。Google Web Tookit, Celtix做的事情与之类似,不过它们是针对JAVA的,而MPC是针对C++的。

2. 基本开发流程

2.1 代码的编写

对于移植工程来讲,基本代码都在Windows下完成,只需要吧代码传到Linux下,然后在Linux下面组织源码目录即可。对于传到Linux编辑,可以使用Linux下的vi工具来完成,也可以通过UltraEditFtp方式打开Linux下的文件进行编辑。viLinux下面最常用的一个文本编辑器,后面将会介绍vi的一些基本用法。

有一点值得注意,Windows下文本里面的回车符包含两个字符‘\n’和‘\r’,而Linux下的文本里面的回车符只包含一个字符‘\n’。这样,如果上传文件的时候没有选择正确的方式,应该使用文本方式上传的使用了二进制方式,或者应该使用二进制方式上传的使用了文本方式,那么在Linux下都会出现问题,打开的文本当中每一行的行尾就会出现一个‘^M’字符。可以通过vi的匹配替换功能(稍后会做介绍)或者重新按照正确的方式上传来解决。

2.1.1 vi简介

viLinux下最常用的一个文本编辑器,小巧而且功能强大,一次我们把它单独拿出来做一下介绍。如果想了解更详细的信息,请执行man vi查看其联机帮助文档。

2.1.2 命令模式和编辑模式

vi的工作模式包括命令模式和编辑模式两种。命令模式下可以执行vi中定义的一些命令,这些命令跟一些特定的键相对应,命令模式下所有的键盘相应将会作为命令来解释。编辑模式就是编辑文档的模式,在编辑模式下所有键盘的相应都为作为文档输入的内容。通过ESC键可以从编辑模式切换到命令模式。通过一些编辑命名可以从命令模式进入编辑模式。

2.1.3 基本命令

基本命令指的是在命令模式下,通过敲键执行的命令,这里我们介绍几个常用的命令:

i             在当前开始插入,进入编辑模式

I             在行首开始插入,进入编辑模式

a             在当前字符后追加,进入编辑模式

A            在行尾追加,进入编辑模式

x            删除当前光标处的字符

X            删除当前光标前面的字符

D            删除从光标位置到行尾的所有字符

dd           删除当前行

dw          删除当前的单词

u            取消刚才的操作

G            跳到文件末尾

此外,在命令模式下,通过输入‘/’可以进行查询,通过输入‘:’可以输入一些其他命令。输入‘:’可以输入的命令包括:

q            退出

w           保存

wq          保存退出

q         强制退出,不保存

w        对受保护的文件强制写,包括只读文件

set number     显示行号

数字       跳到某一行

2.1.4 查找和匹配

vi的查找功能也非常强大,命令模式下通过输入‘/’就可以进入查找模式,可以输入要查找的关键字。然后可以通过‘n’来查找下一处。

在输入‘:’后,还可以输入一些匹配替换的命令——“%s”。命令的格式为“:%s /str1/str2,执行之后将把当前文件中所有str1替换成str2。举一个典型的例子,前面我们说过,Windows下的文本文件如果以二进制的方式传到了Linux下,那么vi打开的时候每行的行尾就会出现一个“^M”,我们可以用vi打开这个文件,然后通过vi的匹配替换功能去掉这些“^M”。命令格式如下:

       :%s /^M//

其中“^M”通过CTRL+V CTRL+M来输入。  

2.2 编译

2.2.1  简单编译

       对于简单的程序,如只有几个源文件,可以直接使用编译器进行编译,或者把几条编译命令写在一个脚本文件里面,通过执行脚本文件实现工程的编译和连接。比如只有一个hello.cpp文件的工程,可以通过如下命令编译:

CC –o hello hello.cpp

其中CC是编译器,不同的系统下面可能有不同的编译器。一般来说,大多数Linux系统下的C编译器都叫cc,而C++编译器叫CCLinux下面带的C编译器为gccC++编译器为g++-o参数用来指定输出的目标的名称,也就是编译后执行程序的名称。这种情况下编译和连接一步完成。

对于稍微负责一些的程序,包含多个源文件的,可以编写一个编译脚本,相当于windows下的批处理。如下:

工程中包含hello.cppfunc.cppother.cpp,我们可以用如下脚本来实现工程的编译。

        CC –c hello.cpp

        CC –c func.cpp

        CC –c other.cpp

        CC –o hello hello.o func.o other.o

多个文件情况下,把编译和连接分开执行,先逐个编译源文件,然后再进行链接,形成最终的可执行程序。参数-c就是声明只进行编译操作。

2.2.2  使用Makefile

当工程达到一定的规模的时候,2.2.1中的做法显然是不能满足要求的,如果非要那样做,将会带来很大的工作量,而且还非常容易出错。这是我们就要使用Makefile来帮助我们完成工程的编译工作。

Makefile文件相当于一个工程文件,文件中描述了工程中的源代码、额外需要的库文件及其路径、额外需要的头文件路径已经编译器类型、编译参数等。通过make命令来调入Makefile进行工程的编译。当执行make命令是,会在当前目录下搜索名称为“Makefile”或者“makefile”的文件,作为当前编译的工程文件,也可一指定其他的工程文件,如make –f MyMakefile

一个简单的Makefile文件内容如下:

#Makefile for Linux(RedHat)

 

宏定义

#目标程序名称

 

BIN_NAME=demo

 

#编译器及编译参数

CC = gcc

CXX = g++

CXXFLAGS = -g

 

 

文件列表

#源代码列表

 

SRCS=\

       demo.cpp \

       func.cpp

#目标文件列表,通过源代码列表生成

OBJS=${SRCS:.cpp=.o}

 

#依赖关系

#depends

all:${BIN_NAME}

.depends : ${SRCS}  #依赖规则

       @echo Creating depend information,please wait ...

 

依赖关系

       ${CXX} -M ${SRCS} > .depends   #执行的命令

 

-include .depends

#%.o:%.cpp

#     @echo Compling file: $? please wait ...

#     ${CXX} ${CXXFLAGS} -c $?

${BIN_NAME}:${OBJS}

       @echo Linking file: $@ please wait ...

       ${CXX} -o $@ ${OBJS}

clean:

       rm -f ${BIN_NAME} ${OBJS} *pure .depends

 

Makefile文件中,大体来说分为“宏定义”、“文件列表”和“依赖关系”三个部分。宏定义中定义Makefile中可以使用的一些宏,定义好一个宏以后,在Makefile的后面部分可以通过宏引用来获得宏的值。像编译器、编译参数等通常都会定义成宏,然后通过修改宏的值就可以修改编译器类型及编译参数。在更复杂的Makefile中,宏定义中还会包含依赖的库文件、需要增加的库文件路径及头文件路径、程序中预定义的宏等。

其实文件列表也是一个宏,只是考虑到他的重要性,所以单独作为一个部分来介绍。在后面的依赖关系中,就会用到这个宏。文件列表中定义了工程包含的所有源文件和这些源文件编译以后生成的目标文件。目标文件可以通过源文件列表生成。上面我们使用${SRCS:.cpp=.o}来生成目标文件列表,意思就是说把源文件中所有扩展名为“.cpp”换成“.o”。

依赖关系部分是Makefile文件的关键,其中定义了多个依赖关系。每一个依赖关系又包括一个依赖规则和一组执行的命令。依赖规则由目标和依赖对象构成,也就是说这个目标依赖于指定的依赖对象。当执行make的时候,如果发现目标不存在或者目标的依赖对象的更新时间比目标还新,就会执行依赖关系中的命令,来完成对目标的编译。Makefile中可以定义多个目标,每个目标对应一个依赖关系和一组命令。执行make的时候,可以执行要编译的目标。如make clean表示检查执行目标cleanall是一个默认的目标,即执行make不加参数的时候,就会默认为要检查执行all这个目标。Makefile中可以为all目标指定依赖对象和要执行的命令。Makefile中,也可以只指定依赖关系,不指定要执行的命令,这种情况大多用于依赖对象也是一个定义的目标。Make在检查依赖关系的时候,如果发现依赖对象也是一个目标,就会先去检查这个目标的依赖关系,看是否需要重新编译这个目标,然后才会执行本依赖关系下面的命令。

为了实现源文件的编译,我们在依赖关系中需要定义源文件和目标文件之间的依赖关系,然后执行程序和这些目标文件之间建立一个依赖关系。这样就可以实现整个工程的编译了。上面的这个Makefile中,包含了两种定义这种依赖关系的方法,一种是通过编译器的“-M”命令来形成依赖关系文件,然后把这个文件的内容include到当前的依赖关系中,另一种是通过匹配替换的做法,直接通过源文件和目标文件的映射关系形成依赖关系。红色字体部分和蓝色字体部分分别代表这两种做法。“-M”参数是gcc/g++编译器特有的,其他编译器一般没有这个参数,应该使用第二种做法。

在不同的系统中,Makefile的语法会有一些细微的差别,在进行多平台移植的时候应该注意这个问题,分别对不同的系统编写不同的Makefile

2.2.3  automakeautoconf

Makefile 基本结构虽然很简单,但是妥善运用这些规则就可以变换出许多不同的花样。却也因为这样,许多刚刚开始学习写Makefile 时会觉得没有规范可以遵循,每个人写出来的Makefile都不大一样,不知道从哪里下手,而且常常会受到自己的开发环境的限制,只要环境参数不同或者路径更改,可能 Makefile 就得跟着修改修改。虽然有 GNU Makefile Conventions (GNU Makefile惯例例)订出一些使用 GNU 程式设计时撰写 Makefile 的一些标准和规范,但是内容很长而且很复杂,并且经常作一些调整,为了减轻程序开发人员维护Makefile 的负担,因此出现了Automake

程序设计者只需要写一些预先定义好的宏 (macro),提交给AutomakeLinux下C++开发初探 - Ellie - 封存记忆处理后会产生一个可以供 Autoconf 使用的 Makefile.in文件。再配合利用 Autoconf产生的自动培植设置文件 configure 即可产生一份符合符合 GNU Makefile 惯例的 Makeifle 了。

       automake的基本用法,是先用autoscan搜索当前目录,然后形成一个configure.scan文件,然后以configure.scan文件为蓝本,形成一个configure.in文件,这是最终需要的文件。然后使用aclocal处理一下本地的宏,再使用autoconf生成一个configure脚本文件。configure脚本文件用来形成最终的makefile。之后编译一个Makefile.am,定义automake需要的一些宏,包括源文件列表等。执行automake,形成Makefile.in文件。这样,需要的准备工作基本完成。编译的时候,通过执行configure脚本,会自动生成Makefile文件,然后再执行make就可以了。

       关于automake的详细用法请查看参考资料。

2.3 运行和调试

Windows不统,Linux下面的文件只有有可执行的权限就可以执行,不像Windows那样依赖于扩展名。所以Linux下的执行程序大都没有扩展名,任何文件只要给了一个执行权限,就可以直接运行。可以通过chmod +x来给一个文件增加可执行的权限。编译后形成的目标文件,通常都已经有了执行的权限,所以可以直接执行。

2.3.1  设置运行环境

对于简单的程序来说,不用设置什么运行环境,直接运行即可。对于负责的程序来讲,可能执行程序和需要的共享库文件不在同一个目录下,就需要设置一些运行环境了。设置运行环境主要就是设置共享库的路径。Linux下的共享库,相当于Windows下面的动态库(dll文件),是一种以.so为扩展名的文件。系统的共享库存放在/usr/lib下面,程序运行的时候会自动去那个目录下面寻找需要的共享库。对于用户自己开发的共享库,一般不能放在/usr/lib目录下面,而是放在用户的程序目录下面。一个 比较复杂的程序,其目录结构分的比较清晰,并不是把所有的文件都放在一个目录下面。下面列出了一个典型的目录结构:

mainpath               程序主目录

mainpath/bin          执行程序目录

mainpath/lib           共享库目录

这种情况下,就需要设置程序的运行环境,指定库文件目录。设置运行环境其实就是在运行程序前设置若干个环境变量,其中共享库的路径就是其中之一,也可一设置一些程序需要的其他的环境变量。Linux下面用LD_LIBRARY_PATH来表示用户共享库的路径。设置完环境变量,就可以运行程序了。

有一种更方便的方法,就是编写执行脚本。Linux下面的脚本相当于Windows下的批处理文件,就是执行一系列的命令。本质上来说,脚本文件就是一个文本文件,编辑完成后增加一个可执行权限即可。在Linux下面的终端窗口,都是基于外壳(shell)的。在Linux中大家最常使用Bourne Shell以及C Shell ,系统默认采用的是Bourne Shell。不同的shell有不同的编程语言,也就是说编写shell脚本的语言,用来实现一些更高级的功能。在脚本中,可以指定本脚本采用的语言。关于shell编程的详细信息,请查阅参考资料。一般情况下,Bourne Shell脚本文件以.sh为扩展名,C Shell脚本文件以.csh为扩展名。这里我们以Bourne Shell为例。

一个典型的程序目录中,一般存在3个脚本:执行脚本(run.sh)、停止进程脚本(kill.sh)和设置环境脚本(setenv.sh)。脚本文件一般都放在程序的主目录下面,进入主目录可以直接通过这些脚本来执行程序。下面简单列出了这几个脚本的内容。

setenv.sh

       #!/bin/sh

path=`pwd`

LD_LIBRARY_PATH=$path/lib:$LD_LIBRARY_PATH

export LD_LIBRARY_PATH

      

run.sh

       #!/bin/sh

path=`pwd`

LD_LIBRARY_PATH=$path/lib:$LD_LIBRARY_PATH

export LD_LIBRARY_PATH

cd bin

./demo

cd ..

      

       kill.sh

#!/usr/bin/sh

for sid in `ps -e |grep demo |awk {'print $1'}`

do

kill $sid

done

      

       值得注意的一点是,外壳其实也是一个进程,运行脚本以后会创建一个新的进程。环境变量依赖于进程,也就是说脚本里面设置的环境变量在脚本完成后将会消失,不回影响外壳进程和其他进程。也就是说,正常情况下我们的setenv.sh相当于不起任何作用。不过可以通过另一种执行方法来保留脚本执行过程中设置的环境变量,即“. setenv.sh”。其实就是在执行命令的前面加上一个‘.’和空格。这样脚本中设置的环境变量就会作用域执行这个脚本的外壳进程,也会影响到以后在这个外壳下面运行的所有进程。

2.3.2  使用调试工具进行调试

程序的调试,是程序开发中最重要的一部分,通过调试来找到程序中的bugLinux面的程序调试工具有很多,包括IDE环境下的调试工具和字符界面的调试工具。这里我们主要介绍字符界面的调试工具。常用的调是工具是dbx,大部分Linux系统下都有。在linux下面,调试工具为gdb,功能跟dbx类似。

Windows类似,要以调试方式运行程序,就必须按照debug版来编译程序。Linux下面的编译一一般通过-g参数来声明使用debug方式进行编译,这样编译后的目标文件中就包含了源代码的信息。下面以gdb为例,介绍一下Linux下面程序调试的基本方法。

1)        以调试方式运行程序

在执行程序所在的目录下,执行gdb,以程序名称为参数即可进入gdb调试界面。如:gdb demo

2)        查看源文件,设置断点

gdb下,使用list命令可以查看主函数所在的文件的内容。默认情况下,一次显示10行内容,再次执行list可以显示后面的10行。

确定要在哪行上设置断点,然后用break命令进行断点设置。如:

break 10 表示在第10行上设置一个断点。

break命令还可以指定要设置断点的文件(如果不是主函数所在的文件),如:

break func.cpp:10 表示在func.cpp的第10行上设置一个断点。

除了在具体的行上设置断点外,还可以在某个函数上设置断点,只要把行号换成函数名即可。

clear命令用来清楚当前行的断点,delete命令可以删除所有设置的断点。

3)        运行程序

设置完断点后,可以在gdb下通过run命令运行程序,如果程序需要什么参数,直接放在run命令后即可,如:

run –start

其中-start是程序允许需要的一个参数。

4)        断点处程序分析

当程序执行到断点的时候,就会定下来等待用户进行处理,这是可以通过一些命令来查看程序当前的运行状态,这些命令包括:

print        打印变量的值,只需要用变量名做命令参数即可

where     显示程序当前的调用栈

set variable 改变变量的值,可能会影响到程序后面运行的结果

5)        单步调试

在断点处处理完成后,可以继续往下执行程序,next命令用来执行一行代码,step命令用来跟踪到即将执行的函数中。continue命令可以继续运行当前程序,直到遇到下一个断点。

       上述流程中,简单介绍了一些如何使用使用gdb来进行程序的调试。gdb中的所有命令,都可以用简写来表示,只要写道能够区分处改命令的字母数即可,比如可以简单的输入n表示next命令。除了上面介绍的命令外,还有很多有用的命令。attachdetach命令,可以在程序正常运行的情况下把gdb帮定到一个进程中,进行调试,调试结束后恢复正常的程序运行,推出gdb对程序运行没有影响;pwd命令可以显示当前的工作目录,cd命令可以改变当前的工作目录;thread命令可以查看当前的线程ID。还有很多别的命令,可以通过help命令获得详细的帮助信息。

       除了dbxgdb外,Linux下面还有很多调试工具,详细信息请看参考资料。

2.3.3  core dump

对于Linux开发人员来说,core dump是再熟悉不过的了。在Linux下面,core dump就是程序bug的最严重的体现。core dump又叫核心转储,当程序运行过程中发生异常,程序以外退出的时候,系统会把程序当前的内存状况存储在一个core文件中,就叫core dump

既然core文件记录了程序运行时的情况,就可以通过core文件来分析错误的原因。使用gdb/dbx 运行程序名 core就可以把程序恢复到发生错误的哪一时刻,可以看到程序在执行哪行代码,调用什么函数时发生了错误。这是,可以借助调试工具的其他命令进行更细致的分析。个人觉得,Solaris下实现的dbx远比gdb强大的多,可以看到更多的信息。

值得注意的是,在linux下面有一个系统限制,就是core文件的大小。如果这个限制设成0,则程序发生异常以后不回产生core文件。如果想得到core文件,请使用ulimit –c命令来调整这个参数。ulimit –a命令可以查看到系统当前的限制参数情况。

3. 常用命令介绍

Linux下面的常用命令,跟Windows下的控制台命令有很大差别。Linux下的命令,可能是一个执行程序,或者一个可以执行的脚本,系统的命令一般都可以在/usr/bin/下面找到。另外需要说明的一点,Linux下面严格区分文件名的大小写,包括命令的大小写。下面简单介绍一下Linux系统下常用的几个命令。

3.1 常用命令列表

ls             列出当前目录下的文件,相当于Windowsdir

cd            切换目录,和Windows下的cd命令相同。

mkdir       创建一个目录。

rm           删除一个文件或者目录。-r参数表示删除的是目录,-f参数表示强制删除。

mv           移动或者修改文件/目录名称。

cp            拷贝文件或目录,-r参数表示拷贝的对象是目录。

ps            查看当前系统的进程情况,-e是最常用的一个参数,表示列出所有进程。

man                查看手册,这是一个最常用的命令,通过他可以查看指定命令或者函数的使用手册。比如man ps就可以产看ps命令的帮助手册,man fopen可以查看fopen函数的用法。

find          在指定目录下查找指定的文件。

grep         在指定文件中查找指定的内容。

vi             编辑文本文件,后面会做详细的介绍。

tar           文件打包和解包工具,通过cvf参数打包,通过xvf解包

gzip                压缩工具,可以把文件压缩成gz文件,通过-d参数进行解压缩,常于tar命令一起使用。

3.2 命令的组合

Linux下面,可以把多个命令组合使用,可以把一个命令的执行结果作为另一个命令的输入。其中,最常跟别的命令组合使用的命令就是grep。在2.3.1中的那个kill.sh脚本中,我们就使用了这样一个组合命令“ps -e |grep demo |awk {'print $1'}”,用来列出当前系统中所有名称为demo的进程的进程号。这种用法是我们开发过程中最常用的方法。另外如果我们想知道我们的程序的网络情况,可以通过netstat命令和grep命令来组合使用,获得指定端口的状态情况,比如netstat –a | grep 43001,这个命令将列出当前系统中所有在端口43001上的连接情况。

通常的grep命令,只能查找当前目录下的文件,比如:grep OPEN_MAX’ *.h 就是查找当前目录下面的所有头文件中是否含有“OPEN_MAX”这个词。而find命令可以查找指定路径下面的所有子目录,发现所有匹配的文件,那么,我们就可以用grep命令和find命令组合,来实现子目录下的文件内容查找。命令格式如下:

grep 'OPEN_MAX' `find /usr/include -name '*.h'`

这样可以查找所有在/usr/include及其子目录下的头文件,看是否包含“OPEN_MAX”。执行的时候,先执行find命令,形成一个文件列表,然后把这个文件列表作为grep的一个参数来执行grep命令。这是命令组合的另一种方式。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics