⌛️
GNU ☁️
上一篇文章链接: 【Linux学习笔记④】——Shell程序设计【变量 输入与输出 条件表达式 判断语句 循环语句 Shell函数】.
下一篇文章链接: …
● GNU 的全称为 GNU’s Not Unix,这是官方的递归定义,永远找不到本义,是开源软件的幽默。
● GNU工程已经开发了一个被称为 “GNU”(GNU是“不是UNIX”的缩写)的、对 Unix 向上兼容的完整的自由软件系统 (free software system) 。由 Richard Stallman【自由软件运动的精神领袖、GNU计划以及自由软件基金会的创立者、著名黑客】完成的最初的 GNU 工程的文档被称为 ‘GNU宣言’,该宣言已经被翻译成多种其它语言。
● 你可以也可以不用为获取 GNU 软件而支付费用。不论是否免费,一旦你得到了软件,你在使用中就拥有三种特定的自由:
① 首先是复制程序并且把它送给你的朋友或者同事的自由。
② 而后是通过获取完整的源代码,按照你的意愿修改程序的自由。
③ 最后是发布软件的改进版并且有助于创建自由软件社团的自由。
比较项 | GNU | Unix |
---|---|---|
起源 | 由Richard Stallman开发的,他是MIT AI Lab的黑客。 | 由Ken Thompson和Dennis Ritchie为贝尔实验室开发的。 |
最初命名 | GNU是开发的软件的名称。 | 由贝尔实验室开发的名称为AT&T UNIX。 |
独自发挥作用 | 该软件(Shell)本身无法运行,因为它需要一个内核才能与硬件交互。 | UNIX由Shell和内核组成,可以单独运行。 |
依赖 | GNU仅仅是Shell软件,它依赖于任何内核,并且正确地部署了UNIX内核。 | 它不依赖于任何其他操作系统,它具有自己的组件。 |
源代码 | GNU源代码可免费向公众公开,可以根据要求修改代码。 | UNIX源代码不适用于公众。 |
● GUN C 是在 Linux 系统下的 C语言。
● 用高级语言编写的代码必须经过编译和链接,最终生成可执行的目标代码。
① 首先,C/C++源代码会经过编译器,汇编源代码经过汇编器,生成目标文件(.o
文件)。
② 如果有多个文件需要编译,为了便于管理,可将具体操作按一定规则写入makefile
文件,make
工具根据makefile
文件的要求执行相关命令生成目标文件。
③ 如果这些目标代码中的函数需要在其他应用中重复使用,可通过归档文件.ar
将这些.o
文件归档为函数库。
④ 最后,通过链接器将目标文件及相关函数库链接成为共享库、可链接文件或可执行文件。
● GCC(GNU Compiler Collection)是 GNU 下编译器及其相关工具的集合。
● GCC 原名为 “ GNU C 语言编译器 ”,因为它原本只能处理 C 语言。但随着 GCC 的发展,在功能上得到了不断的扩充,现在它具有一下特点:
① 支持多种高级语言(如 C++、Java等)
② 支持多种硬件处理器(例如:x86、ARM等)
③ 支持多种操作系统平台(例如:Linux、Windows等)
■ 语法:gcc [选项] 目标文件 源文件
▶功能:将 C 语言编译为目标代码或可执行文件。
● 常用选项如下:
选项 | 功能 |
---|---|
-S | 编译生成汇编文件(.s ) |
-c | 编译生成目标文件(.o ) |
-o | 指定输出文件名 |
-Wall | 打印警告信息 |
● 例如:
gcc -c test.c # 生成目标代码 test.o
gcc -o test test.o # 将目标文件 test.o 链接为可执行文件 test
■ 语法:as [选项] 汇编文件
▶功能:将汇编语言源代码汇编为目标代码。
● 常用选项如下:
选项 | 功能 |
---|---|
-o | 设置输出文件名 |
-M | 输出链接信息 |
-T commandfile | 指定链接命令文件 |
● 例如:
ld -T linkcmds -o test test.o # 指定链接命令文件
■ 语法:ld [选项] 目标文件列表
▶功能:将若干目标文件和函数库链接在一起,重定位符号引用和数据。(在默认情况下,无须定义链接命令文件,链接器 ld 会使用默认的链接命令文件)
● 例如:
as -o test.o test.s # 将汇编语言源代码 test.s 汇编为目标代码 test.o
# 由于 gcc 是编译工具的集成器, 因此汇编器 as 可以用 gcc -S 代替
● 在开发规模较大的应用项目时,常采用模块化设计方法,即将系统分解为若干个模块。各模块完成各自特定的功能。
● 此时,系统中存在多个源代码文件。当生成最终的可执行文件时,必须逐个编译这些源代码文件,并在最后将所有的目标代码链接为可执行程序…如果这些步骤都需要手工操作就很耗时间。
● 为此 GNU 项目开发了一个用于自动完成这些操作的项目管理工具make
,用户只需将这些步骤按一定的语法规则以命令的方式写入文本文件,一般命名为Makefile
。此后用户只需在命令提示符下输入make
命令,make
工具会根据Makefile
文件中的定义,自动执行一些列编译和链接工作。这便节省了时间。
■ Makefile文件的语法:
目标文件: 依赖文件1 依赖文件2 ... 依赖文件n
命令1
命令2
...
命令n
◆ 说明:① 只有当所依赖的文件被更新时,make
才执行相应的命令更新目标文件。② “命令” 指的是产生目标文件需要执行的命令。
■ make命令的语法:make [选项] [目标]
▶功能:创建指定的目标,如果没有指定目标,则创建第一个目标。make
使用的默认的规则定义文件是 GNUmakefile、makefile、Makefile。
● 常用选项如下:
选项 | 功能 |
---|---|
-o | 设置输出文件名 |
-M | 输出链接信息 |
-T commandfile | 指定链接命令文件 |
● 样例如下:【首先编写一个 Makefile 文件,准备对其进行项目管理】
# test_script
appexam: main.o app.o mod.o lib.o
gcc -o appexam main.o app.o mod.o lib.o
main.o: main.c app.h
gcc -c main.c
app.o: app.c app.h
gcc -c app.c
mod.o: mod.c
gcc -c mod.c
lib.o: lib.c lib.h
gcc -c lib.c
clean:
rm -f*.o # 移除所有后缀为 .o 的文件。因为已经链接好程序了,不再需要这些文件了。
● 然后我们对该脚本进行调用:
make appexam # 指定要创建的目标为 appexam 这个文件
● 然后,我们将得到各目标之间的依赖关系如下图所示:【当某个目标被修改时,则依赖它的所有上级目标(包括上级祖先等)都会被重新编译】
◆ 注:最下面一排的,“main.o” 改为 “main.c”、“app.o” 改为 “app.c”。
● 为了使在 Makefile 中规则的书写更为简洁,也为了能适应不同的开发环境,可在定义 Makefile 定义变量,变量可用于保存文件名列表、命令和命令参数等。make 工具支持 4 种类型的变量:自定义变量、环境变量、预定义变量、自动变量。
● 这类变量有用户自定义,一般用大写字母表示。
■ 语法:变量名=字符串
▶功能:将 “字符串” 赋值给 “变量名”。注:在 Makefile 中无数据类型。
★调用:$(变量名)
● 样例如下:【即把 3.2 的例子改写如下】
# test_script
OBJS=main.o app.o mod.o lib.o
appexam: $(OBJS)
gcc -o appexam $(OBJS) # 相当于命令 gcc -o appexam main.o app.o mod.o lib.o
main.o: main.c app.h
gcc -c main.c
app.o: app.c app.h
gcc -c app.c
mod.o: mod.c
gcc -c mod.c
lib.o: lib.c lib.h
gcc -c lib.c
clean:
rm -f*.o # 移除所有后缀为 .o 的文件。因为已经链接好程序了,不再需要这些文件了。
● make
在运行过程中,会将环境变量转化为同名同值的make
变量,用户也可在Makefile
中对这些变量进行重新定义。
● GNU make
预定义了一些变量(就像英语中的固定搭配的语法),它们在Makefile
文件中可以直接使用。也可以对这些变量进行重新定义。一些常用的预定义变量如下表:
预定义变量名 | 含义 | 默认值 |
---|---|---|
AR | 归档程序 | ar |
AS | 汇编器 | as |
CC | C语言编译器 | cc |
CXX | 带有标准输出的 C 语言预处理程序 | $(CC)-E |
RM | 删除文件的命令 | rm -r |
● 样例如下:【即可把 3.3.1 的例子改写如下】
# test_script
OBJS=main.o app.o mod.o lib.o
appexam: $(OBJS)
$(CC) -o appexam $(OBJS) # 相当于命令 gcc -o appexam main.o app.o mod.o lib.o
main.o: main.c app.h
$(CC) -c main.c
app.o: app.c app.h
$(CC) -c app.c
mod.o: mod.c
$(CC) -c mod.c
lib.o: lib.c lib.h
$(CC) -c lib.c
clean:
rm -f*.o # 移除所有后缀为 .o 的文件。因为已经链接好程序了,不再需要这些文件了。
● 自动变量由make
工具预先定义,具有特定的含义,它的值与规则中的目标和依赖对象有关。下面给出部分常用的自动变量及其定义。
变量 | 功能 |
---|---|
$^ | 所有的依赖文件,以空格隔开,以出现的先后为序 |
$< | 第一个依赖文件的名称 |
$? | 所有的依赖文件,以空格分开,它们的修改日期比目标的创建日期晚 |
$* | 不包含扩展名的目标文件名称 |
$@ | 目标的完整名称 |
● 样例如下:【即可把 3.3.3 的例子改写如下】
# test_script
OBJS=main.o app.o mod.o lib.o
appexam: $(OBJS)
$(CC) -o $@ $^ # 相当于命令 gcc -o appexam main.o app.o mod.o lib.o
main.o: main.c app.h
$(CC) -c -o $@ $< # 相当于 gcc -c -o main.c
app.o: app.c app.h
$(CC) -c -o $@ $<
mod.o: mod.c
$(CC) -c -o $@ $<
lib.o: lib.c lib.h
$(CC) -c -o $@ $<
clean:
rm -f*.o # 移除所有后缀为 .o 的文件。因为已经链接好程序了,不再需要这些文件了。
● 通常为了产生目标文件,需要在目标文件和依赖对象之间建立明确的规则,定义如何生成目标的流程,但有时可简化这些操作。即用以下三种规则。
● GNU make 定义了内置的隐含规则,在不给出产生目标文件的命令时,由make
自动添加。
● 样例如下:【即可把 3.3.4 的例子改写如下】
# test_script
OBJS=main.o app.o mod.o lib.o
appexam: $(OBJS)
$(CC) -o $@ $^ # 相当于命令 gcc -o appexam main.o app.o mod.o lib.o
main.o: main.c app.h
app.o: app.c app.h
mod.o: mod.c
lib.o: lib.c lib.h
clean:
rm -f*.o # 移除所有后缀为 .o 的文件。因为已经链接好程序了,不再需要这些文件了。
● 定义:将具有某后缀的文件(例如.c
文件)转换为具有另外一种后缀的文化(例如 .o
文件)的方法。使用方法是将每个以两个成对出现的后缀名定义。
● 样例如下:【即可把 3.4.1 的例子改写如下】
# test_script
.c.o:
gcc -c $<
OBJS=main.o app.o mod.o lib.o
appexam: $(OBJS)
$(CC) -o $@ $^ # 相当于命令 gcc -o appexam main.o app.o mod.o lib.o
clean:
rm -f*.o # 移除所有后缀为 .o 的文件。因为已经链接好程序了,不再需要这些文件了。
● 模式规则是对具体规则的进一步抽象,定义了一类具有相同行为特点的规则,例如用%
表示通配符号。
● 样例如下:【即可把 3.4.2 的例子改写如下】
# test_script
%.c: %.o
$(CC) -c $<-o $@
OBJS=main.o app.o mod.o lib.o
appexam: $(OBJS)
$(CC) -o $@ $^ # 相当于命令 gcc -o appexam main.o app.o mod.o lib.o
main.o: main.c app.h
app.o: app.c app.h
mod.o: mod.c
lib.o: lib.c lib.h
clean:
rm -f*.o # 移除所有后缀为 .o 的文件。因为已经链接好程序了,不再需要这些文件了。
● 函数库:由若干目标文件按某种格式构成的集合,目标文件是由源文件经过编译生成的中间代码。在进行软件开发的过程中,往往会积累许多可复用代码。为了提高软件的开发效率,可将这些代码编译,并分类打包成函数库,供其他项目使用。
● 函数库可分为两类:静态库和共享库。它们俩在与应用程序的链接方式上具有不同的特点。
① 在链接静态库时,会将使用的静态库对象嵌入到可执行映像文件中。
② 在链接共享库时,仅当可执行映像文件中保留加载目标对象所需的信息后,在调用时,才真正将目标对象加载至内存。
● 静态库由ar
工具创建。经编译的应用程序和静态库链接时,链接器将静态库中被调用的对象嵌入到可执行映像文件中,这样在没有静态库的环境下,应用程序也能独立运行。
● 静态库文件的命名规则是libxxx.a
,以lib
开头,.a
作为文件名后缀
■ 语法:ar [选项] [归档文件] 目标文件列表
▶功能:用于创建、修改和查询归档文件。
● 常用的一些选项如下:
选项 | 功能描述 |
---|---|
-c | 创建一个函数库 |
-r | 向函数库中插入目标对象,若存在则替换 |
-u | 若函数库中已经存在同名目标,则用新目标更新 |
-t | 显示函数库中目标对象列表 |
-d | 从函数库中删除目标对象 |
● 样例如下:【用两个 C 源文件创建静态库】
第一个命名为 “test_1.c” 的 C 语言源文件:
// test_1.c
int add(int x, int y)
{
return x + y;
}
第二个命名为 “test_2.c” 的 C 语言源文件:
// test_2.c
int func(int count)
{
int sum = 0;
for(int i = 1; i < count; i++)
sum += i;
return sum;
}
创建静态库的步骤如下:
(1) 编译 test_1.c 和 test_2.c, 生成目标文件
gcc -c -Wall test_1.c # 生成目标文件 test_1.o
gcc -c -Wall test_2.c # 生成目标文件 test_2.o
(2) 创建静态库 ---> 并将库命名为 test_lib.a
ar -cru lib_test.a test_1.o test_2.o
// 注: 选项 c 告诉了 ar 创建一个新的静态库, 除非该静态库已存在
// 选项 r 告诉了 ar 替换已经存在的目标文件
// 选项 u 告诉了 ar 被替换的目标文件必须是最新的
(3) 为了使用静态库, 首先需定义静态库的应用接口, 代码如下:
// test_interface.h
#ifndef _DEMOLIB_API_H
#define _DEMOLIB_API_H
extern int add(int x, int y); // 外部函数声明(即说明该函数的实现在外部)
extern int func(int count);
#endif
下面是使用lib_test.a
的测试程序(该文件命名为test_file.c
),代码如下:
// test_file.c
#incldue<stdio.h>
#incldue"lib_test.a"
int main()
{
int val, x, y;
x = 2;
y = 18;
val = add(x, y);
printf("The mult of x and y is %d.\n", val);
val = func(100);
printf("The sum is %d.\n", val);
return 0;
}
利用静态库test_lib.a
编译生成可执行文件test
的具体方法如下:
gcc test_file.c -L. lib_test.a -o test // -L. 表示静态库在当前目录下
● 具体运行结果如下:
◆ 说明:上面的倒数第二个箭头的命令行 “vi test_file,c
” 写错了,改成 “vi test_file.c
”
● 静态库的特点:
① 运行时无需外部库的支持。
② 较高的运行速度。
③ 可执行文件具有较大的体积。
④ 不容易维护。
● 经过编译后的应用程序在和共享库链接时,与静态库不同,没有将共享库中的目标对象嵌入至映像文件,而是只在生成的可执行映像文件时嵌入。因此,离开了共享库的支持,应用程序就无法运行。
● 共享库文件的命名规则是libxxx.so
。
● 共享库的创建、使用和加载和静态库类似,这里不在赘述。
● 注意:若在当前目录下同时存在lib_test.a
和lib_test.so
,在默认情况下首先会使用共享库,若需要使用静态库则需要加上选项-static
,示例如下:
gcc -static test_file.c -L. -lib_test.a -o test
● 共享库的特点:
① 可执行的文件体积小。
② 容易维护
③ 不能离开动态库独立运行
④ 运行速度比较慢
● 动态链接库是运用共享库的一种方式,在运行的任何时刻都可以动态加载共享库。与一般使用的共享库不同,通常应用程序在启动时,不立即加载共享库,而是在需要时,动态加载共享库。在这种情况下,称共享库为动态链接库。(.dll
)
[1]《GNU/Linux编程》
人民邮电出版社
[2]GNU和Unix的区别
链接: https://www.vsdiffer.com/gnu-vs-unix.html.
上一篇文章链接: 【Linux学习笔记④】——Shell程序设计【变量 输入与输出 条件表达式 判断语句 循环语句 Shell函数】.
下一篇文章链接: …
⭐️ ⭐️