Linux基本用法(六)程序开发

C程序编译

gcc可接收处理多种文件,包括归档文件(.a),C语言源文件(.c),C++源文件(.C,.cc,.cxx),汇编语言源文件(.s),预处理输出文件(.i),目标代码(.o)

gcc [option] filelist

-c:只编译,不链接成可执行文件。编译器只是由输入的.c等为后缀的源代码文件生成.o为后缀的目标文件,通常用于编译不包含主程序的子程序文件。
-g:产生调试器gdb所必需的符号信息,要对源代码进行调试,就必须在编译程序时加入这个选项
-O[level]:对程序进行优化编译,链接,采用这个选项,整个源代码会在编译,链接过程中进行优化处理,这样产生的可执行文件的执行效率较高,但是,编译、链接的速度就相应地要慢一些。数字越大优化程度越高。
-o output_filename sourcename:确定输出文件的名称为output_filename,如果不用output_filename,默认输出文件为a.out
-pg:产生供代码剖析工具gprof使用的信息
-S:跳过汇编和链接阶段,保留编译产生的汇编代码(.s文件)
-v:产生尽可能多的输出信息
-w:忽略警告信息
-W:产生比默认模式更多的警告信息
-ansi:强制ANSI标准
-l libfile:链接库文件
-m typeName:根据CPU类型优化代码

需要编译的文件可以放在option前,也可放在option后

gcc f1 f2 ... fN -o outputfile

一般而言,文件数量多时,采取将各源文件编译为目标文件的操作先,然后再链接形成可执行文件,如:

gcc -c f1.c
gcc -c f2.c
...
gcc -c fN.c
​
#也可写成gcc -c f1.c f2.c ... fN.c
gcc f1.o f2.o ... fN.o outputfile

如此,当某个文件发生修改时,可只编译该文件,再将所有文件链接,节省时间。

使用程序库时,需要用-l命令链接,如链接math.h

gcc f1.c -lm -o outputfile

make:处理模块化c程序

make [options] [target] [Macro Definition]
​
-h 或 --help:输出帮助信息。
-d:输出大量调试信息
-n:只打印需要执行的命令而不执行,测试模式
-s:是一个用于静默执行Makefile的命令选项。它会在执行过程中不输出任何信息,包括命令的执行结果和错误信息。
-j 或 --jobs:指定并行执行的任务数,即同时编译的文件数。
-I 或 --include-dir:指定头文件的搜索路径。
-f 或 --file:指定使用的Makefile文件,可以多次使用来指定多个文件。在文件名不是makefile或Makefile时需要使用此参数。
-k 或 --keep-going:即使遇到错误,也继续执行后续的编译任务。
​

make语法规则:

目标文件列表:依赖文件列表

<Tab>命令列表

目标文件列表中文件之间用空格隔开,依赖文件列表亦然。命令列表用回车隔开用户命令,每条命令都需要以<Tab>命令起头

如果想要强制编译系统,可以在make命令前执行touch命令

编写的makefile文件中各命令的执行顺序由编译过程决定,与makefile中的书写顺序无关。

make支持宏替换,需在makefine顶端定义宏:

makefile
​
macro_name = text
​
或
define macro_name
text
endef

其中,macro_name是您为宏指定的名称,而text是宏所代表的文本片段或命令。引用宏时采用$(macro_name)

宏使用参考

$(TARGET): $(SRCS)  
 gcc $(CFLAGS) -o $(TARGET) $(SRCS) $(LDFLAGS)

$(TARGET)$(SRCS)分别代表目标文件名和源文件列表,$(CFLAGS)$(LDFLAGS)分别代表编译选项和链接选项

CFLAGS默认值是-O(编译优化)

makefile文件中可以使用任意shell命令,如压缩等,可使用make command来调用,不过仍然按照上述的make规则语法书写例:

#marco_name definition
CC = gcc
OPTIONS = -O3 -o
OBJECTS = f1.o f2.o f3.o
SOURCES = f1.c f2.c f3.c
HEADERS = f1.h f2.h f3.h
objectfile : $(OBJECTS)
    $ (CC) $ (OPTIONS) objectfile $ (OBJECTS) -lm
​
f1.o:$(HEADERS)
...
​
all.tar:$(SOURCES) $(HEADERS) makefile
    tar -cvf - $(SOURCES) $(HEADERS) makefile >all.tar
​
​

调用make all.tar时,会执行将makefile本身以及编译过程中所涉及到的源文件,头文件打包的命令。而在make时不会执行。

常用的内建宏定义:

  1. MAKE:当前使用的make命令的路径。

  2. MAKECMDGOALS:当前make命令的参数。

  3. MAKEFILE_LIST:当前加载的Makefile的列表。

  4. CURDIR:当前目录的路径。

  5. PREREQ:所有先决条件的列表。

  6. FIRST_PREREQ:第一个先决条件。

  7. DIR:当前目录的名称。

  8. NOTDIR:去除目录路径后的文件名。

  9. SUFFIX:文件名的后缀。

  10. BASENAME:去除后缀后的文件名。

  11. ADDSUFFIX:给文件名添加后缀。

  12. ADDPREFIX:给文件名添加前缀。

  13. JOIN:将两个列表连接成一个。

  14. WORDLIST:从列表中提取单词。

  15. FILTER:过滤列表中的元素。

  16. FINDSTRING:查找字符串中的子串。

  17. SORT:对列表进行排序。

  18. IFELSEENDIF:条件判断语句。

  19. foreachendforeach:循环语句。

  20. callendcall:调用函数语句。

这些内建宏定义可以在Makefile中直接使用,无需额外的初始化。它们提供了一些常用的功能,使得Makefile的编写更加简单和灵活。

补充

$@:比较当前目标文件的名字

$?:比当前目标文件新的依赖文件列表

$<:比当前目标文件新的第一个依赖文件

$^:用空格隔开所有依赖文件(重复出现的文件名只保留一个)

target: dependency  
 gcc -o $@ $<

target是目标文件名,dependency是依赖文件名。当执行make target命令时,Makefile会执行gcc -o $@ $<命令来编译目标文件。

在这个命令中,$@被替换为目标文件名target$<被替换为依赖文件名dependency

在makefile中,以@开头的命令不会显示到屏幕上,除非使用了make -n

使用.PHONY来声明这个目标是一个“假”目标,防止make命令误解。

下面是一个简单的例子来说明.PHONY的用途:

.PHONY: clean  
  
clean:  
 rm -f *.o

在这个例子中,clean是一个假目标,它并不表示一个实际存在的文件或目录。当执行make clean命令时,make命令会执行rm -f *.o命令来删除所有的.o文件,即使存在一个名为clean的文件或目录,make clean命令也会正确执行。

ar:创建、修改、释放库/归档文件

通常用于创建静态库,但也可以用于创建和提取其他类型的存档文件

ar命令的基本语法如下:

ar [选项] [操作] [文件名]

文件名必须以.a结尾,创建后即可被C编译器和Linux库装载器ld所引用

选项可以不加连字符'-',又称为关键字

创建一个名为lib.a的静态库,并将两个目标文件file1.ofile2.o添加到库中,可以使用以下命令:

ar rcs lib.a file1.o file2.o

这将创建一个名为lib.a的存档文件,并将file1.ofile2.o添加到其中。-r选项表示将文件替换现有的存档文件中,不存在则会创建,-c选项表示创建一个新的存档文件(如果它不存在),-s选项表示创建目标文件的索引,不需要创建索引时使用大写S参数,建立索引可提高目标模块的搜索速度并允许模块间函数互相调用,不管排列顺序如何

  • -x:从存档文件中提取文件。ar x lib.a obj.o

  • -d:删除存档文件中的文件。

  • -m:替换存档文件中的文件

q关键字可以将新的目标文件追加至归档文件的末尾

ar tv libxxx.a

t显示归档文件的内容,v显示详细信息

创建好归档文档后,可在gcc中链接,用法示例如下:

  1. 下命令编译和链接您的代码:

gcc main.c -L. -l:lib.a -o my_program

这里的-L.告诉编译器在当前目录中查找库文件,-l:lib.a指定要链接的库文件名称,-o my_program指定生成的可执行文件的名称。

可以用`command```这一命令替换的方法获取批量目标文件

ar s lib.a 等价于ranlib lib.a

查看库文件信息

nm工具,查看库文件或目标文件符号表(符号名称、类型、大小、位置)

nm命令的基本语法如下:

nm [options] [file...]

常用的选项包括:

  • -a:显示所有符号,包括隐藏的符号。

  • -C:使用C语言的格式化方式显示符号。

  • -D:显示动态符号。

  • -g:显示外部符号。

  • -n:以数字方式显示符号名。(按地址)

  • -r:以逆序方式显示符号列表。

  • -s:显示符号大小。

  • -u:显示未定义的符号。

  • -f: 选择显示格式,后面可接sysv,bsd,posix等选项,默认为bsd

版本控制

RCS版本管理系统:以发布号.阶段号.分支号.序列号维护版本号

CVS并行版本系统:RCS的一个前端。允许多个开发者并行修改,提供冲突解决机制

静态分析工具

gprof工具,在使用前,gcc链接需要添加-pg参数(或-p),使得程序运行结束后生成的gmon.out文件可以供gprof分析

:gprof默认不支持多线程程序,默认不支持共享库程序

gprof [option] [executable.file] [output.file]

-b 不再输出统计图表中每个字段的详细描述。

-p 只输出函数的调用图(Call graph的那部分信息)。

-q 只输出函数的时间消耗列表。

-e Name 不再输出函数Name 及其子函数的调用图(除非它们有未被限制的其它父函数)。可以给定多个 -e 标志。一个 -e 标志只能指定一个函数。

-E Name 不再输出函数Name 及其子函数的调用图,此标志类似于 -e 标志,但它在总时间和百分比时间的计算中排除了由函数Name 及其子函数所用的时间。

-f Name 输出函数Name 及其子函数的调用图。可以指定多个 -f 标志。一个 -f 标志只能指定一个函数。

-F Name 输出函数Name 及其子函数的调用图,它类似于 -f 标志,但它在总时间和百分比时间计算中仅使用所打印的例程的时间。可以指定多个 -F 标志。一个 -F 标志只能指定一个函数。-F 标志覆盖 -E 标志。

-z 显示使用次数为零的例程(按照调用计数和累积时间计算)。

gprof产生的信息

%timeCumulativesecondsSelf SecondsCallsSelfTS/callTotalTS/callname
该函数消耗时间占程序所有时间百分比程序的累积执行时间(只是包括gprof能够监控到的函数)该函数本身执行时间(所有被调用次数的合共时间)函数被调用次数函数平均执行时间(不包括被调用时间)(函数的单次执行时间)函数平均执行时间(包括被调用时间) (函数的单次执行时间)函数名
Index%timeSelfChildrenCalledName
索引值函数消耗时间占所有时间百分比函数本身执行时间执行子函数所用时间被调用次数函数名

第一份报告包含的是函数列表,并按照执行时间排序(包含分支执行时间)

第二份报告显示程序的总运行时间,其中called项以“该函数被父函数调用的次数”/“该函数被调用的总次数”显示,函数的递归调用不包含在"/"后面的次数。name中包含函数名和索引号,若函数是循环,则会在函数名和索引号之间显示循环号。Index对应了函数的唯一索引号,与当前分析的函数位于同一行,在其上方(---以下)为其父函数,在其下方(---以上)为子函数

gprof只能分析应用程序在运行过程中消耗掉的用户时间,无法得到程序内核空间的运行时间,可用time看整个程序的运行时间组成。

动态分析工具

gdb

gdb启动程序用法

gdb bugging.c

启动时默认读入~/.gdbinit文件并执行里面的命令,可以用-n忽略此文件

-h:列出命令行的选项简要介绍

-q:禁止显示介绍信息和版权信息

-s[file]:使用保存在指定文件中的符号表

使用gdb前,使用gcc的-g参数编译程序以加入调试时所需要的符号表

同时,可以用nl -ba file.c为.c文件中的每一行前添加一个数字行号方便调试,-b表示添加行号,-a表示将行号显示为数字。

在gdb运行后使用的调试命令

  1. run:开始运行你的程序。

  2. start:开始执行程序。

  3. n:单步执行(next)。

  4. finish:执行完当前函数,返回并停在上个调用的函数。

  5. quit:退出gdb模式。

  6. b:在程序的第8行设置断点(8可以改成函数名,表示在函数处设置断点)。

  7. c:继续执行程序,程序会在设置断点处停下来。

  8. display:将b的值显示出来。

  9. info:查看一共设置了几个断点。

  10. delete:删除编号为3的断点。

  11. watch:当程序访问某个存储单元时中断 。

  12. backtrace:回溯程序发生错误时的位置

  13. step:单步执行,会深入函数内部

  14. next:单步执行,但不会进入函数内部

  15. x [addr]:查看addr处的内容

  16. print [str]:打印出变量或表达式得到值

  17. list:列出程序部分或全部源代码。后面可以接行号,文件名:行号,函数名,文件名:函数名,地址。

gdb调试时仍然允许执行外部shell命令。

调试完毕后,可以选用编译优化选项重新编译可执行文件,也可用strip file删除掉可执行文件中的调试信息。

time

time filename,详情可参考:

Linux time命令 | 菜鸟教程 (runoob.com)

显示格式为

real xxx

user xxx

sys xxx

real为实际时间,user为用户cpu时间(真正的代码执行时间),sys为系统cpu时间(系统活动所花费的时间)。

由于可能有多个进程同时运行,总时间并不一定等于系统时间与用户时间之和

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值