Linux编译gcc/g++、自动化构建工具make/makefile

  • Post author:
  • Post category:linux



目录


1.g++/gcc的使用


2.Makefile


1.g++/gcc的使用

在学习gcc/g++之前,需要先回顾一下程序的翻译过程:

预处理(头文件展开、去注释、宏替换、条件编译)

编译:把c编程汇编语言

汇编:把汇编变成二进制(不是可执行,二进制目标文件)

链接:把写的代码和c标准库中的代码合起来


gcc的格式:gcc [选项] 要编译的文件 [选项] [目标文件]


[wjmhlh@VM-12-9-centos lesson7]$

gcc test.c


[wjmhlh@VM-12-9-centos lesson7]$ ll

total 16

-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov  9 20:27

a.out


-rw-rw-r– 1 wjmhlh wjmhlh  320 Nov  9 20:27

test.c


[wjmhlh@VM-12-9-centos lesson7]$ ./a.out

hello1

hello2

hello3

直接使用语句:gcc test.c 会直接编译完成,不会显示程序的翻译的过程。

因此,以下代码的含义:


预处理:



gcc -E test.c -o test.i




-o:指明形成的临时文件名称


,.i(后缀,在linux中后缀没意义,是给人看的) ;

-E:从现在开始,进行程序的翻译,当我将预处理做完,就停下来

[wjmhlh@VM-12-9-centos lesson7]$

gcc -E test.c -o test.i


[wjmhlh@VM-12-9-centos lesson7]$ ll

total 36

-rwxrwxr-x 1 wjmhlh wjmhlh  8360 Nov  9 20:27

a.out


-rw-rw-r– 1 wjmhlh wjmhlh   320 Nov  9 20:27

test.c


-rw-rw-r– 1 wjmhlh wjmhlh 17005 Nov  9 20:30

test.i

可以使用vim test.i 打开查看:可以看到,头文件展开后的预处理的代码。并且,注释部分没有了,在条件编译下的printf(“hello show\n”);也消失了,因为SHOW这个宏没定义,所以只保留了default那一行代码,这就是条件编译;

[wjmhlh@VM-12-9-centos lesson7]$ ./a.out

hello1

hello2

hello3


hello deafult


宏: 234

如果需要条件编译生效,需要定义宏

以下是结果:

hello1

hello2

hello3


hell show


宏: 234

同时:我们也可以在外面定义宏

:在文件名后加-D+宏名

[wjmhlh@VM-12-9-centos lesson7]$


gcc test.c -DSHOW



[wjmhlh@VM-12-9-centos lesson7]$ ./a.out

hello1

hello2

hello3


hell show


宏: 234


[wjmhlh@VM-12-9-centos lesson7]$


gcc test.c



[wjmhlh@VM-12-9-centos lesson7]$ ./a.out

hello1

hello2

hello3


hello deafult


宏: 234

当预处理完后,代码还是C语言,只不过是“干净”的C语言,没有注释没有未定义。

于是,接下来,要将C语言变成汇编语言。


编译



gcc -S test.c -o test.s


-S:从现在开始,进行程序的翻译,

做完编译工作,变成汇编之后,就停下来

[wjmhlh@VM-12-9-centos lesson7]$


gcc -S test.c -o test.o



[wjmhlh@VM-12-9-centos lesson7]$ ll

total 40

-rw-rw-r– 1 wjmhlh wjmhlh   321 Nov  9 20:47 test.c

-rw-rw-r– 1 wjmhlh wjmhlh 16988 Nov  9 20:45 test.i

-rw-rw-r– 1 wjmhlh wjmhlh   710 Nov  9 20:55 test.s

[wjmhlh@VM-12-9-centos lesson7]$

vim test.s


下面这个图的test.o,写错了,应该是test.s

此时,已经将C语言,变成汇编语言了,但是,汇编语言并不能被计算机执行,而是二进制;

于是,需要进行汇编,汇编就是将汇编语言,变成二进制(不可执行,二进制目标文件)。


汇编



gcc -c test.s -o test.o


-c:从现在开始,进行程序的翻译,做完汇编工作,编程可重定向目标二进制,就停下来

[wjmhlh@VM-12-9-centos lesson7]$


gcc -c test.c -o test.o



[wjmhlh@VM-12-9-centos lesson7]$ ll

total 44

-rw-rw-r– 1 wjmhlh wjmhlh   321 Nov  9 20:47 test.c

-rw-rw-r– 1 wjmhlh wjmhlh 16988 Nov  9 20:45 test.i

-rw-rw-r– 1 wjmhlh wjmhlh  1808 Nov  9 21:04 test.o

-rw-rw-r– 1 wjmhlh wjmhlh   710 Nov  9 21:01 test.s

[wjmhlh@VM-12-9-centos lesson7]$

vim test.o

虽然变成了二进制,但是还是不能被执行,因为少了链接,没有将库结合起来。因为这三部,都只在编译我的代码,并没有带上C标准库上的代码。


链接



gcc test.o -o mytest


链接过程,形成了可执行程序,可执行的二进制程序(库+我写码)。

[wjmhlh@VM-12-9-centos lesson7]$


gcc test.o



[wjmhlh@VM-12-9-centos lesson7]$ ll

total 44

-rwxrwxr-x 1 wjmhlh wjmhlh  8408 Nov  9 21:08


a.out



-rw-rw-r– 1 wjmhlh wjmhlh   321 Nov  9 20:47 test.c

-rw-rw-r– 1 wjmhlh wjmhlh 16988 Nov  9 20:45 test.i

-rw-rw-r– 1 wjmhlh wjmhlh  1809 Nov  9 21:04 test.o

-rw-rw-r– 1 wjmhlh wjmhlh   710 Nov  9 21:01 test.s

当然,如果我们想重命名,可以带上-o

[wjmhlh@VM-12-9-centos lesson7]$


gcc test.o -o mytest



[wjmhlh@VM-12-9-centos lesson7]$ ll

total 56

-rwxrwxr-x 1 wjmhlh wjmhlh  8408 Nov  9 21:08


a.out



-rwxrwxr-x 1 wjmhlh wjmhlh  8408 Nov  9 21:10


mytest



-rw-rw-r– 1 wjmhlh wjmhlh   321 Nov  9 20:47 test.c

-rw-rw-r– 1 wjmhlh wjmhlh 16988 Nov  9 20:45 test.i

-rw-rw-r– 1 wjmhlh wjmhlh  1809 Nov  9 21:04 test.o

-rw-rw-r– 1 wjmhlh wjmhlh   710 Nov  9 21:01 test.s

[wjmhlh@VM-12-9-centos lesson7]$


./mytest



hello1

hello2

hello3

hello deafult

宏: 234

在链接这一步:

在这里涉及到一个重要的点:

函数库

我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?

接下来,就是函数库的作用了!


函数库一般分为静态库和动态库



静态库


是指编译链接时,

把库文件的代码全部加入到可执行文件中

,因此生成的文件比较大,但在运行时也就不再需要库文件了。


静态链接:好处:不受库升级或被删除的影响。坏处:形成的可执行程序太大。



动态库


与之相反,

在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,

这样可以节省系统的开销,即形成的可执行程序小。


动态链接:好处:形成的可执行程序小。坏处:受库的改动影响。

那么,在一般情况下,我们的Linux的函数库是静态还是动态的呢?

先看下面代码:

[wjmhlh@VM-12-9-centos lesson8]$

touch mytest.c


[wjmhlh@VM-12-9-centos lesson8]$ ll

total 0

-rw-rw-r– 1 wjmhlh wjmhlh 0 Nov 10 15:07 mytest.c

[wjmhlh@VM-12-9-centos lesson8]$ vim mytest.c

[wjmhlh@VM-12-9-centos lesson8]$

gcc mytest.c -o mytest


[wjmhlh@VM-12-9-centos lesson8]$ ll

total 16

-rwxrwxr-x 1 wjmhlh wjmhlh

8360

Nov 10 15:09

mytest


-rw-rw-r– 1 wjmhlh wjmhlh  210 Nov 10 15:08 mytest.c

从上面看到,我们形成的可执行程序的大小是8360,也就是大概8k左右。

于是,我们继续看:

[wjmhlh@VM-12-9-centos lesson8]$

file mytest


mytest: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),


dynamically linked


(uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=ea8c82beaf490b03226c3200ce7a0cbb54cd0bc2, not stripped

[wjmhlh@VM-12-9-centos lesson8]$

ldd mytest


linux-vdso.so.1 =>  (0x00007ffd7e35d000)



libc.so.6 => /lib64/libc.so.6


(0x00007f88f1346000)

/lib64/ld-linux-x86-64.so.2 (0x00007f88f1714000)

在解释上面的代码前,先要了解一下在Linux下库的命名:

动态库:libXXXXXX.so 以lib为前缀,以.so为后缀,可以带上版本号,XXX是库的名字

静态的:libYYYYYY.a 以lib为前缀,以.a为后缀,可以带上版本号,YYY是库的名字

而要区分是什么库,那么,去掉前缀和后缀,剩下的就是我们需要知道的库。

如上面:


libc.so.6


——>

lib

c

.so.6

最终我们看到的是我们最熟悉的c,也就是c的标准库了,是个动态库,


这也说明了,在Linux下,默认的是动态库。

这也是我们写的头文件中的stdio中的std的意思——C标准库,用了标准库。

这时候就会有个疑问?既然我们在stdio.h中使用了C标准库,那么C标准库在哪?咋看不见?

其实就是在


lib64/libc.so.6


这里。

[wjmhlh@VM-12-9-centos lesson8]$

ls /lib64/libc.so.6




/lib64/libc.so.6



[wjmhlh@VM-12-9-centos lesson8]$

ls /lib64/libc.so.6 -l


lrwxrwxrwx 1 root root 12 Jul 25 16:58


/lib64/libc.so.6 -> libc-2.17.so


于是,我们可以查看我们的库了。

[wjmhlh@VM-12-9-centos lesson8]$


ls /lib64/libc-2.17.so -al




-rwxr-xr-x 1 root root

2156592

May 19 00:18 /lib64/libc-2.17.so


其中,2156592就是库的大小

如果我们需要静态库了,那咋办?

我们可以在gcc的时候,在后面加上-static。

[wjmhlh@VM-12-9-centos lesson8]$


gcc mytest.c -o mytest_s -static



[wjmhlh@VM-12-9-centos lesson8]$ ll

total 860

-rwxrwxr-x 1 wjmhlh wjmhlh   8360 Nov 10 15:09 mytest

-rw-rw-r– 1 wjmhlh wjmhlh    210 Nov 10 15:08 mytest.c

-rwxrwxr-x 1 wjmhlh wjmhlh 861288 Nov 10 15:26


mytest_s


我们来查看一下它所使用的库:

[wjmhlh@VM-12-9-centos lesson8]$ file mytest_s

mytest_s: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux),


statically linked


, for GNU/Linux 2.6.32, BuildID[sha1]=b9f8bdddd842b61ac994be5e91eff1d7e3e64234, not stripped

[wjmhlh@VM-12-9-centos lesson8]$ ldd mytest_s

not a dynamic executable——-不是可动态执行的

可以看到,确实是静态库了,使用了静态链接,同时,我们可以看到静态链接和动态链接的体积区别

静态:861288

动态:8360

一百倍!!!😀要是我需要下载的软件,一家公司是8360大小,另外一家是861288大小,功能等等几乎一样,我肯定选8360啊!!!

其实,在这里我们就能继续看到,我们在Linux的指令,其实都是动态库中的。

[wjmhlh@VM-12-9-centos lesson8]$


file /usr/bin/ls



/usr/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),


dynamically linked


(uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=c8ada1f7095f6b2bb7ddc848e088c2d615c3743e, stripped

[wjmhlh@VM-12-9-centos lesson8]$


file /usr/bin/pwd



/usr/bin/pwd: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),


dynamically linked


(uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=8f1d0ff9fee13b5d44a1189122856912af0486bc, stripped

所有,千万不要删掉系统自带的动态库!!!而且,这个库只有一份,意味着所有用C语言写的程序,不会出现重复的库代码,因此动态库也叫共享库。因此,要是去下载一个C写的程序,也不需要下载C标准库了。

而静态库呢?在执行静态链接的时候,拷贝的是.so内部的代码吗?

NO!系统里面必须存在.a结尾的静态库。

[wjmhlh@VM-12-9-centos lesson8]$ ls /lib64/libc.a



/lib64/libc.a



[wjmhlh@VM-12-9-centos lesson8]$ ll /lib64/libc.a

-rw-r–r– 1 root root 5105516 May 19 00:18 /lib64/libc.a

这个就是C语言的静态库了。在静态链接的时候,会将静态库的代码拷贝过去。一般来说,系统会自带动态库,而静态库需要自己安装。

同样的对于c++,一样有自己的C++标准库,动静态库。

[wjmhlh@VM-12-9-centos lesson8]$

touch mytest.cpp


[wjmhlh@VM-12-9-centos lesson8]$ ll

total 4

-rw-rw-r– 1 wjmhlh wjmhlh 210 Nov 10 15:08 mytest.c

-rw-rw-r– 1 wjmhlh wjmhlh   0 Nov 10 15:42 mytest.cpp

[wjmhlh@VM-12-9-centos lesson8]$ vim mytest.cpp

[wjmhlh@VM-12-9-centos lesson8]$ ll

total 8

-rw-rw-r– 1 wjmhlh wjmhlh 210 Nov 10 15:08 mytest.c

-rw-rw-r– 1 wjmhlh wjmhlh 188 Nov 10 15:43 mytest.cpp

[wjmhlh@VM-12-9-centos lesson8]$

g++ mytest.cpp -o mytestcpp


[wjmhlh@VM-12-9-centos lesson8]$ ldd mytestcpp

linux-vdso.so.1 =>  (0x00007ffff6c62000)


libstdc++.so.6 =>

/home/wjmhlh/.VimForCpp/vim/bundle/YCM.so/el7.x86_64/libstdc++.so.6 (0x00007f42d0d10000)

libm.so.6 => /lib64/libm.so.6 (0x00007f42d0a0e000)

libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f42d07f8000)

libc.so.6 => /lib64/libc.so.6 (0x00007f42d042a000)

/lib64/ld-linux-x86-64.so.2 (0x00007f42d1091000)

wjmhlh@VM-12-9-centos lesson8]$

g++ mytest.cpp -o mytestcpp -static


[wjmhlh@VM-12-9-centos lesson8]$ ll

total 1580

-rw-rw-r– 1 wjmhlh wjmhlh     210 Nov 10 15:08 mytest.c

-rwxrwxr-x 1 wjmhlh wjmhlh 1608376 Nov 10 15:44 mytestcpp

-rw-rw-r– 1 wjmhlh wjmhlh     188 Nov 10 15:43 mytest.cpp

[wjmhlh@VM-12-9-centos lesson8]$

ldd mytestcpp


not a dynamic executable

[wjmhlh@VM-12-9-centos lesson8]$ ll

total 1580

-rw-rw-r– 1 wjmhlh wjmhlh     210 Nov 10 15:08 mytest.c

-rwxrwxr-x 1 wjmhlh wjmhlh 1608376 Nov 10 15:44 mytestcpp

-rw-rw-r– 1 wjmhlh wjmhlh     188 Nov 10 15:43 mytest.cpp

[wjmhlh@VM-12-9-centos lesson8]$

file mytestcpp


mytestcpp: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux),

statically linked

, for GNU/Linux 2.6.32, BuildID[sha1]=58b7771454ee8e14960bf100fb6376db5d8a9a03, not stripped

最后:系统本身为了支持我们去编程,给我们提供了标准库的.h(接口),标准的动静态库.so/.a(实现方法)。所以,


我们的代码+库代码==可执行程序!

其实,上述的内容,在windows下的原理差不多,也需要动静态库。windows的动态库的后缀:.dll,静态库:.lib。

2.Makefile

makefile是什么?

makefile是一个工具,可以”自动化编译”,只需要一个make命令,整个工程就会完全自动编译,大大地提高软件开发效率。



makefile是一个文件,make是一个命令。

如何使用makefile?注:可以大写,可以小写。

首先:创建makefile文件,文件名必须的是makefile

[wjmhlh@VM-12-9-centos mk]$

touch makefile


[wjmhlh@VM-12-9-centos mk]$ ll

total 4

-rw-rw-r– 1 wjmhlh wjmhlh  0 Nov 10 23:40

makefile

然后:vim makefile

要往里面写入依赖关系和依赖方法:

保存退出。之后,我们不用再使用gcc或g++了,直接使用make命令,完成自动编译

[wjmhlh@VM-12-9-centos mk]$

make



gcc mycode.c -o mycode


[wjmhlh@VM-12-9-centos mk]$

cat makefile




mycode:mycode.c

gcc mycode.c -o mycode


-rw-rw-r– 1 wjmhlh wjmhlh   42 Nov 10 23:44 makefile

-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 10 23:43


mycode



-rw-rw-r– 1 wjmhlh wjmhlh   81 Nov 10 23:39 mycode.c

[wjmhlh@VM-12-9-centos mk]$

./mycode



hello makefile

如果这样子来看,其实反应不出来makefile的作用如何之大。因为工程量少。等工程量大,就知道了。因此现在所知道的作用,便是再makfile里面写gcc,等以后随便写代码后,直接make,不需要gcc,方便了!


makefile原理:


mycode:mycode.c

gcc mycode.c -o mycode


根据上面解释的依赖方法和依赖关系:mycode是要形成的可执行程序,mycode是mycode.c的儿子,因此mycode.c的mycode的依赖对象,而形成的(依赖)方法便是 gcc mycode.c -o mycode


目的

:

依赖对象


依赖方法




↑ 这里的空格,必须以table为开头!必须以table为开头!必须以table为开头!

因为makefile是文件,因此我们需要知道的是,makefile内部,要写的就是依赖方法和依赖对象!

注意:是文件,不是目录(文件夹)。创建的时候,是touch makefile,不是mkdir makefile。

[wjmhlh@VM-12-9-centos mk]$

touch makefile


[wjmhlh@VM-12-9-centos mk]$ vim makefile

[wjmhlh@VM-12-9-centos mk]$

make



gcc mycode.c -o mycode


[wjmhlh@VM-12-9-centos mk]$ ll

total 20

-rw-rw-r– 1 wjmhlh wjmhlh   40 Nov 11 10:03 makefile

-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:03

mycode


-rw-rw-r– 1 wjmhlh wjmhlh   81 Nov 10 23:39 mycode.c

[wjmhlh@VM-12-9-centos mk]$ ./mycode


hello makefile

如果有一天,我不想要这个可执行程序了,那么改如何清理呢?

我们可以再下面写上clean:

从此:想要编译:make   想要清理已经编译好的可执行程序:make clean

-rw-rw-r– 1 wjmhlh wjmhlh   77 Nov 11 10:12 makefile

-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:03

mycode


-rw-rw-r– 1 wjmhlh wjmhlh   81 Nov 10 23:39 mycode.c

[wjmhlh@VM-12-9-centos mk]$

make clean



rm -f mycode


[wjmhlh@VM-12-9-centos mk]$ ll

total 8

-rw-rw-r– 1 wjmhlh wjmhlh 77 Nov 11 10:12 makefile

-rw-rw-r– 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c

具体解释:

①clean不依赖任何对象,是独立存在的。因此直接换行写下一行。

clean:

rm -f mycode

rm -f mycode 是clean的依赖方法。

而为了表明clean是清理这里的项目,那么加上.PHONY:clean,来代表伪目标;

伪目标是什么?

.PHONY:被该关键字修饰的对象,是一个伪目标

首先来了解一下一些细节:

当我们make一个可执行程序后,如果继续make,它就会显示,已经是最新的可执行程序,不需要编译了。

-rw-rw-r– 1 wjmhlh wjmhlh 77 Nov 11 10:12 makefile

-rw-rw-r– 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c

[wjmhlh@VM-12-9-centos mk]$

make



gcc mycode.c -o mycode


[wjmhlh@VM-12-9-centos mk]$

make



make: `mycode’ is up to date.


[wjmhlh@VM-12-9-centos mk]$

make



make: `mycode’ is up to date.


[wjmhlh@VM-12-9-centos mk]$

make



make: `mycode’ is up to date.

如果我们在这种情况下,很久没用这个代码,而这个代码没有任何修改,不需要再make。

如果我们在这种情况下,去改写代码,那么我们还需要再make一下吗?需要。

换句话说:同样的代码,只需要make一次。

但是我们将同样的思路去这些make clean,却发现,make clean可以一直被执行,即使可执行程序已经被清理了,但还是可以执行make clean。

[wjmhlh@VM-12-9-centos mk]$

make clean



rm -f mycode


[wjmhlh@VM-12-9-centos mk]$ ll

total 8

-rw-rw-r– 1 wjmhlh wjmhlh 77 Nov 11 10:12 makefile

-rw-rw-r– 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c

[wjmhlh@VM-12-9-centos mk]$

make clean



rm -f mycode


[wjmhlh@VM-12-9-centos mk]$

make clean



rm -f mycode


[wjmhlh@VM-12-9-centos mk]$

make clean



rm -f mycode

为什么会这样?


其实,这就是.PHONY 的作用:伪目标代表的含义就是:该目标,总是被执行的!

我们试一下,用.PHONY来修饰mycode(目标)

-rw-rw-r– 1 wjmhlh wjmhlh 91 Nov 11 10:23 makefile

-rw-rw-r– 1 wjmhlh wjmhlh 81 Nov 10 23:39 mycode.c

[wjmhlh@VM-12-9-centos mk]$

make



gcc mycode.c -o mycode


[wjmhlh@VM-12-9-centos mk]$ ll

total 20

-rw-rw-r– 1 wjmhlh wjmhlh   91 Nov 11 10:23 makefile

-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:24

mycode


-rw-rw-r– 1 wjmhlh wjmhlh   81 Nov 10 23:39 mycode.c

[wjmhlh@VM-12-9-centos mk]$

make



gcc mycode.c -o mycode


[wjmhlh@VM-12-9-centos mk]$

make



gcc mycode.c -o mycode


[wjmhlh@VM-12-9-centos mk]$

make



gcc mycode.c -o mycode


[wjmhlh@VM-12-9-centos mk]$ ll

total 20

-rw-rw-r– 1 wjmhlh wjmhlh   91 Nov 11 10:23 makefile

-rwxrwxr-x 1 wjmhlh wjmhlh 8360 Nov 11 10:24

mycode


-rw-rw-r– 1 wjmhlh wjmhlh   81 Nov 10 23:39 mycode.c

mycode变成了总是能够被执行的目标了。

我们一般不会将我们所需要的可执行程序用.PHONY来修饰,因为只要代码没修改过,只需要make一次就可以了。

其实我特别好奇:

gcc是如何得知我不需要再编译了呢?

这就需要提到

三个时间

[wjmhlh@VM-12-9-centos mk]$

stat mycode.c


File: ‘mycode.c’

Size: 81            Blocks: 8          IO Block: 4096   regular file

Device: fd01h/64769d    Inode: 789551      Links: 1

Access: (0664/-rw-rw-r–)  Uid: ( 1001/  wjmhlh)   Gid: ( 1001/  wjmhlh)


Access: 2022-11-10 23:39:57.210243832 +0800

Modify: 2022-11-10 23:39:55.912237391 +0800

Change: 2022-11-10 23:39:55.912237391 +0800

[wjmhlh@VM-12-9-centos mk]$

stat mycode


File: ‘mycode’

Size: 8360          Blocks: 24         IO Block: 4096   regular file

Device: fd01h/64769d    Inode: 789553      Links: 1

Access: (0775/-rwxrwxr-x)  Uid: ( 1001/  wjmhlh)   Gid: ( 1001/  wjmhlh)


Access: 2022-11-11 10:24:14.205381640 +0800

Modify: 2022-11-11 10:24:13.801379696 +0800

Change: 2022-11-11 10:24:13.801379696 +0800

①Modify:指


文件内容


被修改的时间

②Change:指


文件属性


被修改的时间

③Access:指文件被访问后的时间

例如:我们去改文件属性:

当前的mycode的Change:

2022-11-11 10:24:13.801379696 +0800

其他两个时间:

Access: 2022-11-11 10:24:14.205381640 +0800

Modify: 2022-11-11 10:24:13.801379696 +0800

-rw-rw-r– 1 wjmhlh wjmhlh   77 Nov 11 10:28 makefile


-rwxrwxr-x

1 wjmhlh wjmhlh 8360 Nov 11 10:24 mycode

-rw-rw-r– 1 wjmhlh wjmhlh   81 Nov 10 23:39 mycode.c

[wjmhlh@VM-12-9-centos mk]$

chmod u-w mycode


[wjmhlh@VM-12-9-centos mk]$ ll

total 20

-rw-rw-r– 1 wjmhlh wjmhlh   77 Nov 11 10:28 makefile


-r-xrwxr-x

1 wjmhlh wjmhlh 8360 Nov 11 10:24 mycode

-rw-rw-r– 1 wjmhlh wjmhlh   81 Nov 10 23:39 mycode.c

[wjmhlh@VM-12-9-centos mk]$ stat mycode

File: ‘mycode’

Size: 8360          Blocks: 24         IO Block: 4096   regular file

Device: fd01h/64769d    Inode: 789553      Links: 1

Access: (0575/-r-xrwxr-x)  Uid: ( 1001/  wjmhlh)   Gid: ( 1001/  wjmhlh)


Access: 2022-11-11 10:24:14.205381640 +0800

Modify: 2022-11-11 10:24:13.801379696 +0800

Change: 2022-11-11 10:32:35.103783432 +0800

此时的时间:

Access: 2022-11-11 10:24:14.205381640 +0800

Modify: 2022-11-11 10:24:13.801379696 +0800



Change: 2022-11-11 10:32:35.103783432 +0800

可以得知:这里的Change的时间被改变了,其他两个没有发生改变


再看对文件内容的修改:

[wjmhlh@VM-12-9-centos mk]$

vim mycode.c


[wjmhlh@VM-12-9-centos mk]$ make

gcc mycode.c -o mycode

[wjmhlh@VM-12-9-centos mk]$

stat mycode


File: ‘mycode’

Size: 8360          Blocks: 24         IO Block: 4096   regular file

Device: fd01h/64769d    Inode: 789553      Links: 1

Access: (0775/-rwxrwxr-x)  Uid: ( 1001/  wjmhlh)   Gid: ( 1001/  wjmhlh)


Access: 2022-11-11 10:36:36.216934574 +0800

Modify: 2022-11-11 10:36:35.814932656 +0800

Change: 2022-11-11 10:36:35.814932656 +0800


同理,这里会对Modify的时间做修改,但是我们可以看见的是,Change的时间也被修改了。

这是为什么?

因为对内容改变了,文件的大小也改变了,即属性也改变了。

这里很合理,但不可思议的是,Access为啥不变(我举的这个例子,因为我make了一下,时间间隔比较长,所以改变了,其实是没变的。)


Access代表是,访问这个文件的时间。


其实访问它,确实是会变,但是呢,如果在短时间内(非常短的时间),它不会变。即不会频繁去改变时间。其原因是,我们会频繁地访问文件,如果频繁地去修改时间,意味着要非常多次地去访问磁盘,是一个负担!而且,很少人去关心文件被访问的时间,而且去关心文件的内容属性被修改的时间。

回到那个问题:gcc如何得知不需要再编译可执行程序?

首先:我们需要知道的是,一定是现有源文件,再有可执行程序。所以!在编译完成,生产可执行程序后,


源文件的Modify时间一定比可执行程序的Modify时间早!


于是,我们就知道了,gcc是通过比较两个时间来得知是否需要重新编译。



如果源文件的Modify比可执行程序的Modify早,说明,代码没有被修改,不需要make重新编译。



如果源文件的Modify比可执行程序的Modify晚,说明代码被修改,需要make重新编译。

此时,我们可以去想想,为什么加了.PHONHY后,总是被执行,.PHONY是怎么做的?

其实原理就是跟touch一下已有的源文件(可以理解为摸一下文件,导致文件时间改变了)一样。所以加了.PHONY的作用就是,不要再去对比时间了,直接给我make!


再来看看新的问题:

为什么直接输入make,会自己识别是编译可执行程序?而不是清理。清理则需要输入make clean?

编译可执行程序的时候,我们其实也可以写成make mycode。只不过呢?make这个命令,是从上到下,按顺序执行。

也就是说,第一个被make碰到的目标文件,可以省略掉目标文件名,仅此而已。


但这里也有个问题?

不是说好make是按顺序往下走吗?怎么make了后,只执行了一次?而没有继续往下走了呢?


默认情况下:makefile只形成一个目标文件,那么后续的,需要再此输入命令。


makefile的推到规则:

其实在写makefile时,mycode依赖的不是mycode.c,而是mycode.o;

而mycode.o依赖的是mycode.s;

mycode.s依赖的是mycode.i;

mycode.i依赖的是mycode.c

在执行make命令的时候,从上到下扫描,要生成mycode,找mycode.o,没有mycode.o,那就找往下找,一直找。



版权声明:本文为wjmhlh原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。