文章目录
一、make和makefile的区分
make是一个命令,makefile是一个文件
二、依赖关系和依赖方法
依赖关系: 依赖关系指明了两个文件之间的依赖性。例如test.o文件的生成要依赖test.s文件
依赖方法: 依赖方法就是我依赖这个关系需要做什么。例如test.o形成需要test.s进行汇编,即gcc -c test.s -o test.o
三、make/Makefile的使用
#include<stdio.h>intmain(){printf("hello Makefile\n");return0;}
如上一串代码,如果我们需要在Linux中对其进行编写,生成可执行程序,以往我们需要敲一长串指令
gcc test.c -o test
,当我们学会makefile之后我们就不在需要敲这一长串指令了,只需要一个make指令即可。
1、makefile的编写
- 首先在源文件的目录下创建一个makefile或者Makefile文件(首字母可以大写,其他的不行)
- 然后打开makefile文件,进行规则的编辑: 第一行中冒号左边的
mycode
是目标文件,该目标文件依赖于冒号右边的mycode.c
而产生,所以第一行写的是依赖关系,第二行必须使用一个tab键之后写上两个文件的依赖方法。 - 接着然后直接使用
make
命令即可。如下: 这样就形成了我们所需要的可执行程序文件。
2、临时文件的清理
完成上面的编写后我们就可以生成需要的可执行文件,但若我们需要清理生成的临时可执行程序文件,则可以进行如下编写:
.PHONY
表示被该关键字修饰的对象是一个伪目标。(该伪目标总是可被执行)
使用: 如下图:
疑问: 为什么编译的时候直接使用make就可以了,而进行清理需要加上clean呢?
其实make后面也可以接mycod,只不过make默认对makefile文件中的第一个目标文件可以省略名称
3、makefile的工作原理
- make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
- 默认情况下Makefile的第一个目标为终极目标。 即默认情况下,makefile只形成一个可执行目标文件,形成之后,后续的依赖关系和依赖方法不再执行(默认从上到下扫描总是执行一个,默认不指明情况下,只形成第一个)
- all:Makefile文件默认只生成第一个目标文件即完成编译,但是我们可以通过all 指定所需要生成的目标文件。
- 若需要完成指定的功能,只需在make命令后加上自己写入makefile中的命令
4、文件的三个时间
上方我们提到,
.PHONY
是修饰伪目标的,该伪目标总是可被执行。什么意思呢?如图:
进行第一次make的时候,我们可以进行编译生成可执行程序文件,但当我们进行第二次make的时候就不可以了而且提示我们说mycode是最新的,而我们由
.PHONY
修饰的
clean
就可以总是被执行的。
那为什么gcc编译知道mycode是最新的呢?接下来我们认识一下文件的三个时间:
[wyt@VM-20-4-centos lesson3]$ stat mycode.c
File: ‘mycode.c’
Size:81 Blocks:8 IO Block:4096 regular file
Device: fd01h/64769d Inode:794005 Links:1
Access:(0664/-rw-rw-r--) Uid:(1001/ wyt) Gid:(1001/ wyt)
Access:2023-02-1117:08:52.893235379+0800//文件最近访问时间
Modify:2023-02-0413:21:39.841893206+0800//内容被修改时间
Change:2023-02-0413:21:39.841893206+0800//属性被修改时间
Birth:-
4.1、Access:最近一次访问文件的时间
读取文件内容,修改文件内容,Access都会发生变化.
修改文件内容,Access的时间会立即更新;但是读文件操作,不会立即更新。
我们读取文件或者查看文件,如cat、find等操作,这些都属于高频操作,如果我们频繁使用这些命令,可能会频繁更新Access时间,导致Linux的处理速度下降
在较新的Linux内核中,这个问题被优化了,对文件进行读操作以后,Access时间不会立即更新,经过一定的时间间隔,OS才会自动进行更新时间
4.2、Modify:最近一次修改文件内容的时间
只要是修改文件内容,Modify的时间都会被更新,这个被视为低频操作,所以一般修改文件内容以后,会立即刷新。
但是修改文件内容,有可能会顺带着修改文件属性,比如新增内容会改变文件大小(文件大小属于文件属性)
4.3、Change:最近一次修改文件属性的时间
修改文件属性也被视为低频操作,修改文件属性会立即更新Change的时间
只是单纯的修改文件属性,不会影响到Modify和Access的时间
4.4、‘xxx’ is up to date问题
所以我们在上面遇到的make问题就很好解答了。
第一次make后,生成一个可执行目标文件,再之后make时,需要比较可执行目标文件
mycode
和源文件
mycode.c
的Modify的时间
可执行目标文件
mycode
的modify的时间比源文件
mycode.c
的modify时间新,所以不可以再进行编译了。
当我们的源文件时间进行修改后,我们又可以再次进行make编译了
总结:
当源文件的Modify时间比目标文件的Modify时间新时,才可以再次利用make进行编译。
四、makefile的推导规则
makefile文件在扫描时,是从上往下进行扫描的,比如下图:
make会一层一层地去找文件的依赖关系,直到最后编译出第一个目标文件。
拿上图举例:
如上,最后找到mycode.i目标文件后,就会在一层一层的往上,最后形成一个可执行的目标文件。
上述的推导过程就如同一个栈结构,先进后出。
五、进度条小程序
1、行缓冲区问题
先看下面两串代码执行的现象:
代码1:
现象:
代码2:
现象:
两串代码的区别仅仅就是代码1有
\n
,而代码2没有那为什么会造成不同的现象呢?
首先我们要明确一点就是,代码是顺序结构的,所以他是从上往下执行,所以先执行printf在执行sleep。至于为什么会有不同的现象,如下:
,这里就有一个行缓冲区的概念在这里。对于C语言级别的缓冲区而言,任何字符串都会先保存在这个缓冲区里面,等待刷新在显示屏上。但是显示器刷新是属于行刷新,就是遇到‘\n’就进行刷新。所以现在我们就理解了,代码2中没有’\n’,要打印的字符串一直保存在了C语言级别的缓冲区,只有程序运行快结束是才会把这些字符串刷新在屏幕上。
那我们该如何将代码2的printf打印立马显示到显示器上呢?
#include<stdio.h>#include<unistd.h>intmain(){printf("hello LInux");fflush(stdout);//fflush会立即刷新缓冲区sleep(2);return0;}
2、回车换行(\r与\n)
理解:
\r
:回车。回到当前行的最开始\n
:换行。换到下一行,但列不变
在语言层面:
\n
就是回车换行
3、倒计时功能
#include<stdio.h>#include<unistd.h>intmain(){int cnt=10;while(cnt){printf("%2d\r",cnt);//2d控制刷新两位,\r表示回车 fflush(stdout);//手动刷新缓冲区 sleep(1);--cnt;}return0;}
3、进度条的实现
多文件形式:
头文件
process.h
#pragmaonce#include<stdio.h>#include<string.h>#include<unistd.h>#defineNUM101externvoidProncessOn();//函数的声明
源文件
process.c
#include"process.h"voidProncessOn()//函数的定义{int cnt =0;char bar[NUM];memset(bar,'\0',sizeof(bar));constchar*lable ="|\\-/";while(cnt <=100){printf("[%-100s][%d%%][%c]\r", bar, cnt, lable[cnt%4]);fflush(stdout);//立即打印
bar[cnt++]='#';//sleep(1);//单位是秒,太慢了usleep(50000);//微妙 5S/100 == 0.05S == 50000}printf("\n");}
效果如下:
版权归原作者 Clumsy、笨拙 所有, 如有侵权,请联系我们删除。