Linux系统编程学习 NO.8 ——make和Makefile、进度条程序

前言

今天是1024程序员节,不知不觉离第一次写博客已经过去了一年了。在此祝各位程序员不写bug,不再秃头。

make和Makefile

什么是make和Makefile?

make和Makefile是软件开发时所用到的工具和文件。make是一个指令工具。Makefile是一个当前目录下的文件。make和Makefile可以提高我们在Linux下的开发效率。

简单演示

下面用一份c代码来进行演示。
在这里插入图片描述
在当前目录下新建一个Makefile文件,并写上相应的依赖关系与依赖方法。
在这里插入图片描述
接着使用一下make指令。
在这里插入图片描述

依赖关系和依赖方法的解释

这里使用一个生活中的例子来解释依赖关系和依赖方法。现在已经是10月23日了,月底来了,相信不少同学的钱包也已经见底了。这是该怎么办呢?当然是打电话给你的老爸要生活费。找老爸要生活费这一行为也需要对应的依赖关系和依赖方法。你打一个电话过去说,“爹啊,月底了我没钱了,你给我的xx上转1500元当做生活费。”。此时,这你打电话给你爹,首先声明的是依赖关系,要到生活费的前提必须是他是你爹。毕竟你总不能打电话给你舍友的爹要生活费吧。那么往你手机上转账的行为就是对应的依赖方法。你要生活费总不能只给你爹说,“爹啊!”。你只声明依赖关系却没有依赖方法,你的老爹也不会给你转账的。

make会自动推导Makefile的依赖关系

下面我写一个稍微复杂一点的场景来演示make自动推导Makefile的依赖关系。
在这里插入图片描述
在这里插入图片描述
上面的依赖关系的代码顺序可以随意交换,但是依赖关系的逻辑关系不能缺失,否则make无法自动推导依赖关系。

make的默认动作

make将文件的第一个依赖方法和依赖方法设置成为make指令的默认动作。
在这里插入图片描述

.PHONY

为什么make了之后不能接着make呢?
在这里插入图片描述
这是因为操作系统为了提高编译效率而是实现的机制。由于你的源文件的生成时间是大于可执行性程序的生成时间的。此时make就不会编译你的源文件。如果你修改了源文件,此时你的源文件的生成时间是小于你的可执行程序的。故make会生成新的可执行程序。
在这里插入图片描述
那么它是如何实现按的呢?这里需要引出一个概念即,一个文件的时间属性。一个文件的时间属性分为三种, Access时间(最近被访问时间)、Modify时间(修改时间)、Change时间(改变时间)。 几乎你对文件的任何操作都会修该Access时间。在介绍Modify时间和Change时间前,在这里再次提及一个概念。文件本质等于文件的内容加文件的属性。这里的Modify时间显示的是最近一次文件内容被修改的时间。而Change时间则表示最近一次文件属性被修改的时间。这里介绍一个指令stat,用于查看文件状态。

stat 文件名


在这里插入图片描述
为什么最近访问时间没有变呢?其实早期的linux系统的最近访问时间实惠随着你的操作而实时更新的。后来由于处于对性能的优化,最近访问时间不会随着访问而修改。因为文件是存储在磁盘上的,而磁盘属于外部设备。它的访问效率是比较低的,为了进一步优化性能所以最近访问时间默认情况下不会随着被访问而修改。那么我想要让他修改呢?那就是用touch命令。
在这里插入图片描述
只要修改源文件的最近修改时间,make就就可以通过依赖关系来进行在次编译。
在这里插入图片描述
所以make的对于是否可以再次编译的条件是源文件的最近修改时间小于可执行程序的最近修改时间。
如果我希望每次编译都执行呢?需要在Makefile文件中加入.PHONY来修饰,使它总是被执行。
在这里插入图片描述
但是这种写法并不好,因为有时候我们的一些编译问题还是需要我们手动清理后再编译才能解决的。这里建议.PHONY来修饰clean。

特殊符号

由于每次都要手写gcc命令比较麻烦,可以尝试使用特殊符号来代替对应的可执行程序和源文件。
在这里插入图片描述
这里的$@可以替换成的是:左边的内容, $^ 可以替换成:右边的内容。
在对应的方法前加上@符号可以不回显指令的内容。
在这里插入图片描述

进度条程序

经过了一段时间的Linux操作系统的学习,下面就带着大家用一个简单进度条程序来练练手吧。

前言

回车和换行的概念

这里需要提前知道一个概念,即回车和换行的概念。c语言中的’\n’就可以同时实现回车和换行的操作,但是它还有别的功能,且听为等会讲述。换行大家一定不会陌生,就是从一行跳转到下一行,c语言中,\n表示换行+回车(注:c语言没有单独换行的转义字符)。回车表示将光标移动到文档的开头,在c语言中,\r表示回车。
这里我举一个生活中的例子,在19/20世纪的欧洲,一些上层阶级往往会有当时比较潮流的科技设备,打字机。
在这里插入图片描述
在打字机上编写文字时,纸张是不断向上走的。这其实就是一个换行。每写一行文字,就需要手动将印章挪到每行的开头,这其实就是回车。

缓冲区的概念

下面我直接用代码举例
在这里插入图片描述
sleep函数是一个系统调用接口。当执行这个函数时,会休眠n秒。那么上面的代码输出结果是什么呢?答案是会先在显示器上打印hello linux,随后下一行休眠了一秒钟又输出了bash命令行。
在这里插入图片描述
如果不带上\n呢?结果会是什么呢?答案是当程序执行时,第一秒钟休眠了,随后在下一行中hello linux于bash同时输出在了一行。
在这里插入图片描述
这究竟是为什么呢?我相信在大家初步c语言学习的时候,一定听过一个概念就是c语言三大结构之一的顺序结构。代码是从main函数自上而下依次被执行的。那为什么是先休眠后输出呢?因为在sleep期间,程序的数据被暂时保存起来了。保存它的地方就是缓冲区。在这里进需要记住一个简单的概念,即缓冲区是一段被c语言所维护的内存空间。

下面介绍如何强制刷新缓冲区,使我们对应的printf语句先被执行。c程序默认会为用户打开三个输入输出流,标准输入(stdin)、标准输出(stdout)和标准错误(stderr)。这里介绍一个函数fflush(),它接受一个文件流stream,会强制刷新这个流,使得缓冲区内的数据立刻被写到文件中(补充:Linux下一切皆文件,也包括显示器)。
在这里插入图片描述

实现一个进度条程序

10秒倒计时小程序

简单写一个10秒倒计时程序来为进度条程序做一个铺垫。
在这里插入图片描述
这里我就简单说一下程序的实现思路,当程序在屏幕上输出数字时,我们需要让它立刻被刷新出来并写入显示器文件。随后休眠一秒后,又会在显示上一个数字的位置,用上一个数字-1的值将它覆盖。因为使用了\r回车转义字符。这里的%-2d其实是输出格式控制。

进度条程序简易版

这里实现采取定义与生命分离的方式进行。这里创建三个文件,分别用于声明方法和实现方法以及测试方法。
在这里插入图片描述
先简单配置一下Makefile文件。
在这里插入图片描述
这里的:右边有两个.c文件,不过$^还是会默认带上这两个.c文件。.PHONY修饰clean表示该指令总是被执行。

第二步,在头文件中包含相关头文件以及声明函数方法。
在这里插入图片描述
第三步,实现进度条程序。定义一个字符串数组存储进度条的输出样式,为了让精度条执行时,有一个相应的动画效果。开辟一个数组来存储进度条的内容样式,通过循环来控制进度条在屏幕上的显示。每次回车打印完内容后将进度条的添加进度条内容,搭配上usleep就能达到控制进度条的速度。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

进阶版进度条

下面以模拟下载软件时的场景,带大家看一个关于回调函数版本进度条的实现。
首先,我们对原来的processbar.c进行一个修改。
在这里插入图片描述
使用回调函数的设计思路将进度条模块独立成回调函数,当下载时,download函数内部控制具体的进度条逻辑。主函数中只需要传一个回调函数就能使进度条跑起来。
在这里插入图片描述