【命令行魔法:掌握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

相关文章
|
1月前
|
Linux 数据安全/隐私保护
适用于 Linux 的最佳命令行下载加速器
适用于 Linux 的最佳命令行下载加速器
51 3
|
2月前
|
监控 数据可视化 Ubuntu
|
2月前
|
监控 Unix Linux
Linux系统工具
Linux系统工具
49 6
|
2月前
|
监控 Java Linux
Linux系统之安装Ward服务器监控工具
【10月更文挑战第17天】Linux系统之安装Ward服务器监控工具
61 5
Linux系统之安装Ward服务器监控工具
|
2月前
|
JSON JavaScript Linux
Linux系统之安装cook菜谱工具
【10月更文挑战第15天】Linux系统之安装cook菜谱工具
39 2
Linux系统之安装cook菜谱工具
|
1月前
|
缓存 监控 Linux
Linux性能分析利器:全面掌握perf工具
【10月更文挑战第18天】 在Linux系统中,性能分析是确保软件运行效率的关键步骤。`perf`工具,作为Linux内核自带的性能分析工具,为开发者提供了强大的性能监控和分析能力。本文将全面介绍`perf`工具的使用,帮助你成为性能优化的高手。
100 1
|
1月前
|
缓存 监控 Linux
掌握Linux性能分析:深入探索perf工具
【10月更文挑战第26天】
32 1
|
2月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
106 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
3月前
|
人工智能 监控 Shell
常用的 55 个 Linux Shell 脚本(包括基础案例、文件操作、实用工具、图形化、sed、gawk)
这篇文章提供了55个常用的Linux Shell脚本实例,涵盖基础案例、文件操作、实用工具、图形化界面及sed、gawk的使用。
681 2
|
3月前
|
监控 安全 Linux
如何利用Kali Linux进行网站渗透测试:最常用工具详解
如何利用Kali Linux进行网站渗透测试:最常用工具详解
137 6