Linux从0到1——Linux环境基础开发工具的使用(上)

Linux从0到1——Linux环境基础开发工具的使用(上)

  • 1. Linux软件包管理器yum
    • 1.1 yum介绍
    • 1.2 用yum来下载软件
    • 1.3 更新yum源
  • 2. Linux编辑器:vi/vim
    • 2.1 vim的基本概念
    • 2.2 vim的基本操作
    • 2.3 vim正常模式命令集
    • 2.4 vim底行模式命令集
    • 2.5 视图模式:批量化注释
    • 2.6 vim也支持多文件编辑
    • 2.7 简单的vim配置
      • 2.7.1 如何解决新用户无法sudo提权
      • 2.7.2 基础配置
      • 2.7.3 vim插件
  • 3. Linux编译器:gcc/g++
    • 3.1 快速上手
    • 3.2 gcc/g++工作原理
    • 3.3 动静态库概念与理解
      • 3.3.1 库
      • 3.3.2 动静态库
  • 4. Linux项目自动化构建工具:make/Makefile
    • 4.1 快速上手
    • 4.2 小插曲:文件时间戳
    • 4.3 make/Makefile具有依赖性的推导能力
    • 4.4 make/Makefile语法拓展

1. Linux软件包管理器yum


1.1 yum介绍


1. 什么是软件包:

在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序。但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理解成windows上的安装程序)放在一个服务器上, 通过包管理器可以很方便的获取到这个编译好的软件包, 直接进行安装。软件包和软件包管理器, 就好比 “App” 和 “应用商店” 这样的关系。

在早期,智能手机还没有普及的时候,大多数人在电脑上下载软件都是去浏览器上直接搜,直接下载。但是这样下载下来的软件就很容易出问题(有病毒)。现在不一样了,大家装软件的时候一般都用自带的软件商店,有了一个统一的入口(至少大家用手机是这样)。这些软件商店都有审核APP的功能,这样病毒软件就不容易被大家所下载了。

大家再思考一个问题,应用商店里的软件是在你的手机里吗?肯定不是,应用商店中的软件都安装在一个远端的服务器上。所以,应用商店其实就是一个客户端,其实我们现在使用的所有APP大多数都是客户端软件。这个远端的服务器,就是服务器端。

跟手机相关的这些软件,服务器,都和手机厂商有关。应用商店这款客户端软件,和服务器,实际上就是手机厂商提供的。如果手机厂商发现在自己服务器中上线的软件有问题,或者说差评很多,它是有权利把这款软件下架的。所以,yum其实就相当于一个客户端软件-应用商店。

那么Linux下的软件服务器谁提供?难道是有人免费把软件写好放在一个免费的服务器中吗?别说,还真是。这就需要我们来理解一下yum的生态了。一款操作系统的好坏,比如CentOS,ubuntu,主要要看它的生态。设想一个场景:老板让你帮忙挑一款操作系统部署环境,你挑出了一款操作系统,理由是什么?你可以说这款操作系统的社区很活跃,有很多bug都已经暴漏出来并且被修复了,更重要的是XXX大公司也在使用这款操作系统,大公司一般都是发现漏洞的先锋部队,等我们需要bug时,差不多也被他们解决了,直接照搬解决方案即可(场景结束)。如果说有人喜欢一款操作系统,他是绝对不希望这款操作系统没落的,所以有一些牛人们就在自愿的去开发一些软件,完善操作系统,来开源的让大家使用。

而且,只要有大公司在用这款操作系统,那么这家公司是绝对不会希望这款操作系统倒下的。所以他们会捐助那些开发Linux服务器的人,大公司也不差这点钱。所以Linux不差钱,也不差人。

yum(Yellow dog Updater, Modified)是Linux下非常常用的一种包管理器. 主要应用在Fedora, RedHat, Centos等发行版上。(ubuntu系统上的软件包管理器是apt

2. yum怎么知道去哪里下载软件:

yum作为一个软件包安装工具,肯定知道去哪里下载软件,这个配置信息就在yum下的文件中,我们来通过命令查看一下:

在这里插入图片描述


1.2 用yum来下载软件


1. 做一个小实验,下载sl命令来跑一辆小火车:

通过命令yum list | grep sl,查找我们需要安装的软件,就像在应用商店搜索一样:

在这里插入图片描述

这里就可以看到我们要下的软件,然后执行命令yum install sl,将sl下载下来,之后直接输入sl按回车,就会出现一台奔跑的小火车:

在这里插入图片描述

输入命令yum remove sl可以卸载下好的sl。在执行这些命令时系统经常会提问你是否要执行该操作,如果你不想让他问你,可以使用-y,如yum remove -y sl

2. 如何下载官方软件源中没有的软件?

有些同学可能下载不了sl指令,这是因为官方的软件源中没有sl指令。我们可以先使用命令yum install -y epel-release下载扩展yum源,然后再yum install sl下载指令。


1.3 更新yum源


CentOS的yum源默认是国外的,也就是从国外的服务器上去下载软件,但是这样速度会很慢,我们希望从国内的服务器上去下载软件,这就需要更新yum源。

在这里插入图片描述

我们不要直接修改它,先将他备份一下。这里我备份成了CentOS-base.repo.bak。然后再修改CentOS-base.repo配置文件。使用命令vim /etc/yum.repos.d/CentOS-Base.repo

在这里插入图片描述

修改圈起来的内容,这里我已经配置好了,大家可能跟我的不一样,抄一下就行了。

修改好之后再执行两条命令:yum clean allyum makecache就可以了。一个是清空缓存,一个是重新制作缓存。


2. Linux编辑器:vi/vim


在这里插入图片描述

vi/vim其实就是一款文本编辑器。vi/vim的区别简单点来说,它们都是多模式编辑器,不同的是vimvi的升级版本,它不仅兼容vi的所有指令,而且还有一些新的特性在里面。例如语法加亮,可视化操作不仅可以在终端运行,也可以运行于x windowmac oswindows。我们课堂上,统一按照vim来进行讲解。


2.1 vim的基本概念


课堂上我们讲解vim的三种模式(其实有好多模式,目前掌握这3种即可),分别是命令模式(command mode)、插入模式(Insert mode)和底行模式(last line mode),各模式的功能区分如下:

1. 正常/普通/命令模式(Normal mode)

  • 控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入Insert mode下,或者到 last line mode。

2. 插入模式(Insert mode)

  • 只有在Insert mode下,才可以做文字输入,按「ESC」键可回到命令行模式。该模式是我们后面用的最频繁的编辑模式。

3. 底行模式(last line mode)

  • 文件保存或退出,也可以进行文件替换,找字符串,列出行号等操作。 在命令模式下,shift+: 即可进入该模式。要查看你的所有模式:打开vim,底行模式直接输入:help vim-modes

我这里一共有12种模式:six BASIC modessix ADDITIONAL modes


2.2 vim的基本操作


进入vim,在系统提示符号输入vim及文件名称后,就进入vim全屏幕编辑画面。例:vim test.c。不过有一点要特别注意,就是你进入vim之后,是处于[正常模式],你要切换到[插入模式]才能够输入文字。

1. [正常模式]切换至[插入模式]

  • 输入a
  • 输入i
  • 输入o

2. [插入模式]切换至[正常模式]

  • 目前处于[插入模式],就只能一直输入文字,如果发现输错了字,想用光标键往回移动,将该字删除,可以先按一下「ESC」键转到[正常模式]再删除文字。当然,也可以直接删除。

3. [正常模式]切换至[底行模式]

  • shift + :, 其实就是输入:退出vim及保存文件,在[正常模式]下,按一下:冒号键进入「Last line mode」,例如:
    • w(保存当前文件)
    • wq (输入wq,保存并退出vim)
    • q!(输入q!,不保存强制退出vim)

2.3 vim正常模式命令集


1. 插入模式:

  • 按「i」切换进入插入模式「insert mode」,按“i”进入插入模式后是从光标当前位置开始输入文件;
  • 按「a」进入插入模式后,是从目前光标所在位置的下一个位置开始输入文字;
  • 按「o」进入插入模式后,是插入新的一行,从行首开始输入文字。

2. 从插入模式切换为命令模式:

  • 按「ESC」键。

3. 移动光标:

  • vim可以直接用键盘来控制光标的上下左右移动,但正规的vim是用小写英文字母「h」、「j」、「k」、「l」,分别控制光标左、下、上、右移一格,也可以直接用键盘上的箭头控制;
  • 按「G」:移动到文章的最后;
  • 按「 $ 」:移动到光标所在行的“行尾”;
  • 按「^」:移动到光标所在行的“行首”;
  • 按「w」:光标跳到下个字的开头;
  • 按「e」:光标跳到下个字的字尾;
  • 按「b」:光标回到上个字的开头;
  • 按[gg]:进入到文本开始;
  • 按[shift+g]:进入文本末端;
  • 按「ctrl」+「b」:屏幕往“后”移动一页;
  • 按「ctrl」+「f」:屏幕往“前”移动一页;
  • 按「ctrl」+「u」:屏幕往“后”移动半页;
  • 按「ctrl」+「d」:屏幕往“前”移动半页。

4. 删除文字:

  • 「x」:每按一次,删除光标所在位置的一个字符;
  • 「#x」:例如,「6x」表示删除光标所在位置的“后面(包含自己在内)”6个字符;
  • 「X」:大写的X,每按一次,删除光标所在位置的“前面”一个字符;
  • 「#X」:例如,「20X」表示删除光标所在位置的“前面”20个字符;
  • 「dd」:删除光标所在行;
  • 「#dd」:从光标所在行开始删除#行。

5. 复制:

  • 「yw」:将光标所在之处到字尾的字符复制到缓冲区中;
  • 「#yw」:复制#个字到缓冲区;
  • 「yy」:复制光标所在行到缓冲区;
  • 「#yy」:例如,「6yy」表示拷贝从光标所在的该行“往下数”6行文字;
  • 「p」:将缓冲区内的字符贴到光标所在位置。注意:所有与“y”有关的复制命令都必须与“p”配合才能完成复制与粘贴功能。
  • 「#p」:从光标往下的位置粘贴#次。

6. 替换:

  • 「r」:替换光标所在处的字符,r前面也可以带#,表示替换#个字符;
  • 「R」:替换光标所到之处的字符,直到按下「ESC」键为止。

7. 撤销上一次操作:

  • 「u」:如果您误执行一个命令,可以马上按下「u」,回到上一个操作。按多次“u”可以执行多次回复;
  • 「ctrl + r」: 撤销的恢复。

8. 更改:

  • 「cw」:更改光标所在处的字到字尾处;
  • 「c#w」:例如,「c3w」表示更改3个字跳至指定的行;
  • 「ctrl」+「g」列出光标所在行的行号;
  • 「#G」:例如,「15G」,表示移动光标至文章的第15行行首;
  • 「~」:将光标后的一个字符进行大小写转化,同时移动光标。

9. 查找:

  • 「#」:输入#,会标记当前光标所在单词,并将该单词出现的地方全部标红,按n可以一直向前寻找。

2.4 vim底行模式命令集


在使用末行模式之前,请记住先按「ESC」键确定您已经处于正常模式,再按「:」冒号即可进入末行模式。

1. 列出行号:

  • 「set nu」: 输入「set nu」后,会在文件中的每一行前面列出行号。
    跳到文件中的某一行;
  • 「#」:「#」号表示一个数字,在冒号后输入一个数字,再按回车键就会跳到该行了,如输入数字15,再回车,就会跳到文章的第15行。

2. 查找字符:

  • 「/关键字」: 先按「/」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按「n」会往后寻找到您要的关键字为止;
  • 「?关键字」:先按「?」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按「n」会往前寻找到您要的关键字为止。

3. 保存文件:

  • 「w」: 在冒号输入字母「w」就可以将文件保存起来。

4. 离开vim:

  • 「q」:按「q」就是退出,如果无法离开vim,可以在「q」后跟一个「!」强制离开vim;
  • 「wq」:一般建议离开时,搭配「w」一起使用,这样在退出的时候还可以保存文件。

5. 在底行模式可以直接输入指令:

  • 「! cmd」:需要先输入一个!,然后后面就可以跟想执行的指令,这样就不用频繁退出vim去操作命令行了。

2.5 视图模式:批量化注释


1. 注释:

以图中代码为例:

在这里插入图片描述

我们先在普通模式下按ctr+v进入视图模式:

在这里插入图片描述

移动光标,选中多行:

在这里插入图片描述

输入shift+i直接进入插入模式,不要移动光标:

在这里插入图片描述

输入//

在这里插入图片描述

再按Esc退出视图模式,完成批量注释:

在这里插入图片描述

这个操作比较复杂,大家多多练习,熟练掌握。

2. 去掉注释:

还是ctr+v进入视图模式:

在这里插入图片描述

移动光标选择区域:

在这里插入图片描述

输入d删除选中内容,完成批量化的注释删除:

在这里插入图片描述


2.6 vim也支持多文件编辑


1. 多文件编辑演示:

在底行模式下,输入:vs + filename,就可以同时编辑多个文件。下面以同时编辑两个文件进行讲解,当然还可以再vs一下再打开一个文件。这里输入的命令是vs code.c,左边的文件就是code.c

在这里插入图片描述

无论打开多少个文件,光标只有一个,那么如何让光标在不同文件中进行切换呢?我们可以在普通模式(命令模式)下,输入ctr+ww进行光标的切换。

如果进入插入模式或者底行模式,光标在哪,就是进入了那个文件的相应模式。

2. 小技巧:

如果我们在写代码的时候出现了错误,并且终端将错误所在的行数提示了出来,比如是code.c第7行出错。我们可以通过vim code.c +7直接将光标定位到文件的第7行,想定位到哪一行就加几。


2.7 简单的vim配置


2.7.1 如何解决新用户无法sudo提权


在之后的配置中,可能遇到普通用户无法使用sudo命令的情况,所以这里我们先解决一下。在Linux系统中有一个信任列表,也叫白名单,只有在这个白名单下的用户才有权使用sudo指令。在CentOS7中,这个白名单的路径是/etc/sudoers,我们接下来要做的就是将普通用户添加到这个白名单中去。

su切换成root用户,然后输入指令vim /etc/sudoers用vim打开白名单,将自己的用户添加进入(我的用户名是LHY)。

在这里插入图片描述

然后保存并退出,普通用户LHY就可以使用sudo提权啦。


2.7.2 基础配置


1. 配置文件的位置

在目录/etc/ 下面,有个名为vimrc的文件,这是系统中公共的vim配置文件,对所有用户都有效。而在每个用户的主目录下,都可以自己建立私有的配置文件,命名为:.vimrc。例如,/root目录下,通常已经存在一个.vimrc文件,如果不存在,则创建之。

切换用户成为自己,进入自己的主工作目录,执行 cd ~。打开自己目录下的.vimrc文件(没有自己创建),执行vim .vimrc,编辑该文件。

2. 写入常用配置选项,用来测试(双引号后面是注释):

set nu              "显示行号
syntax on           "设置语法高亮
set cursorline      "设置行线
set tabstop=4       "设置 tab 键为4个空格的宽度
set shiftwidth=4    "设置自动缩进格式为4格
set autoindent      "设置自动缩进
set cindent         "使用 C/C++ 语言的自动缩进方式    

保存并退出,再次使用vim编辑任意文件:

在这里插入图片描述

大家可以使用Tab键测试一下是不是变成了缩进4个空格(默认是8个);现在的vim自带语法高亮,所以这个配置选项效果不明显;在用vim编写C/C++代码时,自动缩进也可以体现出来,大家可以自行测试一下。

网上也有很多配置vim的文章,大家可以自行查阅。


2.7.3 vim插件


完成了上面的基础配置,vim编写代码的效果还是很low的,我们还想让vim有自动补全单词等等一些功能,让vim向IDE一样方便,怎么办呢?这时就需要我们来下载各种插件了。

但是插件配置的学习成本太高,下面我们直接来执行一段脚本,一键完成vim的自动化部署。curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh,这段命令需要sudo执行(改配置仅适用于CentOS7)。

上面的脚本是大牛提前写好的vim配置,免费放在了gitee上,我们直接下载即可。

在这里插入图片描述

可以看到,完成配置后,vim界面会变得非常酷炫,并且拥有自动补齐等各种功能。但是默认缩进是两个空格,这是阿里的规则。如果大家不习惯,可以找到.vimrc文件,修改如下内容,将缩进改为4格。

在这里插入图片描述


3. Linux编译器:gcc/g++


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

如果大家的电脑上没有装gcc或g++,可以用我们之前学过的yum下载一下。注意,安装g++时,使用的指令是yum install gcc-c++,而不是yum install g++


3.1 快速上手


创建一个test.c文件,在里面写上一段代码:

#include<stdio.h>int sum()
{int sum = 0;for(int i = 1; i <= 100; i++){sum += i;}return sum;
}int main()
{int result = sum();printf("result:%d\n", result);return 0;
}                          

使用gcc编译该代码,编译的过程中可能会出现如下问题:

在这里插入图片描述

这是因为版本问题,我们统一指定用c99来编译源码即可:

在这里插入图片描述

编译成功,并且运行成功。

我们还可以使用选项-o来指定编译后目标文件的名字:

在这里插入图片描述


3.2 gcc/g++工作原理


1. 预处理:

  • 预处理功能主要包括宏定义,文件包含,条件编译,去注释等;
  • 预处理指令是以#号开头的代码行;
  • 实例: gcc –E hello.c –o hello.i
  • 选项“-E”,该选项的作用是让 gcc 在预处理结束后停止编译过程;
  • 选项“-o”是指目标文件,“.i”文件为已经过预处理的C原始程序。
// hello.c 文件的内容如下
#include <stdio.h>                                                                                          #define M 123#define V1 1
//#define V2 1int main()
{
#if V1printf("免费社区版\n");printf("hello Linux:%d\n", M);printf("hello Linux:%d\n", M);printf("hello Linux:%d\n", M);printf("hello C++\n");//printf("hello C++\n");//printf("hello C++\n");
#elif V2printf("专业版本\n");
#elseprintf("超级会员版\n");
#endifreturn 0;
}             

执行预处理指令gcc –E hello.c –o hello.i

在这里插入图片描述

2. 编译(生成汇编):

  • 在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言;
  • 用户可以使用“-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码;
  • 实例: gcc –S hello.i –o hello.s

在这里插入图片描述

3. 汇编(生成机器码):

  • 汇编阶段是把编译阶段生成的“.s”文件转成目标文件;
  • 读者在此可使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了。
  • 实例: gcc –c hello.s –o hello.o

在这里插入图片描述

注意,虽然此时hello.o文件是二进制的,但是它是不能执行的。

在这里插入图片描述

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

  • 在成功编译之后,就进入了链接阶段;
  • 实例: gcc hello.o –o hello

在这里插入图片描述

5. 总结:

上面只是让大家深入了解一下gcc/g++的工作流程,那么在实际应用中,我们只需要会gcc file -o filename即可。

其中选型我们可以特殊记忆,gcc预处理、编译、汇编所用的选项是-E/-S/-c,合起来就是我们键盘左上角的ESc,只需要注意大小写即可;文件名后缀分别时.i/.s/.o,合起来就是.iso,镜像文件的后缀就是.iso


3.3 动静态库概念与理解


3.3.1 库


1. 什么是库?

我们现在所写的所有代码,都是站在巨人的肩膀上的,已经有人给我们写好了对应的可以直接使用的函数了(比如printf())。那么这些功能函数,在哪里?又以什么方式呈现给我们呢?这就要提一提库的概念了。

使用ldd命令可以查看一个可执行程序所依赖的库有哪些:

在这里插入图片描述

我们在写代码时,头文件中包含的内容,是这些函数的声明,而库中所包含的,则是这些函数的具体实现。

我们的可执行程序=我们的代码+头文件+库。

Linux下一切皆文件,库也是文件。所以所谓的开发环境安装,就是安装下载并拷贝头文件和库文件到开发环境中的特定路径下,一定要能被编译器自己找到。

2. 库的命名:

libc.so.6为例,库的前缀都是lib,以.so为后缀的是动态库,以.a为后缀的是静态库。libc.so.6去掉前缀和后缀才是它真正的名字,就是c。所以这个库其实就是c标准库。

大多数的库都是动态库,静态库不多:

在这里插入图片描述

我的电脑中静态库只有这么多,动态库就太多了,这里就不再展示了。

3. 扩展:

在Windows中,也有动静态库的概念。其中xxxx.dll,以.dll结尾的是动态库;xxxx.lib,以.lib结尾的是静态库。


3.3.2 动静态库


1. 动态库:

动态库是C/C++或其他第三方库的所有方法的集合,被我们所写的程序以动态链接的方式,关联起来。库中所有的函数都有入口地址,所谓动态链接,其实就是把要链接的函数地址拷贝到我们的可执行程序的特定位置。

2. 静态库:

静态库也是C/C++或者其他第三方提供的所有方法的集合,被我们的程序以拷贝的方式(静态链接),将所需要的代码,直接拷贝到自己的可执行程序中。

3. 动静态库比较:

  • 动态链接优缺点
    • 优点:形成的可执行程序体积比较小,比较节省资源;
    • 缺点:速度稍慢一些(因为有去库中找函数的这样一个过程,但这不是主要缺点);有强依赖性,动态库没了,所有的依赖这个库的程序都无法运行了。

在这里插入图片描述

可以看到,Linux中绝大多数命令都是用C语言写的,这些命令也都依赖于C标准库,所以一旦我们将这个库删掉,Linux系统也就接近报废了。

  • 静态库优缺点
    • 优点:可以无视库,独立运行;
    • 缺点:体积大,浪费资源。

4. 实验验证:

现在有些Linux系统上默认是不安装静态库的,所以我们可以先安装一下:sudo yum -y install glibc-static

在这里插入图片描述

为什么系统一般不自带静态库,而选择动态库呢?就是因为静态链接的内存体积太大了,我们接受不了。


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


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

4.1 快速上手


1. 小实验:

我们先来创建一个makefileMakefile文件,一般建议m大写。在里面写入内容(注意依赖方法前面一定要加一个Tab):

在这里插入图片描述

什么是依赖环境和依赖方法呢?打个比方,你完成了找你爸要钱这个动作。那么这个过程中,你找你爸,就是依赖环境,要钱,就是完成的依赖方法。还有一个伪依赖环境,就是不写依赖环境,只写依赖方法,就像你把要来的钱花掉一样。

那么这个Makefile有什么用呢?

在这里插入图片描述

Makefile其实就是一个脚本文件,要配合命令make来使用。我们可以看到,配置好Makefile后,make命令自动帮我们完成了将原文件编写成可执行程序的工作。执行make clean指令,自动帮我们完成了可执行程序的清理工作。

既然我们可以通过make clean来执行清理的依赖方法,那么可不可以用make mybin来执行生成可执行程序的依赖方法呢?

在这里插入图片描述

答案是可以的。但是要注意,我们一般将文件的清理任务写在Makefile文件的最下面,如果将clean的逻辑写在最开头,那么我们再执行命令make(不加任何参数),默认执行的就是clean的依赖方法,因为make是从上向下扫描Makefile的,并且只执行形成的第一个目标文件。


4.2 小插曲:文件时间戳


1. 细节:

我们先来看一个现象,Makefile按上面截图的内容不变,连续运行两次make(保证mybin文件已经被清理了),再运行两次清除。

在这里插入图片描述

修改一下源文件的内容,发现make又可以执行了。

我们再修改一下Makefile里的内容:

在这里插入图片描述

然后再多次执行make,发现每次都成功执行了,不再有已经是最新文件,不让执行的限制:

在这里插入图片描述

大家可以大胆的猜测一下,就是和.PHONY有关。.PHONY后面跟的是伪目标,可以让其总是被执行。对于mybin,可以看到的作用就是让其突破了有没有被修改的限制;对于clean现阶段没有什么影响,但是建议在clean前加上.PHONY,标明clean是一个伪目标。

那么又产生了一个新的问题,为什么在我们编译代码的时候,makefilemake总不让我们重新编译我们的代码?现在我们写的代码还是很小型的,不管是编译还是重新编译,它的速度都是很快的。但是设想一下,我们今后处理的项目可能有上万行代码,它编译的速度是非常慢的,所以重新编译代码将是一件很耗费资源的事情。为了节省资源,避免误操作,对于没有更改过的新代码,就不让你重新编译了。

那么,make是怎么判断你是不是新文件的呢?判断的依据就是文件的修改时间。但是,单凭一个文件的修改时间是判别不出新旧的,只有和其他时间对比才能判别新旧,那么和谁对比呢?其实是拿源文件和可执行程序对比。

源文件是文件,我们形成的可执行程序也是文件。重新编译的本质,就是重新写入一个二进制可执行文件,它的修改时间理所当然的也会更改。并且,源文件的修改时间一定在可执行程序之前,因为可执行程序是后形成的。所以,如果源文件的修改时间比可执行程序早,那它就是新文件,不能重新编译;反之则是修改过的文件,可以重新编译。

2. 验证加讲解文件时间属性:

在这里插入图片描述

可以看到时间属性有三个,其中Access表示对内容的最近访问时间,Modify表示对内容的最近修改时间,Change表示对属性的最近修改时间。

其中Change time改变,可能只改变它自己,例如对文件的权限进行修改;对Modify time改变,大多数情况下会和Change timeAccsee time联动,例如用vim修改文件,大小属性会变,访问时间也要变;Access time改变,会和Change time联动,因为Accsee time也是属性,例如查看文件内容。

但是我们会发现,有时我们即使用cat查看了文件内容,Accsee时间也不会改变,这又是为什么?一般而言,一个文件被查看的频率是非常高的,我们所看到的文件,都是在磁盘中存放的,更改时间的本质,其实就是访问磁盘。若Linux系统中充满大量访问磁盘的io操作,实则是变相的减慢了系统速度,所以系统做了一些特殊处理,对更改Accsee时间,添加了次数限制,具体是多少完全看内核。

特殊记忆:文件有ACM时间。

3. 更改文件时间戳:

可以使用touch命令更改文件的时间戳。

在这里插入图片描述

直接touch + filename可以更新文件的所有时间(ACM时间)。细心的大家肯定发现了,Change时间一直在跟着其他两个时间改变,这是因为时间属性也是属性,改变属性,Change时间就会变。

4. 总结:

  • make配合Makefile可以做到,通过时间对比,不让有些代码进行重新编译;
  • 伪目标的作用是,可以让依赖方法总是被执行,不会被任何情况拦截。

4.3 make/Makefile具有依赖性的推导能力


重新编写Makefile如下图(提前声明,实际应用中是严重不推荐这样写的,直接gcc即可):

在这里插入图片描述

make在执行Makefile文件时,从上到下扫描,先看到mybin,依赖环境是hello.o,但是此时hello.o不存在,依赖方法无法执行,先将其入栈,接着向下扫描;再看到hello.o,依赖方法是hello.s,不存在,依赖方法无法执行,也将其入栈,接着向下扫描;依次类推,依赖环境不存在就一直向下扫描。

直到扫描到hello.i,它的依赖环境是hello.chello.c存在,执行依赖方法,然后往回推导,依次出栈,完成依赖方法。

执行makemake clean

在这里插入图片描述

现阶段先了解到这里,我们已经会对单文件做自动化构建和清理了,当然后面还有对多文件的自动化构建和清理,现阶段我们暂时用不上。


4.4 make/Makefile语法拓展


1. 设置不显示提示信息:

在上面的实验中,我们在执行makemake clean时,会发现执行完成后会有一行提示信息。现在我们不想要这些提示信息了,并且想让其输出我们设置的信息。

在这里插入图片描述

只需要在依赖方法前加上@,就不会打印提示信息了;并且我们可以通过echo输出我们设置的信息,来实践一下:

在这里插入图片描述

2. Makefile支持设置变量(也可以理解成宏):

在这里插入图片描述

实践一下:

在这里插入图片描述

3. 依赖关系和目标文件可以简写:

在这里插入图片描述

依赖关系可以简写成$^,目标文件可以简写成$@


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/743847.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java初阶数据结构队列的实现

1.队列的概念 1.队列就是相当于排队打饭 2.在排队的时候就有一个队头一个队尾。 3.从队尾进对头出 4.所以他的特点就是先进先出 所以我们可以用链表来实现 单链表实现要队尾进队头出{要有last 尾插头删} 双向链表实现效率高&#xff1a;不管从哪个地方当作队列都是可以的&…

OpenMP 编程模型

OpenMP 内存模型 共享内存模型&#xff1a; OpenMP 专为多处理器/核心、共享内存机器设计&#xff0c;底层架构可以是共享内存UMA或NUM OpenMP 执行模型 基于线程的并行&#xff1a; OpenMP 程序基于多线程来实现并行&#xff0c; 线程是操作系统可以调度的最小执行单元。 …

react 综合题-旧版

一、组件基础 1. React 事件机制 javascript 复制代码<div onClick{this.handleClick.bind(this)}>点我</div> React并不是将click事件绑定到了div的真实DOM上&#xff0c;而是在document处监听了所有的事件&#xff0c;当事件发生并且冒泡到document处的时候&a…

Facebook:连接世界的社交巨人

在当今数字化时代&#xff0c;Facebook作为全球最大的社交媒体平台之一&#xff0c;扮演着连接世界的重要角色。它不仅仅是一个社交网站&#xff0c;更是一个数字化的社交生态系统&#xff0c;影响着全球数十亿用户的生活和交流方式。本文将深入探讨Facebook的起源、用户规模和…

uniapp——第1篇:基于vue语法的、比原生开发屌的小程序开发

前提&#xff0c;建议先学会前端几大基础&#xff1a;HTML、CSS、JS、Ajax&#xff0c;还有一定要会Vue!&#xff08;Vue2\Vue3&#xff09;都要会&#xff01;&#xff01;&#xff01;不然不好懂 博主作为大二前端小白&#xff0c;刚刚接触前端微信小程序开发时选择的是基于“…

electron + vtkjs加载模型异常,界面显示类似于图片加载失败的图标

electron vtkjs加载模型显示异常&#xff0c;类似于图片加载失败的效果&#xff0c;如上图。 electron开发版本&#xff1a;13。 解决方法&#xff1a;升级electron版本号。 注意&#xff1a;win7最高兼容electron 22版本。

多维时序 | Matlab实现VMD-CNN-GRU变分模态分解结合卷积神经网络门控循环单元多变量时间序列预测

多维时序 | Matlab实现VMD-CNN-GRU变分模态分解结合卷积神经网络门控循环单元多变量时间序列预测 目录 多维时序 | Matlab实现VMD-CNN-GRU变分模态分解结合卷积神经网络门控循环单元多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现VMD-CN…

c++指针的定义和使用

1、定义一个指针 int a10; //定义指针的语法&#xff1a;数据类型 * 指针变量名&#xff1a;int * p&#xff1b; //让指针记录变量a的地址&#xff1a;p &a; int a 10;int* p; p &a; cout << "a的地址为&#xff1a;" << &a <<…

第二十二节 Java 流(Stream)、文件(File)和IO

Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。 Java.io 包中的流支持很多种格式&#xff0c;比如&#xff1a;基本类型、对象、本地化字符集等等。 一个流可以理解为一个数据的序列。输入流表示从一个源读取数据&#xff0c;输出流…

社区居民医疗健康系统 微信小程序

设计原则 本社区健康医疗APP采用 Hbuildex技术&#xff0c;使用Java语言开发&#xff0c;充分保证了系统稳定性、完整性。 社区健康医疗APP的设计与实现的设计思想如下&#xff1a; &#xff08;1&#xff09;操作简单方便、系统界面安全良好、简单明了的页面布局、方便查询相…

GPT-4.5 Turbo意外曝光,最快明天发布?OpenAI终于要放大招了!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

【计算机网络篇】计算机网络的定义和分类

文章目录 &#x1f354;什么是计算机网络&#x1f5c3;️计算机网络的分类⭐按交换方式分类⭐按使用者分类⭐按传输介质分类⭐按覆盖范围分类⭐按拓扑结构分类 &#x1f6f8;小结 &#x1f354;什么是计算机网络 计算机网络是指将多台计算机或其他网络设备通过通信链路连接起来…

汇编语言程序设计 第2章:8086指令系统简介及寻址方式

文章目录 1. 指针的分类及格式1.1 指令的分类1.2 指令格式 2. 寻址方式MOV指令简介2.1 立即寻址2.2 寄存器寻址2.3 直接寻址2.4 寄存器间接寻址2.5 寄存器相对寻址2.6 基址变址寻址2.7 相对基址变址寻址 3. 数据传送指令3.1 通用数据传送指令3.2 地址传送指令&#xff08;LEA、…

C语言黑魔法第三弹——动态内存管理

本文由于排版问题&#xff0c;可能稍显枯燥&#xff0c;但里面知识点非常详细&#xff0c;建议耐心阅读&#xff0c;帮助你更好的理解动态内存管理这一C语言大杀器 进阶C语言中有三个知识点尤为重要&#xff1a;指针、结构体、动态内存管理&#xff0c;这三个知识点决定了我们…

手把手教你打造研究生个人简历模板|轻松驾驭简历设计

在简历设计中&#xff0c;如何展现自己的学历优势&#xff0c;是很多学生困扰的问题。 下面&#xff0c;我们首先从即时设计中分享一些不同风格的研究生简历模板。 风格多样的免费简历设计模板https://js.design/community?categorysearch&search%E7%AE%80%E5%8E%86&…

使用STM32+ESP8266(ESP-01S)+点灯科技(手机端Blinker)实现远程控制智能家居

硬件准备&#xff1a;STM32单片机、ESP8266&#xff08;ESP-01S&#xff09;、CH340C下载烧录器 软件准备&#xff1a;STM32CubeMX、Keil uVision5、Arduino IDE、 点灯科技&#xff08;手机端APP Blinker&#xff09;点灯科技 (diandeng.tech)点击进入 值得注意的是&#x…

【EfficientNetV2】《EfficientNetV2: Smaller Models and Faster Training》

google ICML-2021 文章目录 1 Background and Motivation2 Related Work3 Advantages / Contributions4 Method4. 1 Understanding Training Efficiency4.2 Training-Aware NAS and Scaling4.3 Progressive learning 5 Experiments5.1 Datasets and Metrics5.2 ImageNet ILSVR…

力扣654 最大二叉树 Java版本

文章目录 题目描述解题思路代码 题目描述 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。 递归地在最大值 左边 的 子数组前缀上 构建左子树。 递归地在最大值 右边 的 子数组后缀上…

【Python】新手入门学习:详细介绍单一职责原则(SRP)及其作用、代码示例

【Python】新手入门学习&#xff1a;详细介绍单一职责原则&#xff08;SRP&#xff09;及其作用、代码示例 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyT…

力扣106 从中序与后续遍历序列构造二叉树

文章目录 题目描述解题思路代码 题目描述 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder [9,3,15,20,7], …