【命令行魔法:掌握Linux基础工具开发的独门技艺】(三)

简介: 【命令行魔法:掌握Linux基础工具开发的独门技艺】

【命令行魔法:掌握Linux基础工具开发的独门技艺】(二):https://developer.aliyun.com/article/1425532


链接(生成可执行文件或库文件)


  • 在成功编译之后,就进入了链接阶段。
  • 实例: gcc/g++ code.o –o code 或者 gcc/g++ -o code code.o


在这里涉及到一个重要的概念:函数库


  • 一个平台要支持开发,就必须要提前在系统中安装语言的标准头文件+库文件
  • 我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而 没有定义函数的实现,那么,是在哪里实“printf”函数的呢?
  • 最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到 系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函 数“printf”了,而这也就是链接的作用



问题:拿汇编语言举例,先有的汇编编程语言还是先有的汇编程编译器


这个问题似乎和先有蛋还是现有鸡的问题一样,其实不然,早期我们使用二进制写程序的,由于二进制写起来比较麻烦,所以出现了汇编语言,那么此时汇编语言由谁编译呢?肯定是汇编语言编译器,但是这个汇编语言编译器不会是用汇编语言写的,所以汇编语言编译器是用二进制去写的,汇编语言使用二进制编写的编译器去编译的,从而最后形成软件。编译器也是软件,后续就有人用汇编语言写了一个汇编语言编译器通过二进制编写的编译器去编译,最后才出现了我们的汇编语言编译器。对于先有的汇编编程语言还是先有的编程编译器,我们可以确定的是一定先有二进制编写的编译器。


函数库一般分为静态库和动态库两种。



  • 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也 就不再需要库文件了。其后缀名一般为“.a”
  • 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时 链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件,如下所示。
  • gcc hello.o –o hello gcc默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证。
  • 动静态库都是文件,头文件也是文件。


动态库的优点:所有人都去网咖共享一台电脑,比较节省资源,不会出现太多的重复代码 --- 节省的是磁盘空间,内存和网络等资源。缺点:一旦网咖因为运营而关闭,所有人都不能使用了,对库的依赖性比较强,一旦库丢失,所有使用这个库的程序都无法运行。


静态库的优点:不依赖库,同类型平台中都可以直接使用。缺点:可执行程序体积比较大,比较浪费资源 --- 磁盘空间,内存和网络等资源。


gcc默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证。ldd可以查询一个可执行程序所依赖的库文件。


我们也可以通过指令对可执行程序进行静态链接:gcc -o mybin-static mytest.c -static


这里我们也可以发现静态链接形成的可执行程序大小非常大,静态链接是把库中得代码拷贝过来。


但是我们的云服务器默认是没有安装静态库的,所以我们执行:gcc -o mybin-static mytest.c -static 是会报错的


上面报错信息中ld是我们的链接器,-l是gcc的另外一个选项,c就是说明我们缺少一个c标准库,我们库的名称是去掉前缀lib和后缀.so.6。所以我们是需要安装我们的静态库的,执行指令:

sudo yum install -y glibc-static libstdc++-static


安装完我们可以直接去/lib64/libc.a路径下去查看我们安装的静态库


gcc选项


  • -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
  • -S  编译到汇编语言不进行汇编和链接
  • -c  编译到目标代码
  • -o 文件输出到 文件
  • -static 此选项对生成的文件采用静态链接
  • -g 生成调试信息。GNU 调试器可利用该信息。
  • -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
  • -O0 -O1 -O2 -O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
  • -w  不生成任何警告信息。
  • -Wall 生成所有警告信息。


4.Linux项目自动化构建工具-make/Makefile


4.1背景


  • 会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力
  • 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作
  • makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
  • make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一 种在工程方面的编译方法。
  • make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。


我们先来创建一个makefile文件,然后vim打开makefile文件,输出内容:


然后我们在输入make指令就形成了可执行程序mybin。


4.2依赖关系和依赖方法


这里通过一个小故事了解一下它们之间的关系:

 月末了你已经没有生活费了,此时的你就会向你老爸要生活费,然后你向你老爸说了一句我是你的儿子,然后又说月末了我没有生活费了。对老爸说我是你的儿子这句话就是表面依赖关系,解释的是为什么我要帮你,月末了我没有生活费这句话就是具体的依赖方法,解释的是如何帮你。


如果我们再次输入make呢?


此时显示该文件不需要再编译,当前的mybin已经是最新文件了,我们并没有对源文件test.c进行过任何修改,所以源文件没有变化,我们的可执行程序再编译的结果是一样的,所以gcc也就不再编译了。如果我们改一下源文件,那么make会让我们再编译嘛?答案是可以滴。


这里提一个问题,make和makefile怎么知道当前文件时最新的呢?首先我们要清楚,如果这个源文件我们一直没有修改,就算过了一万年,他仍然是最新的,仍然是不可编译的,所以这个肯定是与我们现在所处的时间概念无关的。但是它肯定与时间有关,但是这个时间是对比出来的,常识告诉我们,改一个源文件就需要打开该源文件,修改之后该文件的修改时间就会被改变,只要可执行程序的最近修改时间比所有源文件最近的修改时间新,说明它就是最新的!


我们可以通过stat+filename查看文件的时间信息


当我们再次vim打开该文件进行修改,再次执行stat+filename


此时我们发现文件的Modify时间就发生了变化,然后我们再make指令编译一下源文件,再次执行stat+filename


我们可以发现mybin的时间永远是源文件test.c更新。通过stat我们可以查看到Modify时间,那么Change时间是什么呢?我们之前提到文件=内容+属性。Modify是对于文件内容而言的,而Change是对于文件属性来说的。通过ll指令查看的就是文件的属性:权限,拥有者和所属组等等。


通过给other去掉读权限,我们可以看到文件的Change时间发生变化


随后我们再对文件内容进行修改,此时会发生什么呢?


我们会发现此时的Modify时间改变了,但是Change时间也发生了改变,因为我们对源文件修改的时候,可能增加或者删除了代码,此时我们文件的大小就可能发生变化,所以Change时间也发生了改变。我们再来了解一下Access时间,它是文件的访问时间。


然后我们多次执行访问文件,我们会发现Access时间没有改变,这是因为Access不是实时更新的,而是访问到一定的次数或者是在系统内部做了一定的访问操作,当系统认为可以时间可以更新便会自动更新。


结论:关于对比可执行程序和我们的源文件哪一个更新的时候,我们通常以Modify时间为准。


如果我们想在不改变源文件内容的时候,我们可以通过修改文件的Modify时间去让make能够重新编译。我们可以使用touch命令,如果文件不存在,touch帮我们创建文件,如果文件存在,touch就会帮我们更新文件。


我们还可以打开vim mytest.c,然后将它设置为伪目标,用 .PHONY 修饰,伪目标的特性是,总是被执行的。


此后我们再输入make编译,此时就不再显示文件是最新的而不可被编译


不过我们一般不将我们的目标文件设置成伪文件,而是将clean设置成伪文件,因为清理工作总是要执行的。


我们也可以将上面的makefile这样写:@表示目标文件,^表示源文件列表。


或者也可以这样写,makefile是支持变量定义的,我们可以将它理解为宏,完成的任务是替换。


这里我们还需要了解一个问题mybin是直接依赖我们的test.c吗?根据我们上面提到滴,并不是,我们写的代码test.c需要经过预处理、编译、汇编和链接才可以形成可执行程序mybin。


  • 上面的文件 mybin ,它依赖 test.o
  • test.o , 它依赖 test.s
  • test.s , 它依赖 test.i
  • test.i , 它依赖 test.c


上面的这个依赖关系和依赖方法是类似于栈的,依赖关系先依次入栈,然后再出栈于相应的依赖方法相匹配。


【命令行魔法:掌握Linux基础工具开发的独门技艺】(四):https://developer.aliyun.com/article/1425555

相关文章
|
6天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
24 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
18天前
|
监控 Unix Linux
Linux操作系统调优相关工具(四)查看Network运行状态 和系统整体运行状态
Linux操作系统调优相关工具(四)查看Network运行状态 和系统整体运行状态
31 0
|
25天前
|
存储 前端开发 Linux
Linux系统之部署ToDoList任务管理工具
【4月更文挑战第1天】Linux系统之部署ToDoList任务管理工具
63 1
|
18天前
|
Linux
Linux操作系统调优相关工具(三)查看IO运行状态相关工具 查看哪个磁盘或分区最繁忙?
Linux操作系统调优相关工具(三)查看IO运行状态相关工具 查看哪个磁盘或分区最繁忙?
21 0
|
3天前
|
JSON Unix Linux
Linux系统之jq工具的基本使用
Linux系统之jq工具的基本使用
32 2
|
4天前
|
监控 安全 Linux
Linux系统之安装ServerBee服务器监控工具
【4月更文挑战第22天】Linux系统之安装ServerBee服务器监控工具
42 2
|
4天前
|
编解码 Linux 数据安全/隐私保护
linux工具之curl与wget高级使用
linux工具之curl与wget高级使用
|
17天前
|
Linux
Linux命令行快捷键
Linux命令行快捷键
|
22天前
|
Linux API C语言
FFmpeg开发笔记(一)搭建Linux系统的开发环境
本文指导初学者如何在Linux上搭建FFmpeg开发环境。首先,由于FFmpeg依赖第三方库,可以免去编译源码的复杂过程,直接安装预编译的FFmpeg动态库。推荐网站<https://github.com/BtbN/FFmpeg-Builds/releases>提供适用于不同系统的FFmpeg包。但在安装前,需确保系统有不低于2.22版本的glibc库。详细步骤包括下载glibc-2.23源码,配置、编译和安装。接着,下载Linux版FFmpeg安装包,解压至/usr/local/ffmpeg,并设置环境变量。最后编写和编译简单的C或C++测试程序验证FFmpeg环境是否正确配置。
40 8
FFmpeg开发笔记(一)搭建Linux系统的开发环境
|
23天前
|
资源调度 JavaScript 安全
Linux系统之部署web-check网站分析工具
【4月更文挑战第3天】Linux系统之部署web-check网站分析工具
68 9