Linux编程环境

Linux编程环境

【学习笔记】

vi编辑器

vi 文件名        #进入文件编辑模式

一般模式:

	yy           #复制当前行
	y数字y       #复制多少行内容
	p            #箭头移动到目的行粘贴
	u            #撤销上一步
	dd           #删除当前行
	d数字d       #删除光标后多少行
	x            #删除一个字母(相当于delete)
	X            #删除一个字母(相当于backspace)
	yw           #复制一个词
	dw           #删除一个词
	shift+^      #移动到行头
	shift+4      #移动到行尾
	1+shift+g    #移动到页头
	shift+g      #移动到页尾
	N+shift+g    #移动到目标行

编辑模式:

	i            #当前光标前
	a            #当前光标后
	o            #当前光标行的下一行
	delete       #删除
	esc          #退出编辑模式

指令模式:

	:w           #保存
	:q           #退出
	!           #强制执行
	:wq!         #强制保存退出

gcc编译器

GCC 是一个交叉平台编译器,能够在当前CPU 平台上为多种不同体系结构的硬件平台开发软件,因此尤其适合在嵌入式领域的开发编译。

gcc编译支持文件

.c,C 语言源代码
.h,程序所包含的头文件
.i,已经预处理过的C 源代码文件
.s,汇编语言源代码文件
.o,编译后的目标文件

gcc编译流程

写个hello.c源代码

gcc hello.c
#include<stdio.h>
int main()
{
printf(“Hello world!\n”);
return 0;
}

保存、退出

1、预处理

该阶段,编译器将上述代码中的stdio.h编译起来,可用“-E”选项查看

gcc格式为

gcc [选项] 要编译的文件 [选项] [目标文件]    #目标文件可缺省,默认生成.out文件
gcc -E hello.c -o hello.i  #-o:指目标文件;-i:指  已经预处理过的源程序

预编译阶段将“stdio.h”中的内容插入到hello.i文件中

2、编译

该阶段,gcc首先检查代码的规范性、语法错误;检查无误后,gcc把代码翻译成汇编语言。可用“-S”选项查看

gcc -S hello.i -o hello.s

3、汇编

汇编阶段是把编译阶段生成的“.s”文件转成目标文件。读者在此可使用选项“-C”看到汇编代码已转化为“.o”的二进制目标代码。

gcc –c file.s –o file.o

4、链接

在预编译中包含进的“stdio.h”中也只有该函数的声明,而没有定义函数的实现。那么,是在哪里实现“printf”函数的呢?答案是:系统把这些函数实现都做到名为libc.so.6 的库文件中去了。

在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,从而链接到libc.so.6 库函数,这样就能实现函数printf 了,这也就是链接的作用。

gcc file.o –o file

gcc编译选项

gcc [选项] [文件]

-E:使用此选项表示仅作预处理,不进行编译、汇编和链接。
-S:编译到汇编语言不进行汇编和链接。
-c:编译到目标代码。
-o:文件输出到文件。
-static:此选项将禁止使用动态库,所以,编译出来的东西一般都很大,也不需要什么动态
链接库即可运行。
-share:此选项将尽量使用动态库,所以生成文件比较小,但是需要系统有动态库。
-I dir:在头文件的搜索路径列表中添加dir 目录。
-L dir:在库文件的搜索路径列表中添加dir 目录。
-llibrary:链接名为library 的库文件。

gdb调试器

Linux 的大部分特色源自于Shell 的GNU(GNU symbolic debugger)调试器,也称作gdb

gdb使用流程

启动gdb

以一段代码为例

gcc gdb_sample.c
#冒泡排序
void bubble_sort(int arr[],  int len) {
    int temp;
    for (int i = 0; i < len; i++) {
        for (int j = 0; j < len-1-i; j++) {//从第一个数开始,依次和后一个数比较
            if (arr[j] > arr[j + 1]) {                          #把较大的数交换到后边
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }    #内层循环结束后,最大一个数排到最后,保持不动
           #每结束一次内层循环,比较次数少一次,因此有-i
        }
    }
}

int main(){
    int arr[] = { 22, 45, 67, 36, 93, 444, 26, 84, 3, 7 };
    int len = (int)sizeof(arr) / sizeof(arr[0]);
    bubble_sort(arr, len);
    for (int i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
}

退出保存后使用gcc对代码进行编译,注意一定要加上选项“-g”,这样编译出的可
执行代码中才包含调试信息,否则之后gdb 无法载入该可执行文件。

启动gdb进行调试:

gcc -g gdb_sample.c -o gdb_sample

在gdb输入命令时,可用不用打全命令,在Linux下,可用敲击两次Tab键来补全命令。

调试命令

查看文件

输入l(list),查看所载入的文件(输入l即可,系统会自动识别)

设置断点
break <function>    #在进入指定函数时停住
break <linenum>     #在指定行号停住
break +offset,break -offset  #在当前行号的前面或后面的offset 行停住
break filename:linenum	#在源文件filename 的linenum 行处停住
break filename:function	#在源文件filename 的function 函数的入口处停住
break *address	#在程序运行的内存地址处停住
break	#break 命令没有参数时,表示在下一条指令处停住
break…if <condition>	#…可以是上述的参数,condition 表示条件,在条件成立时停住
查看断点
info breakpoints [n]
info break [n]
运行代码

输入r(run)即可,gdb 默认从首行开始运行代码,若想从
程序中指定行开始运行,可在r 后面加上行号:

r 10  		#第十行开始执行
查看变量值
p [变量]
恢复程序运行和单步运行
step <count>	#单步跟踪,如果有函数调用,它会进入该函数
next <count>	#同样单步跟踪,但如果有函数调用,不会进入该函数
set step-mode,set step-mode on	#打开step-mode 模式
set step-mod off	#关闭step-mode 模式
finish	#运行程序,直到当前函数完成返回
until 或u	#当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体

Make工程管理

Makefile基本规则

一个简单的Makefile:结构组成

all:     #目标名字放在":"前面,名字由字母和下划线组成
		 #":"后面是需要链接的文件
	echo "Hello World"    #注意echo前必须有一个Tab键位,不能是空格
	#echo 后面是生成目标的命令
test:
	echo "Test Target"   #一个makefile可以定义多个目标

实例演示

vi max.h

int max (int a, int b);

vi max.c

int max(int a,int b){
	if(a>=b){
		return a;
	}
	else{
		return b;
	}
}

vi min.h

int min(int a,int b);

vi min.c

int min(int a,int b){
	if(a<b){
		return a;
	}
	else{
		return b;
	}
}

vi main.c

#include <stdio.h>
#include "max.h"
#include "min.h"

int main(int argc, char* argv[]){
	int a = 5;
	int b = 3;
	int maxNum= max(a,b);
	int minNum = min(a,b);
	printf("the max value is %d\nthe min value is %d\n",maxNum,minNum);
        return 0;
}
一般编译流程

首先编译依赖库文件,然后编译main函数

编译依赖库

编译max.c

gcc -c max.c -o max.o

编译min.c

gcc -c min.c -o min.o
编译main函数

编译main.c

gcc max.o min.o  main.c -o main
执行main
./main
输出结果
the max value is 5 
the min value is 3

如果依赖函数过多,或者调用o库文件多,这样编译将非常麻烦

于是采用makefile方式来进行简化

Makefile编译方式
创建Makefile文件
vi Makefile			#创建Makefile
#编辑Makefile(模板)
simple:main.o max.o min.o
    gcc -o simple main.o max.o min.o					#Tab间隔
main.o:main.c
	gcc -c main.c -o main.o
max.o:max.c
    gcc -c max.c -o max.o
min.o:min.c
    gcc -c min.c -o min.c
运行Makefile

方法一:在Makefile所在的目录下运行命令

make

方法二:运行命令make all

make [目标名]
输出
gcc -c max.c -o max.o
gcc -c min.c -o min.o
gcc max.o min.o main.c  -o simple
执行
./simple
输出
the max value is 5 
the min value is 3

Makefile假目标

在前面的simple 项目中,现在假设在程序所在的目录下面有一个clean 文件,这个文件也可以用过touch命令来创建。

假目标可以采用.PHONY关键字来定义,需要注意的是其必须是大写字母

.PHONY:clean		#假目标

simple:main.o max.o min.o
    gcc -o simple main.o max.o min.o					#Tab间隔
main.o:main.c
	gcc -c main.c -o main.o
max.o:max.c
    gcc -c max.c -o max.o
min.o:min.c
    gcc -c min.c -o min.c
    
clean:
	rm main.o min.o max.o simple

采用.PHONY 关键字声明一个目标后,make 并不会将其当作一个文件来处理,而只是当作一个概念上的目标。对于假目标,我们可以想像的是由于并不与文件关联,所以每一次make 这个假目标时,其所在的规则中的命令都会被执行

Makefile变量

在Makefile 中通过使用变量来使得它更简洁、更具可维护性

自动变量

$@	#表示一个规则中的目标。当我们的一个规则中有多个目标时,$@所指的是其中任何造成命令被运行的目标。(表示的是目标的集合)
$^	#表示的是规则中的所有先择条件。(表示的是依赖的集合)
$<	#表示的是规则中的第一个先决条件。(表示目标列表里最后一个依赖)

【注意】由于在Makefile 中“$”具有特殊含义,因此,如果想采用echo 输出“$”,则必需用两个连着的“$”。还有就是,“$@”对于Shell 也有特殊的意思,我们需要

在“$$@”之前再加一个脱字符“\”。

其他一些字符的表示:

%.o 表示所有的.o文件
%.c 表示所有的.c文件
实例:
.PHONY:clean

CC = gcc				#定义变量
RM = rm
OBJ = simple
OBJS = main.o max.o min.o

$(OBJ):$(OBJS)
	$(CC) -o $@ $^
main.o:main.c
	$(CC) -c $^ -o $@
max.o:max.c
	$(CC) -c $^ -o $@
min.o:min.c
	$(CC) -c $^ -o $@
    
clean:
	@$(RM) $(OBJS) $(OBJ)

Makefile函数

addprefix函数

addprefix 函数是用来给字符串中的每个子串前加上一个前缀,其形式是:$(addprefix prefix, names…)

.PHONY:all
SOURCE = main.c max.c min.c
OBJS = /home/pyma/Makefile
add source = $(addprefix $(OBJS)/. $(SOURCE))

all:
	@echo $(add_source)

filter函数

filter 函数用于从一个字符串中,根据模式得到满足模式的字符串,其形式是:$(filter pattern..., text)

.PHONY:all
SOURCE = main.c max.c min.c
OBJS = /home/pyma/Makefile
add source = $(addprefix $(OBJS)/. $(SOURCE))
sources = $(filter %.c %.s,$(add_source)

all:
	@echo $(add_source)

从结果来看,经过filter 函数的调用以后,sources 变量中只存在.c 文件和.s 文件,而.h 文件则被过滤掉了

filter-out函数

filter-out 函数用于从一个字符串中根据模式滤除一部分字符串,其形式是:$(filter-out pattern..., text)

.PHONY:all
SOURCE = main.c max.c min.c
OBJS = /home/pyma/Makefile
add source = $(addprefix $(OBJS)/. $(SOURCE))
sources = $(filter-out %.h,$(add_source)

all:
	@echo $(add_source)

从结果来看,filter-out 函数将以“.h”为后缀的文件从add_source 变量中给滤除了。可以看出,filter 与filter-out 是互补的

wildcard函数

wildcard 是通配符函数,通过它可以得到我们所需的文件,这个函数相当于我们在Windows或是Linux 命令行中的“*”。其形式是:$(wildcard pattern)

.PHONY:all
SOURCE = $(wildcard *.c)
OBJS = /home/pyma/Makefile
add source = $(addprefix $(OBJS)/. $(SOURCE))

all:
	@echo $(add_source)

运行结果:从当前Makefile 所在的目录下通过wildcard 函数得到所有的C 程序源文件

patsubst函数

patsubst 函数是用来进行字符串替换的,其形式是:$(patsubst pattern, replacement, text)

.PHONY:clean

CC = gcc				#定义变量
RM = rm
OBJ = simple
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))

$(OBJ):$(OBJS)
	$(CC) -O $@ $^
main.o:main.c
	$(CC) -c $^ -o $@
max.o:max.c
	$(CC) -c $^ -o $@
min.o:min.c
	$(CC) -c $^ -o $@
    
clean:
	@$(RM) $(OBJS) $(OBJ)

OBJS 变量中采用patsubst 函数进行字符串替换,将所有的.c 文件都替换成.o 文件。patsubst 函数可以使用模式,所以其也可以用于替换前缀等,功能更加强大

选自华清远见《嵌入式操作系统》

posted @ 2021-05-11 19:02  水鸽  阅读(163)  评论(0编辑  收藏  举报