寻不到花的折翼枯叶蝶,永远也看不见凋谢.............................................................................
文章目录
前言
一、【基本指令】
1、ls
2、pwd
3、cd
4. touch
5.mkdir
6.rmdir
7、rm
8.man
9.cp
10、mv
11、cat
12、tac
13、more
14、less指令
15、head和tail
16、date
17、Cal指令
18、find
19、which
20、alias
21、grep
22、zip和unzip
22、tar指令
22、file
23、bc
24、uname
25、几个重要的热键
26、关机指令
27、命令扩展
二、【shell运行原理及权限问题】
2.1【shell的原理】
2.2【权限概念】
1、【各用户之间的切换】
2、【指令提权】
2.3【权限管理】
1、【文件访问者的分类(人)】
2、【文件类型和访问权限(事物属性)】
3、【文件权限值的表示方法】
字符表示方法
八进制数值表示法
4、【文件访问权限的相关设置方法】
如何修改某用户对某文件的权限
如何改变文件的拥有者
如何改变文件的所属组
如何修改文件的掩码
5、【目录的权限】
1、粘滞位
2、关于目录权限的总结
三、【基础工具】
3.1、【Linux软件包管理器 - yum】
1、【yum的介绍】
编辑2、【查找软件包】
3、【安装软件】
4、【如何实现本地机器和云服务器之间的文件互传?】
编辑5、【卸载软件】
3.2、【Linux编辑器 - vim】
1、【vim的基本概念】
2、【vim下各模式的切换】
3、【vim命令模式各命令汇总】
【移动光标】
【删除】
【复制粘贴】
【剪切】
【撤销】
【大小写切换】
【替换】
【更改】
【翻页】
4、【vim底行模式各命令汇总】
【行号设置】
【保存退出】
【分屏指令】
【执行指令】
3、【vim的简单配置】
3.3【Linux编译器 —— gcc/g++】
1、【gcc/g++的作用】
2、【gcc/g++语法】
预处理
编辑编译
汇编
链接
3、【静态库与动态库】
3.4【Linux调试器 —— gdb】
1、【gdb使用须知】
2、【gdb命令汇总】
(1)【进入gdb】
(2)【启动gdb】
(3)【显示】
(4)【断点】
(5)【调试】
(6)【退出gdb】
3.5【Linux项目自动化构建工具 —— make/Makefile】
1、【make/Makefile的重要性】
2、【依赖关系和依赖方法】
3、【多文件编译】
4、【make原理】
5、【项目清理】
6、【伪目标———.PHONY】
什么是伪目标:
Access:
Modify:
Change:
总结一下:
总结
前言
本篇对Linux的一些基本指令,一些基础工具,以及一些基本的环境配置进行了讲解,请耐心观看!
一、【基本指令】
为了方便命令的演示我提前创建了两个目录分别叫做date_0415和date_0506,位于下面的位置:
/root/xzc/work/
1、ls
ls命令用来查看某一目录下有哪些目录和文件,可以指定要查看的目录或者文件:
语法:
ls [选项] [目录或文件]
功能:对于目录,该命令列出该目录下的所有子目录与文件。对于文件,将列出文件名以及其他信息。并且可以在ls后加入其他选项以便完成其他功能:
1. -a 列出目录下的所有文件,包括以 . 开头的隐含文件:注:在Linux当中,每个目录下默认存在两个隐含文件 . 和 . . ( . 代表当前目录 . . 代表上级目录)。
2.-d 将目录象文件一样显示,而不是显示其下的文件。 如:ls –d 指定目录
3.-i 输出文件的 i 节点的索引信息。 如 ls –ai 指定文件
4.-k 以 k 字节的形式表示文件的大小。ls –alk 指定文件5.-l 列出文件的详细信息。
也可以使用ll(ll-->ls -l):
-n 用数字的 UID,GID 代替名称。 (介绍 UID, GID)
-F 在每个文件名后附上一个字符以说明该文件的类型,“*”表示可执行的普通文件;“/”表示目录;“@”表示符号链接;“|”表示FIFOs;“=”表示套接字(sockets)。(目录类型识别)
-r 对目录反向排序。
-t 以时间排序。
-s 在l文件名后输出该文件的大小。(大小排序,如何找到目录下最大的文件)
-R 列出所有子目录下的文件。(递归)
-1 一行只输出一个文件。2、pwd
用来确认当前所处目录。
语法: pwd
功能:显示用户当前所在的目录3、cd
Linux系统中,磁盘上的文件和目录被组成一棵目录树,每个节点都是目录或文件。
语法:cd 目录名
功能:改变工作目录。将当前工作目录改变到指定的目录下。在开始之前让我们先用tree命令看一下我们的目录树:
cd .. : 返回上级目录 cd /root/xzc/work/ : 绝对路径 cd ../date_0415/ : 相对路径 cd ~:进入用户家目录 cd -:返回最近访问目录
cd ~
cd -
注意:普通用户的主工作目录默认为 /home/xxx,root用户的主工作目录默认为 /root。
4. touch
语法:touch [选项]... 文件...
功能:touch命令参数可更改文档或目录的日期时间,包括存取时间和更改时间,或者新建一个不存在的文件。
常用选项:
-a 或--time=atime或--time=access或--time=use只更改存取时间。
-c 或--no-create 不建立任何文档。
-d 使用指定的日期时间,而非现在的时间。
-f 此参数将忽略不予处理,仅负责解决BSD版本touch指令的兼容性问题。
-m 或--time=mtime或--time=modify 只更改变动时间。
-r 把指定文档或目录的日期时间,统统设成和参考文档或目录的日期时间相同。
-t 使用指定的日期时间,而非现在的时间。5.mkdir
在windows中目录一般称为文件夹,在Linux中称为目录。
语法:mkdir [选项] dirname...
功能:在当前目录下创建一个名为 “dirname”的目录
常用选项:
-p, (--parents) 可以是一个路径名称。此时若路径中的某些目录尚不存在,加上此选项后,系统将自动建立好那些尚不存在的目录,即一次可以建立多个目录;6.rmdir
rmdir是一个与mkdir相对应的命令。mkdir是建立目录,而rmdir是删除命令。语法:rmdir [-p][dirName]
适用对象:具有当前目录操作权限的所有使用者
功能:删除空目录我们看到d1为空目录,那么可以直接进行删除:
而t4不是空目录,直接删除会出错。
常用选项:
-p 当子目录被删除后如果父目录也变成空目录的话,就连带父目录一起删除。7、rm
rm命令可以同时删除文件或目录,所以我们更常使用rm。
语法:rm [-f-i-r-v][dirName/dir]
适用对象:所有使用者
功能:删除文件或目录我们会发现在删除时会进行询问,那么可以通过选项参数进行避免。
常用选项:
-f 即使文件属性为只读(即写保护),亦直接删除
-i 删除前逐一询问确认
-r 删除目录及其下所有文件(递归式)对于以下目录树删除t1中的所有文件,不询问
注意:
当你要删除当前目录下的所以文件时,可以使用通配符 * 。
但是为了保险起见,一般使用通配符删除当前目录下的所有文件时,建议在通配符 * 前面加上 . / 告知系统要删除的资源在当前目录下( . 代表当前目录)。上面说到 -f 是强制性删除,-r 是递归式删除,这两个结合在一起就是递归式强制性删除,千万不要执行以下指令,因为在Linux当中没有类似回收站的东西也没有撤销删除的功能,删除指令一旦执行则是不可逆的。
该指令的意思是从(/)根目录开始进行递归式强制性删除,即删除根目录下所有文件,这是一个大忌,典型的删库跑路行为。所以,当你要使用“rm -rf dirname”这条指令前,你必须明确以下两点:
1、你清楚的知道你自己正在干什么。
2、执行该命令后所造成的结果是你能够承担的。8.man
Linux的命令有很多参数,我们不可能全记住,但是我们可以通过查看联机手册获取帮助。而访问Linux联机手册页的命令是man语法: man [选项] 命令
常用选项
-k 根据关键字搜索联机帮助
num 只在第num章节找
-a 将所有章节的都显示出来,比如 man printf 它缺省从第一章开始搜索,知道就停止,用a选项,当按下q退出,他会继续往后面搜索,直到所有章节都搜索完毕。解释一下手册主要分为8章,具体如下:
1 是普通的命令
2 是系统调用,如open,write之类的(通过这个,至少可以很方便的查到调用这个函数,需要加什么头文件)
3 是库函数,如printf,fread4是特殊文件,也就是/dev下的各种设备文件
5 是指文件的格式,比如passwd, 就会说明这个文件中各个字段的含义
6 是给游戏留的,由各个游戏自己定义
7 是附件还有一些变量,比如向environ这种全局变量在这里就有说明
8 是系统管理用的命令,这些命令只能由root使用,如ifconfig(仅限于root用户)9.cp
在Windows中我们可以通过Ctrl+C和Ctrl+V将一个地方的文件或目录复制一份到另一个地方,我们若是在Linux当中想要完成该操作,就要用到cp指令。
语法:cp [选项] 源文件或目录 目标文件或目录
功能: 复制文件或目录对于以下目录树:
说明: cp指令用于复制文件或目录,如同时指定两个以上的文件或目录,且最后的目的地是一个已经存在的目录,则它会把前面指定的所有文件或目录复制到此目录中。若同时指定多个文件或目录,而最后的目的地并非一个已存在的目录,则会出现错误信息。
这里如果出现同名文件的情况,就会进行询问是否覆盖:常用选项:
-f 或 --force 强行复制文件或目录, 不论目的文件或目录是否已经存在
-i 或 --interactive 覆盖文件之前先询问用户
-r或者-R --recursive,递归处理,将指定目录下的文件与子目录一并处理。若源文件或目录的形态,不属于目录或符号链接,则一律视为普通文件处理
10、mvmv命令是move的缩写,可以用来移动文件或者将文件改名(move (rename) files),是Linux系统下常用的命令,经常用来备份文件或者目录。
语法: mv [选项] 源文件或目录 目标文件或目录
功能:
1. 根据mv命令中第二个参数类型的不同而就具有不同的功能1)当第二个参数是不存在的名称时,mv指令完成文件重命名或目录重命名。
2)当第二个参数是已存在目录名称时,mv指令将文件移动置该目录中。
3)当第二个参数是已存在的目录名称时,源文件或目录参数可以有多个,mv命令将各参数指定的源文件均移至目标目录中。同样出现重名文件的问题,就会询问是否进行覆盖。常用选项:
-f :force 强制的意思,如果目标文件已经存在,不会询问而直接覆盖
-i :若目标文件 (destination) 已经存在时,就会询问是否覆盖!11、cat
语法:cat [选项][文件]
功能: 查看目标文件的内容用nano为text.txt文件编写内容,并用cat进行打印。
常用选项:
-b 对非空输出行编号
-n 对输出的所有行编号
-s 不输出多行空行注意: cat指令会将目标文件内容全部打印到屏幕上。若是目标文件内容过大,则会导致刷屏(直到将文件内容全部打印到屏幕上)。因此cat指令一般仅适用于查看内容较少的文件。
12、tac
使用tac指令,我们可以反向查看目标文件的内容。
语法: tac 文件
功能: 反向查看目标文件的内容。13、more
上面说到,cat指令仅适用于查看内容较少的文件,我们若是要查看海量信息文件,则可以适用more指令。
语法:more [选项][文件]
功能:more命令,功能类似 cat
注意: more指令只能通过按回车键进行下翻操作,无法进行上翻操作,并且more指令在查看之前会先加载整个文件。14、less指令
less 工具也是对文件或其它输出进行分页显示的工具,应该说是linux正统查看文件内容的工具,功能极其强大,这也就意味着相较于cat和more我们更喜欢使用less。
less 的用法比起 more 更加的有弹性。在 more 的时候,我们并没有办法向前面翻, 只能往后面看但若使用了 less 时,就可以使用 [pageup][pagedown](一般是键盘的方向键) 等按键的功能来往前往后翻看文件,更容易用来查看一个文件的内容!
除此之外,在 less 里头可以拥有更多的搜索功能,不止可以向下搜,也可以向上搜。语法: less [参数] 文件
功能:
less与more类似,但使用less可以随意浏览文件,而more仅能向前移动,却不能向后移动,而且less在查看之前不会加载整个文件。
选项:
-i 忽略搜索时的大小写
-N 显示每行的行号
/字符串:向下搜索“字符串”的功能
?字符串:向上搜索“字符串”的功能
n:重复前一个搜索(与 / 或 ? 有关)
N:反向重复前一个搜索(与 / 或 ? 有关)
15、head和tail
head 与 tail 就像它的名字一样的浅显易懂,它们是用来显示开头或结尾某个数量的文字区块。head 用来显示档案的开头至标准输出中。
语法: head [参数]... [文件]...
功能:
head 用来显示档案的开头至标准输出中,默认head命令打印其相应文件的开头10行。
选项:
-n<行数> 显示的行数而 tail 当然就是看档案的结尾,tail 命令从指定点开始将文件写到标准输出。
使用tail命令的-f选项可以方便的查阅正在改变的日志文件,tail -f filename会把filename里最尾部的内容显示在屏幕上,并且不但刷新,使你看到最新的文件内容.
语法: tail[必要参数][选择参数][文件]
功能: 用于显示文件的末尾至标准输出中,默认tail指令打印其相应文件的末尾10行。不指定文件时,作为输入信息进行处理。常用查看日志文件。
选项:
-f 循环读取
-n<行数> 显示行数我们现在既能单独取出文件的头和尾了,那么该如何取出文件的某一段内容呢?
例如,现在big.txt文件当中有1000行内容,我们如何读取第101至110行?
在认识了head和tail指令后,你可能很容易想到如下方法:先用head指令读取test.txt文件中的前110行存入临时文件temp.txt中,然后将temp.txt文件的后10行打印输出即可。
如下:
注意:
>(表示输出重定向),图中第一条指令的意思是,将本应该输出到屏幕的内容输出重定向到tem.txt文件当中。
这样写显然有点麻烦(还需要创建一个临时文件),有一种简便的方式可以实现该操作,该方式需要用到“命令行管道”。我们知道,管道是用于传输某种资源的(下水管道用于传输水,天然气管道用于传输天然气),而计算机上面最重要的资源就是数据。
因此我们可以将big.txt文件的前110行从管道左侧输入管道,并在管道右侧读取数据进行进一步的操作即可。如下图:
16、date
date 指定格式显示时间: date +%Y:%m:%ddate 用法:date [OPTION]... [+FORMAT]
1.在显示方面,使用者可以设定欲显示的格式,格式设定为一个加号后接数个标记,其中常用的标记列表如下 :
%H : 小时(00..23)
%M : 分钟(00..59)
%S : 秒(00..61)
%d : 日 (01..31)
%m : 月份 (01..12)
%Y : 完整年份 (0000..9999)%X : 相当于 %H:%M:%S
%F : 相当于 %Y-%m-%d2.在设定时间方面
1)date -s 设置当前时间,只有root用户才能设置,普通用户只能查看。
2)date -s 20210725 将时间设置为20210725,这样会将具体时间设置为空(00:00:00)。
3)date -s 01:01:01 设置具体时间,不会对日期做更改。
4)date -s “01:01:01 2021-07-25” 同时设置日期和具体时间。
5)date -s “01:01:01 20210725” 同时设置日期和具体时间。
6)date -s “2021-07-25 01:01:01” 同时设置日期和具体时间。
7)date -s “2021072501:01:01” 同时设置日期和具体时间。3.时间戳
时间->时间戳:date +%s
时间戳->时间:date -d@1508749502
Unix时间戳(英文为Unix epoch, Unix time, POSIX time 或 Unix timestamp)是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。注意
时间戳是当前时间与计算机起始时间的差值,单位为秒。
17、Cal指令
cal命令可以用来显示公历(阳历)日历。公历是现在国际通用的历法,又称格列历,通称阳历。“阳历”又名“太阳历”,系以地球绕行太阳一周为一年,为西方各国所通用,故又名“西历”。命令格式: cal [参数][月份][年份]
功能: 用于查看日历等时间信息,如只有一个参数,则表示年份(1-9999),如有两个参数,则表示月份和年份
常用选项:
-3 显示系统前一个月,当前月,下一个月的月历
-j 显示在当年中的第几天(一年日期按天算,从1月1号算起,默认显示当前月在一年中的天数)
-y 显示当前年份的日历18、find
语法: find 路径 选项 …
功能: 用于在文件树中查找文件,并作出相应的处理(可能访问磁盘->效率比较低)。常用选项:
1)-name 按照文件名查找文件。2)-size 按照文件大小查找文件。
19、which
Linux当中的每个指令其实都是用计算机语言编写出的可执行程序,这些程序都存储在文件当中,我们可以通过which指令查看某一指令的程序文件所在路径。
语法: which 指令
功能: 用于查找某一指令的程序文件所在路径。当我们执行某一指令时,实际是调用其可执行程序,因此我们还可以通过其程序文件的所在路径进行调用该指令。
20、alias
当你觉得某一指令太长时,你可以使用alias指令给它起一个别名。
语法: alias 别名=‘原名’
功能: 可以用于给指令取别名。21、grep
grep指令类似于关键词搜索(行过滤器)。
语法: grep 选项 搜寻字符串 文件
功能: 在文件中搜索字符串,将找到的行打印出来。常用选项:
-i :忽略大小写的不同,所以大小写视为相同
-n :顺便输出行号
-v :反向选择,亦即显示出没有 '搜寻字符串' 内容的那一行22、zip和unzip
zip指令用于打包压缩目录或文件。
语法: zip 选项 压缩文件.zip 待打包目录或文件
功能: 将目录或文件打包压缩成zip格式。常用选项:
-r 递归处理,将指定目录下的所有文件和子目录一并处理。unzip指令用于解压文件。
语法: unzip 压缩文件.zip 选项 目录
功能: 将压缩文件解压到指定目录当中。常用选项
-d 用于指定解压的目录,如果不加-d 则默认解压到当前目录下。
22、tar指令
tar指令也可以用来打包压缩和解压文件,并且还可以实现不解开压缩包,直接查看里面内容的操作,以及只打包不压缩等操作。语法: tar 选项 压缩文件 …
功能: 打包/解包,不解开压缩包,直接查看里面内容。常用选项:
1)-c 建立一个压缩文件的参数指令(create的意思)。
2)-x 解开一个压缩文件的参数指令。
3)-t 查看tarfile里面的文件。
4)-z 是否同时具有gzip属性?即是否需要用gzip压缩?
5)-j 是否同时具有bzip2属性?即是否需要用bzip2压缩?
6)-v 压缩的过程中显示文件(常用)。
7)-f 使用档名,请留意,在f之后要立即接档名,不要再加参数。
8)-C 解压到指定目录。示例:
打包操作:
解包操作:
不解包直接查看其内容:
22、file
语法: file
功能: 查看文件的更多属性信息。23、bc
Windows里面有计算器,Linux下面也有,bc指令就是Linux下的计算器。
语法: bc
功能: 进行运算。24、uname
语法: uname 选项
功能: 用于获取电脑和操作系统的相关信息。
常用选项:
1)-r 输出电脑和操作系统相关信息。2)-a 详细输出所有信息,依次为内核名称,主机名,内核版本号,内核版本,硬件名,处理器类型,硬件平台类型,操作系统名称。
25、几个重要的热键
1)上下按键。上下按键在Linux当中存储的是历史命令,通过按上下键我们可以查看我们最近敲的命令。
我们也可以使用history指令查看历史命令。2)Ctrl+D。退出当前命令行。
3)Ctrl+C。让当前程序停掉(终止失控程序)。
4)Tab。自动补齐功能。5)Ctrl+R。搜索历史命令。
26、关机指令
语法: shutdown 选项
常见选项:
1)-h 将系统的服务停掉后,立即关机。
2)-r 将系统的服务停掉之后就重新启动。
3)-t sec 经过sec秒后关机。
注: 所有关机指令只能由root用户执行。27、命令扩展
1)安装和登录命令:login、shutdown、halt、reboot、install、mount、umount、chsh、exit、last。 2)文件处理命令:file、mkdir、grep、dd、find、mv、ls、diff、cat、ln。 3)系统管理相关命令:df、top、free、quota、at、lp、adduser、groupadd、kill、crontab。 4)网络操作命令:ifconfig、ip、ping、netstat、telnet、ftp、route、rlogin、rcp、finger、mail、nslookup。 5)系统安全相关命令:passwd、su、umask、chgrp、chmod、chown、chattr、sudo ps、who。 6)其他命令:tar、unzip、gunzip、unarj、mtools、man、unendcode、uudecode。
如何添加和删除用户?
这里我们可以发现我们使用的是root用户,而root用户是linux系统中权限最高的用户,因此为了防止使用root用户做出一些不可挽回的错误我们实际上在现实的开发中经常会使用普通用户,下面让我们了解一下如何创建一个普通用户:
可以通过
ls /home
对新创建的用户进行查看:
总结:
第一步: 输入addser yourid(yourid是待创建的用户的用户名)。
第二步: 输入passwd yourid进行设置密码。 Linux当中输入密码时,密码不回显。
第三步: 再次输入密码进行确认。
那么我们现在已经可以创建普通用户了,当我们不再使用普通用户时,又该如何删除呢?
删除账号时之间输入userdel -r yourid(yourid是要删除的用户的用户名)。
此时我们在/home目录下便看不到该用户的用户名了。
注意: 创建和删除用户的操作者都必须是root用户,而不是普通用户。
二、【shell运行原理及权限问题】
2.1【shell的原理】
我们几乎都用过Windows操作系统,都知道Windows以图形化界面为交互方式,而Linux以命令行界面为交互方式。Windows和Linux的交互方式虽然不同,但本质上是一样的,图形化界面和命令行界面都是为了让用户进行相关操作从而减少用户的使用成本,而图形化界面和命令行界面就是我们所说的“外壳程序”。
Linux严格意义上说的是一个操作系统,我们称之为“核心(kernel)“ ,但我们一般用户,不能直接使用kernel。而是通过kernel的“外壳”程序,也就是所谓的shell,来与kernel沟通。
那么我们为什么不能直接使用kernel?
从技术角度,Shell的最简单定义是命令行解释器(command Interpreter)它主要包含:
1、将使用者的命令翻译给核心(kernel)处理。
2、同时,将核心的处理结果翻译给使用者。
对比windows GUI,我们操作windows 同样也不是直接操作windows内核,而是通过图形接口,点击,从而完成我们的操作。
shell 对于Linux,具有有相同的作用,主要是对我们的指令进行解析,解析指令给Linux内核。反馈结果在通过内核运行出结果,通过shell解析给用户。
Shell运行原理:
(1)创建子进程,让子进程进行命令行解释。
(2)子进程出现任何问题,都不影响父进程Shell。注意: Shell只是所有外壳程序的统称,例如在centos 7当中的外壳程序名叫bash。
2.2【权限概念】
Linux下有两种用户:超级用户(root)、普通用户。
超级用户:可以再linux系统下做任何事情,不受限制
普通用户:在linux下做有限的事情。
超级用户的命令提示符是“#”,普通用户的命令提示符是“$”。1、【各用户之间的切换】
实际上我们在大多数的使用场景下都是以普通用户的身份来完成操作的,但是有时普通用户可能权限不够,需要从普通用户切换到超级用户,进而完成某些操作。
那么我们该如何完成从普通用户到超级用户之间和从超级用户到普通用户之间的切换呢?
实际上我们就需要用到su命令。
命令:su [用户名] 功能:切换用户。
输入命令以后输入对应用户的密码即可。
注意:
(1)从普通账号切换为root账号时,指令当中的root可省略,因为root账号只有一个。
(2)该指令也可以从一个普通用户切换为另一个普通用户,输入待切换用户的账号密码即可。
(3)切换用户后,若想切回上次的用户,可通过Ctrl+D实现。2、【指令提权】
我们都知道root的权力很大,我们不能每次想要完成一些权限高的操作时就将自己切换成root用户,这样即麻烦又不安全,实际上对于上面的情景我们采用的是提权操作sudo命令。
语法: sudo 指令 功能: 提升当前指令的权限。
当我们输入sudo进行提权操作以后需要输入当前所使用普通用户的密码,但是有时候也会遇到提权失败的情况,这时候很大可能是因为当前的普通用户没有在root用户的可信任列里,我们在进行提权操作之前必须要将该普通用户添加到root的可信任列表中,具体操作如下:
首先只有root才能将用户添加到可信任列表中,所以我们要先切换到root用户,然对/etc/sudoers文件进行修改。
添加完毕后,该用户就可以使用sudo指令,也就是拥有提升当前指令权限的能力了。
2.3【权限管理】
1、【文件访问者的分类(人)】
对于用户来说,权限可以将用户分为三大类:
(1)文件和文件目录的所有者(文件拥有者)。
(2)文件拥有者所在的组的用户(文件所属组)。
(3)其他用户(other)。注意:
(1)对于某一文件而言,其拥有者、所属组和other就是由超级用户(root)和普通用户所扮演。除了文件拥有者和文件所属组之外的都叫other。
(2)在Linux当中,所有用户都要隶属于某一个组,哪怕这个组只有你一个人(此时该组就以你的用户名为组名)。那么为什么会有所属组的概念呢?
如果没有所属组的概念那么当你创建了一个文件后,要么就是只有你自己(拥有者)能看到,要么就是其他人(other)也都能看到。而你所希望的是你自己和你的小组成员能看到,剩下的人看不到。
于是就有了所属组这个概念,这时你就可以将文件设置为拥有者和所属组可见,而other不可见。所以所属组的存在是为了更灵活的进行权限配置,满足团队协作。这里我们可以通过指令ll来查看某一文件或文件目录的拥有者和所属组。注:对于root而言不存在权限的概念,root不受权限的约束。
2、【文件类型和访问权限(事物属性)】
权限涉及到某个具体的事物来说,我们还需要讨论事物本身的属性。对于文件来说,我们应该讨论其文件类型,以及是否具有可读、可写和可执行的属性。
使用指令ll,我们可以看到前面有一串字符,这串字符实际上就代表着该文件的类型和属性。
这串字符由10个字符组成的。其中第一个字符所代表的就是该文件的文件类型。
常见的类型:
不同的字符代表不同的文件类型。 1)-:代表普通文件。 2)d:代表目录。 3)l:代表链接文件(类似于Windows当中的快捷方式)。 4)b:代表块设备文件(例如硬盘、光驱等)。 5)p:管道文件。 6)c:字符设备文件。 7)s:套接口文件。
注意: 在Linux当中,文件类型与文件后缀无关,简单来说就是Linux下一切皆文件,意思是说在linux下无论什么后缀的文件(像.c,.cpp,.py)都是普通的文本文件,但是这并不代表当我们使用一些工具时也不要求文件的后缀,比如我们使用gcc编译c语言文件时,文件后缀必须为.c,所以我们在创建文件时最好还是像windows下面为其指定后缀。剩下的9个字符每三个为一组,分别代表该文件相对于其拥有者、所属组以及other是否拥有某种属性。
举个例子:
data.txt文件是一个普通文件,该文件的拥有者和所属组对其都是可读可写的,但该文件的other对其只有读的权力。
3、【文件权限值的表示方法】
字符表示方法
ll指令打印文件权限值时的表示方法就是字符表示法。例如八进制数值表示法
字符表示法中的每一个字符所在位置所表示的结果只有两种可能,要么为真,要么为假,因此我们可以将这三个字符换为三个二进制位,进而换为一个八进制位进行表示。例如
4、【文件访问权限的相关设置方法】
如何修改某用户对某文件的权限
我们一般使用chmod这个命令来实现某一用户对某文件的权限问题修改。
语法: chmod 选项 权限 文件名或目录名 功能: 设置文件的访问权限。 常用选项: -R 递归修改目录文件的权限。
chmod指令权限值的格式:
格式一: 用户符号 +/-/= 权限字符
1)+:向权限范围增加权限代号所表示的权限。 2)-:向权限范围取消权限代号所表示的权限。 3)=:向权限范围赋予权限代号所表示的权限。 用户符号: 1)u:拥有者。 2)g:所属组。 3)o:other。 4)a:所有用户。
下面看一个例子:
格式二: 三位八进制数字
我们将rwx与8进制数字联系在一起,即当有某个权限时就为1,否则为0。然后将对应的八进制数转换为二进制,进而设置对应权限值。
如何改变文件的拥有者
语法: chown 选项 用户名 文件名或目录名 功能: 修改文件的拥有者。 常用选项: -R 递归修改目录文件的拥有者。
注意: 修改文件的拥有者需要root用户进行操作,若是普通用户则需要进行权限提升。
也可以使用chown指令同时修改文件的拥有者和所属组,将拥有者和所属组的用户名用冒号隔开即可。
如何改变文件的所属组
语法: chgrp 选项 用户名 文件名或目录名 功能: 修改文件的所属组。 常用选项: -R 递归修改目录文件的所属组。
注意: 修改文件的所属组也需要进行权限提升。
如何修改文件的掩码
我们查看新建的文件和目录,它们都有自己默认的权限。
实际上,新建文件的默认权限为0666,新建目录的默认权限为0777。其中第一位的0与特殊权限有关,我们这里不必深究,而后面三位就是权限的八进制数值表示方法,我们将其翻译为字符表示方法。
但实际上你会发现,你所创建出来的文件和目录的权限值往往不是我们所翻译出来的值,原因就是创建文件和目录的时候还要受到umask的影响,假设默认权限是mask,则实际创建出来的文件权限是:mask&(~umask)
语法: umask 权限值 功能: 查看或修改文件掩码。
我们可以通过指令umask查看文件默认掩码。
因此我们实际创建出来的文件和目录的权限值还需要进行进一步换算才能得出。首先我们将掩码的的后三位八进制换算为二进制,然后对其进行按位取反。
然后将之前的新建文件的默认权限值和新建目录的默认权限值分别与其进行按位与操作,得到的就是我们创建出来的文件和目录的权限值。
也可以理解为凡是在umask中出现的权限位,都不能在最终权限中出现。
因此我们也可以通过修改umask来设置文件的访问权限。
注意: 超级用户的默认掩码为0022,普通用户的默认掩码为0002。
5、【目录的权限】
对于文件来说,其可读可写可执行的属性我们都知道分别代表着什么对应的操作,那对于目录来说可读可写可执行又分别代表着什么呢?
1)可读权限: 如果用户没有该目录的可读权限,则无法通过ls指令查看目录中的文件内容。
2)可写权限: 如果用户没有该目录的可写权限,则无法通过一系列指令在目录中创建文件或删除文件。
3)可执行权限: 如果用户没有该目录的可执行权限,则无法通过cd指令进入到目录当中。那么这就会出现一个问题:
只要用户拥有某目录的可写权限,就可以删除该目录当中的文件,而不论该用户是否拥有该文件的可写权限,这显然是不合理的。(即删除文件与文件本身无关)为了解决这个不合理的问题,Linux引入了粘滞位的概念。
1、粘滞位
语法: chmod +t 目录名 功能: 给目录加上粘滞位。
当一个用户将某一个目录加上粘滞位后,该目录的权限值的最后一位变为字符“t”。
此时另一个用户就算有该目录的可写权限,也无法删除该目录下的文件。
当一个目录被设置为粘滞位,则该目录下的文件只能由:
1)超级用户删除。
2)该目录的拥有者删除。3)该文件的拥有者删除。
注意: 虽然目录被加上了粘滞位,但如果用户有该目录的可写权限,则不影响其在该目录下创建文件
2、关于目录权限的总结
目录的可执行权限是表示你可否在目录下执行命令。
如果目录没有-x权限,则无法对目录执行任何命令,甚至无法cd 进入目, 即使目录仍然有-r 读权限(这个地方很容易犯错,认为有读权限就可以进入目录读取目录下的文件)
而如果目录具有-x权限,但没有-r权限,则用户可以执行命令,可以cd进入目录。但由于没有目录的读权限所以在目录下,即使可以执行ls命令,但仍然没有权限读出目录下的文档。
三、【基础工具】
3.1、【Linux软件包管理器 - yum】
在Linux下安装软件的方法大概有以下三种:
1)下载到程序的源代码,自行进行编译,得到可执行程序。
2)获取rpm安装包,通过rpm命令进行安装。(未解决软件的依赖关系)
3)通过yum进行安装软件。(常用)1、【yum的介绍】
yum是一个在Fedora、RedHat以及CentOS中的前端软件包管理器,能够从指定的服务器自动下载RPM包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软件包,无须繁琐地一次次下载、安装。注意:一个服务器同一时刻只允许一个yum进行安装,不能在同一时刻同时安装多个软件。
因为yum是从服务器上下载RPM包,所以在下载时必须联网,可以通过ping指令判断当前云服务器是否联网。
2、【查找软件包】
使用yum list指令,可以罗列出可供下载的全部软件。yum list
说明一下:
1)软件包名称:主版本号.次版本号.源程序发行号-软件包的发行号.主机平台.cpu架构。
2)"x86_64"后缀表示64位系统的安装包,"i686"后缀表示32位系统安装包,选择包时要和系统匹配。
3)"el7"表示操作系统发行版的版本,“el7"表示的是"centos7/redhat7”,“el6"表示"centos6/redhat6”。
4)最后一列表示的是“软件源”的名称,类似于“小米应用商店”,“华为应用商店”这样的概念。举个例子,这里我们以查找lrzsz为例:
lrzsz可以将Windows当中的文件上传到Linux当中,也可以将Linux当中的文件下载Windows当中,实现云服务器和本地机器之间进行信息互传。我们需要执行如下命令:
yum list | grep lrzsz
由于包的数量非常多,所以我们可以使用grep指令筛选出我们所关注的包,这里我们以lrzsz为例。
此时就只会显示与lrzsz相关的软件包,我们对其进行安装。
3、【安装软件】
指令:sudo yum install 软件名
这里我们以安装lrzsz为例,首先执行如下命令:
sudo yum install lrzsz
yum会自动找到都有哪些软件包需要下载,这时候敲“y”确认安装,当出现“complete”字样时,说明安装完成。
注意事项:
1)安装软件时由于需要向系统目录中写入内容,一般需要sudo或者切换到root账户下才能完成。
2)yum安装软件只能一个装完了再装另一个,正在使用yum安装一个软件的过程中,如果再尝试用yum安装另外一个软件,yum会报错。4、【如何实现本地机器和云服务器之间的文件互传?】
既然已经安装了lrzsz,这里就顺便说一下lrzsz如何使用。
指令:rz -E
通过该指令可选择需要从本地机器上传到云服务器的文件。
指令:
sz 文件名
该指令可将云服务器上的文件下载到本地机器的指定文件夹。
5、【卸载软件】
指令:sudo yum remove 软件名
如果要删除刚刚安装的lrzsz只需要执行如下命令:
sudo yum remove lrzsz
yum会自动卸载该软件,这时候敲“y”确认卸载,当出现“complete”字样时,说明卸载完成。
3.2、【Linux编辑器 - vim】
1、【vim的基本概念】
vim在我们做开发的时候,主要解决我们编写代码的问题,本质上就是一个多模式的文本编辑器。我们这里主要介绍vim最常用的三种模式:命令模式、插入模式、底行模式。
(1)命令模式(Normal mode)。
在命令模式下,我们可以控制屏幕光标的移动,字符、字或行的删除,复制粘贴,剪贴等操作。
(2)插入模式(Insert mode)。
只有在插入模式下才能进行文字输入,该模式是我们使用最频繁的编辑模式。
(3)底行模式(Command mode)。
在底行模式下,我们可以将文件保存或退出,也可以进行查找字符串等操作。在底行模式下我们还可以直接输入vim help-modes查看当前vim的所有模式。2、【vim下各模式的切换】
如果我们要使用vim编辑一个文件,无论是已经存在的还是不存在的,都可以使用以下命令,可以将其理解为windows中的记事本,用来编辑一些东西,也可以通过安装一些插件来实现相应功能。
指令:vim 文件名
进入vim后默认为命令模式(普通模式),要输入文字需切换到插入模式。【命令模式】切换至【插入模式】
1)输入「i」:在当前光标处进入插入模式。
2)输入「a」:在当前光标的后一位置进入插入模式。
3)输入「o」:在当前光标处新起一行进入插入模式。【命令模式】切换至【底行模式】
输入「Shift+;」即可,实际上就是输入「:」。
【插入模式】或【底行模式】切换至【命令模式】
插入模式或是底行模式切换至命令模式都是直接按一下「Esc」键即可。3、【vim命令模式各命令汇总】
【移动光标】
1)按「k」:光标上移。
2)按「j」:光标下移。
3)按「h」:光标左移。
4)按「l」:光标右移。
5)按「$」:移动到光标所在行的行尾。
6)按「^」:移动到光标所在行的行首。
7)按「gg」:移动到文本开始。
8)按「Shift+g」:移动到文本末尾。
9)按「n+Shift+g」:移动到第n行行首。
10)按「n+Enter」:当前光标向下移动n行。
11)按「w」:光标从左到右,从上到下的跳到下一个字的开头。
12)按「e」:光标从左到右,从上到下的跳到下一个字的结尾。
12)按「b」:光标从右到左,从下到上的跳到上一个字的开头。
【删除】
1)按「x」:删除光标所在位置的字符。
2)按「nx」:删除光标所在位置开始往后的n个字符。
3)按「X」:删除光标所在位置的前一个字符。
4)按「nX」:删除光标所在位置的前n个字符。
5)按「dd」:删除光标所在行。
6)按「ndd」:删除光标所在行开始往下的n行。
【复制粘贴】
1)按「yy」:复制光标所在行到缓冲区。
2)按「nyy」:复制光标所在行开始往下的n行到缓冲区。
3)按「yw」:将光标所在位置开始到字尾的字符复制到缓冲区。
4)按「nyw」:将光标所在位置开始往后的n个字复制到缓冲区。
5)按「p」:将已复制的内容在光标的下一行粘贴上。
6)按「np」:将已复制的内容在光标的下一行粘贴n次。
【剪切】
1)按「dd」:剪切光标所在行。
2)按「ndd」:剪切光标所在行开始往下的n行。
3)按「p」:将已剪切的内容在光标的下一行粘贴上。
4)按「np」:将已剪切的内容在光标的下一行粘贴n次。
【撤销】
1)按「u」:撤销。
2)按「Ctrl+r」:恢复刚刚的撤销。
【大小写切换】
1)按「~」:完成光标所在位置字符的大小写切换。
2)按「n~」:完成光标所在位置开始往后的n个字符的大小写切换。
【替换】
1)按「r」:替换光标所在位置的字符。
2)按「R」:替换光标所到位置的字符,直到按下「Esc」键为止。
【更改】
1)按「cw」:将光标所在位置开始到字尾的字符删除,并进入插入模式。
2)按「cnw」:将光标所在位置开始往后的n个字删除,并进入插入模式。
【翻页】
1)按「Ctrl+b」:上翻一页。
2)按「Ctrl+f」:下翻一页。
3)按「Ctrl+u」:上翻半页。
4)按「Ctrl+d」:下翻半页。4、【vim底行模式各命令汇总】
在使用底行模式之前,记住先按「Esc」键确定你已经处于命令模式,再按「:」即可进入底行模式。【行号设置】
1)「set nu」:显示行号。
2)「set nonu」:取消行号。
【保存退出】
1)「w」:保存文件。
2)「q」:退出vim,如果无法离开vim,可在「q」后面跟一个「!」表示强制退出。
3)「wq」:保存退出。
【分屏指令】
1)「vs 文件名」:实现多文件的编辑。
2)「Ctrl+w+w」:光标在多屏幕下进行切换。
【执行指令】
1)「!+指令」:在不退出vim的情况下,可以在指令前面加上「!」就可以执行Linux的指令,例如查看目录、编译当前代码等。3、【vim的简单配置】
1)在目录/etc/下面,有个名为vimrc的文件,这是系统中公共的配置文件,对所有用户都有效。
2)在每个用户的主目录/home/xxx下,都可以自己建立私有的配置文件,命名为“.vimrc”,这是该用户私有的配置文件,仅对该用户有效。例如,普通用户在自己的主目录下建立了“.vimrc”文件后,在文件当中输入set nu指令并保存,下一次打开vim的时候就会自动显示行号。
vim的配置比较复杂,某些vim配置还需要使用插件,建议不要自己一个个去配置。比较简单的方法是直接执行以下指令(想在哪个用户下让vim配置生效,就在哪个用户下执行该指令,不推荐直接在root下执行):
curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh
然后按照提示输入root密码:然后等待安装配置,最后手动执行source ~/.bashrc即可。
配置完成后,像什么自动补全、行号显示以及自动缩进什么的就都有了。
3.3【Linux编译器 —— gcc/g++】
1、【gcc/g++的作用】
gcc和g++分别是GNU的C和C++的编译器,gcc和g++在执行编译的时候一般有以下四个步骤:
1)预处理(头文件展开、去注释、宏替换、条件编译)。
2)编译(C代码翻译成汇编语言)。
3)汇编(汇编代码转为二进制目标代码)。
4)链接(将汇编过程产生的二进制代码进行链接)。2、【gcc/g++语法】
语法: gcc/g++ 选项 文件
常用选项:
1)-E 只进行预处理,这个不生成文件,你需要把他重定向到一个输出文件里面(否则将把预处理后的结果打印到屏幕上)。
2)-S 编译到汇编语言,不进行汇编和链接,即只进行预处理和编译。
3)-c 编译到目标代码
4)-o 将处理结果输出到指定文件,该选项后需紧跟输出文件名。
5)-static 此选项对生成的文件采用静态链接。
6)-g 生成调试信息(若不携带该选项则默认生成release版本)。
7)-shared 此选项将尽量使用动态库,生成文件较小。
8)-w 不生成任何警告信息。
9)Wall 生成所有警告信息。
10)-O0/-O1/-O2/-O3 编译器优化选项的四个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高。预处理
gcc -E test.c -o test.i
- 预处理功能主要包括头文件展开、去注释、宏替换、条件编译等。
- 预处理指令是以#开头的代码行。
- -E选项的作用是让gcc/g++在预处理结束后停止编译过程。
- -o选项是指目标文件,“xxx.i”文件为已经过预处理的原始程序。
编译gcc -S test.i -o test.s
- 在这个阶段中,gcc/g++首先检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,将代码翻译成汇编语言。
- 用户可以使用-S选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
- -o选项是指目标文件,“xxx.s”文件为已经过翻译的原始程序。
汇编
gcc -c test.s -o test.o
- 汇编阶段是把编译阶段生成的“xxx.s”文件转成目标文件。
- 使用-c选项就可以得到汇编代码转化为“xxx.o”的二进制目标代码了。
链接
gcc test.o -o test
- 在成功完成以上步骤之后,就进入了链接阶段。
- 链接的主要任务就是将生成的各个“xxx.o”文件进行链接,生成可执行文件。
- gcc/g++不带-E、-S、-c选项时,就默认生成预处理、编译、汇编、链接全过程后的文件。
- 若不用-o选项指定生成文件的文件名,则默认生成的可执行文件名为a.out。
注意: 链接后生成的也是二进制文件。
3、【静态库与动态库】
函数库一般分为静态库和动态库两种:
- 静态库是指编译链接时,把库文件的代码全部加入到可执行文件当中,因此生成的文件比较大,但在运行时也就不再需要库文件了,静态库一般以.a为后缀。
- 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件当中,而是在程序运行时由链接文件加载库,这样可以节省系统的开销,动态库一般以.so为后缀。
动态链接:
优点:省空间(磁盘的空间,内存的空间),bin体积小,加载速度快。
缺点:依赖动态库,程序可移植性较差。
静态链接:
优点:不依赖第三方库,程序的可移植性较高。
缺点:浪费空间。gcc和g++默认生成的二进制程序是动态链接的,我们可以使用file指令进行查看。
其次,我们还可以使用ldd指令查看动态链接的可执行文件所依赖的库。
图中的/lib64/libc.so.6就是当前云服务器当中的C标准库)。
虽然gcc和g++默认采用的是动态链接,但如果我们需要使用静态链接,带上-static选项即可。
gcc test.c -o test_s -static
此时生成的可执行文件就是静态链接的了。
我们可以查看源代码相同,但链接方式不同而生成的两个可执行程序test和test_s的大小。
这也证明了动态链接比较节省空间,而静态链接比较浪费空间。
3.4【Linux调试器 —— gdb】
1、【gdb使用须知】
程序发布方式:
- debug版本:程序本身会被加入更多的调试信息,以便于进行调试。
- release版本:不会添加任何调试信息,是不可调试的。
在Linux当中gcc/g++默认生成的可执行程序是release版本的,是不可被调试的。如果想生成debug版本,就需要在使用gcc/g++生成可执行程序时加上-g选项。
对同一份源代码分别生成其release版本和debug版本的可执行程序,并通过ll指令可以看到,debug版本发布的可执行程序的大小比release版本发布的可执行程序的大小要大一点,其原因就是以debug版本发布的可执行程序当中包含了更多的调试信息。
2、【gdb命令汇总】
这里通过一个例子进行汇总:
#include <stdio.h>// 求 0-n 的和 int AddToN(int n) {int res = 0;for (int i = 0; i <= n; i++){res += i;}return res; }int main() {int n, res;scanf("%d", &n);res = AddToN(n);printf("res: %d\n", res);return 0; }
gcc test.c -o test -g 生成 test.Debug文件,之后进行调试:
在使用gdb进行调试之前,最好确保系统中安装了了gdb,如果没有安装使用如下命令安装即可:
(1)【进入gdb】指令:
gdb -q 文件名 || gdb 文件名 //区别在于前者看起来更简洁
(2)【启动gdb】
- 「run/r」:运行代码(启动调试),要运行程序,直接输入run即可,程序会一直运行到断点处停下来,如果没有断点则直接运行结束。
- 「start」:启动程序并停在程序的入口处
- 「continue」:继续运行程序
(3)【显示】
- 「list」:列出当前文件的源代码,默认列出前10行 (list 可以简写为 l),如果想要从第一行开始打印,可以使用「list 1」命令,一次默认打印10行,如果要继续往后看,可以使用 list + 命令或者直接按回车,因为gdb会记住你上一次的操作,当你没有输入时按回车,gdb就重复你上一次的操作,这里就是list,会自动将后10行的内容显示出来
- 「list/ n」:显示从第n行开始的源代码,每次显示10行,若n未给出则默认从上次的位置往下显示.,这里gdb会以n为中心,显示出前5行和后4行代码,共计10行
- 「list/ 函数名」:显示该函数的源代码。
(4)【断点】
直接run会运行到程序退出不方便调试,为了利于调试和观察我们需要在程序的某些地方加上断点,好让程序在一个便于观察的地方停下来,使用的命令如下linenum是要设置断点的行号
- 「break/b n」:在第n行设置断点,设置好断点后可以查看已经设置的断点。
- 「info breakpoint/b」:查看已打断点信息。
- 「delete/d 编号」:删除指定编号的断点,在打断点时,gdb会默认给每一个断点一个编号,因此在删除断点的时候,只能根据编号删除。
- 「disable 编号」:禁用指定编号的断点,关闭断点不等于删除断点,它跟类似于将这个断点暂时禁用,以后有需要可以随时打开它,所以在info b时还能够看到这个断点存在的信息,之不过其Enb字段由 y(yes) 变成了 n(no)
- 「enable 编号」:启用指定编号的断点。
- 「break/b 函数名」:在某函数体内第一行设置断点。
(5)【调试】
在完成了断点的设置后,就可以让程序停在断点处,然后一步一步调试来观察程序,这里可以分为两种方式:
- 「next/n」:逐过程调试,逐过程调试会执行下一行,当遇到函数调用时,会一次性执行完该函数,也就是说不会进到函数体内部。
- 「step/s」:逐语句调试,单步调试时同样会执行下一行,当遇到函数调用时,会进入改函数体内部,其命令为。
- 「until 行号」:跳转至指定行,在调试程序时,有时候需要让程序执行到指定的代码行之前或之后停止,可以使用gdb的until命令。
- 「backtrace (简写为 bt)」:在单步调试时,可以从一个函数体内部进到另一个函数体内部,此时我们所处的栈帧位置也发生了变化,可以查看堆栈信息,查看各级函数调用及参数。
- 「finish」:执行完当前正在调用的函数后停下来(不能是主函数),当进入到另一个函数体之后,可以使用该命令跳出这个函数体回到调用的地方。
- 「print/p 变量」:打印变量的值。,首先在gdb中启动程序并进入调试模式,接着在程序执行到需要查看变量的位置时,可以使用break或b命令在该位置设置断点,然后使用run或r命令运行程序,程序将在断点处停止,最后使用print或p命令查看变量的值。
- 「print/p 表达式」:打印表达式的值,通过表达式可以修改变量的值。
- 「display 变量」:将变量加入常显示(每次停下来都显示它的值),在调试程序时,经常需要反复查看某个变量的值,而不希望每次都输入一遍print命令。这时可以使用gdb的display命令,该命令会在每次程序停止时自动显示指定变量的值 。每指定一个变量,这个变量都会带上一个编号,如果要关闭某个变量的长显示,使用下面的命令undisplay
- 「undisplay 编号」:取消指定编号变量的常显示。
- 「display &变量」:将变量的地址加入常显示。
- 「info/i locals」:查看当前栈帧当中局部变量的值。
- 「continue/c」:运行到下一个断点处,首先在gdb中启动程序并进入调试模式,接着使用break或b命令设置(多个)断点,然后使用run或r命令运行程序,程序将在第一个断点处停止,在停止时,可以使用print命令查看变量的值,使用step或s命令逐行执行程序,使用next或n命令跳过函数调用等,最后如果想要继续执行程序,可以使用c命令
- 「set var 变量=x」:修改变量的值为x,这里将 i 设为10, 下一步不满足循环条件,直接跳出循环
(6)【退出gdb】
- 「quit/q」:退出gdb,也可以ctrl + d 或 输入 quit 后按回车
3.5【Linux项目自动化构建工具 —— make/Makefile】
1、【make/Makefile的重要性】
- 会不会写Makefile,从侧面说明了一个人是否具备完成大型工程的能力。
- 一个工程的源文件不计其数,按照其类型、功能、模块分别放在若干个目录当中,Makefile定义了一系列的规则来指定:哪些文件需要先编译,哪些文件需要后编译,甚至于进行更复杂的功能操作。
- Makefile带来的好处就是“自动化编译”,一旦写好,只需一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
- mak是一个命令工具,是一个解释Makefile当中指令的命令工具,一般来说,大多数的IDE都有这个命令,例如:Delphi的make,VisualC++的nmake,Linux下GNU的make。可见,Makefile都成为了一种在工程方面的编译方法。
- make是一条命令,Makefile是一个文件,两个搭配使用,完成项目自动化构建。
2、【依赖关系和依赖方法】
在使用make/Makefile前我们首先应该理解各个文件之间的依赖关系以及它们之间的依赖方法。
依赖关系: 文件A的变更会影响到文件B,那么就称文件B依赖于文件A。
- 例如,test.o文件是由test.c文件通过预处理、编译以及汇编之后生成的文件,所以test.c文件的改变会影响test.o,所以说test.o文件依赖于test.c文件。
依赖方法: 如果文件B依赖于文件A,那么通过文件A得到文件B的方法,就是文件B依赖于文件A的依赖方法。
- 例如,test.o依赖于test.c,而test.c通过gcc -c test.c -o test.o指令就可以得到test.o,那么test.o依赖于test.c的依赖方法就是gcc -c test.c -o test.o。
3、【多文件编译】
当你的工程当中有多个源文件的时候,应该如何进行编译生成可执行程序呢?
首先,常用的一种方法是,我们可以直接使用gcc指令对多个源文件进行编译,进而生成可执行程序。
但进行多文件编译的时候一般不使用源文件直接生成可执行程序,而是先用每个源文件各自生成自己的二进制文件,然后再将这些二进制文件通过链接生成可执行程序。
原因:
- 若是直接使用源文件生成可执行程序,那么其中一个源文件进行了修改,再生成可执行程序的时候就需要将所以的源文件重新进行编译链接。
- 而若是先用每个源文件各自生成自己的二进制文件,那么其中一个源文件进行了修改,就只需重新编译生成该源文件的二进制文件,然后再将这些二进制文件通过链接生成可执行程序即可。
注意: 编译链接的时候不需要加上头文件,因为编译器通过源文件的内容可以知道所需的头文件名字,而通过头文件的包含方式(“尖括号”包含和“双引号”包含),编译器可以知道应该从何处去寻找所需头文件。
但是随着源文件个数的增加,我们每次重新生成可执行程序时,所需输入的gcc指令的长度与个数也会随之增加。这时我们就需要使用make和Makefile了,这将大大减少我们的工作量。
步骤一: 在源文件所在目录下创建一个名为Makefile/makefile的文件
步骤二: 编写Makefile文件。
Makefile文件最简单的编写格式是,先写出文件的依赖关系,然后写出这些文件之间的依赖方法,依次写下去。
编写完毕Makefile文件后保存退出,然后在命令行当中执行make指令便可以生成可执行程序,以及该过程产生的中间产物。
我们发现图中在生成mytest过程中,蓝色框部分会显示生成过程中需要执行的指令,并且我们要重复输入依赖文件(main.o,test.o,main.c,test.c)那有没有办法简化Makefile文件的编写并将过程中打印的命令(也就是依赖方法)隐藏起来呢?
Makefile文件的简写方式:
- $@:表示依赖关系中的目标文件(冒号左侧)。
- $^:表示依赖关系中的依赖文件列表(冒号右侧全部)。
- $<:表示依赖关系中的第一个依赖文件(冒号右侧第一个)。
例如以上Makefile文件可以简写为:
说明: gcc/g++携带-c选项时,若不指定输出文件的文件名,则默认输出文件名为xxx.o,所以这里也可以不用指定输出文件名。
Makefile文件隐藏依赖方法的方式
- 只需要在每个依赖方法前面加上符号“ @ ”即可对其隐藏
例如以上Makefile文件可写为:
效果:
4、【make原理】
- make会在当前目录下找名字为“Makefile”或“makefile”的文件。
- 如果找到,它会找文件当中的第一个目标文件,在上面的例子中,它会找到mytest这个文件,并把这个文件作为最终的目标文件
- 如果mytest文件不存在,或是mytest所依赖的后面的test.o文件和main.o文件的文件修改时间比mytest文件新,那么它就会执行后面的依赖方法来生成mytest文件。
- 如果mytest所依赖的test.o文件不存在,那么make会在Makefile文件中寻找目标为test.o文件的依赖关系,如果找到则再根据其依赖方法生成test.o文件(类似于堆栈的程)。
- 当然,你的test.c文件和main.c文件是存在的,于是make会生成test.o文件和main.o文件,然后再用test.o文件和main.o文件生成最终的mytest文件。
- make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
- 在寻找的过程中,如果出现错误,例如最后被依赖的文件找不到,那么make就会直接退出,并报错。
5、【项目清理】
在我们每次重新生成可执行程序前,都应该将上一次生成可执行程序时生成的一系列文件进行清理,但是如果我们每次都手动执行一系列指令进行清理工作的话,未免有些麻烦,因为每次清理时执行的都是相同的清理指令,这时我们可以将项目清理的指令也加入到Makefile文件当中。
像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,但我们可以显示要make执行。
注: 一般将这种clean的目标文件设置为伪目标,用.PHONY修饰,伪目标的特性是:总是被执行。
6、【伪目标———.PHONY】
什么是伪目标:
我们可以发现,上面的Makefile文件中,在依赖关系上面有一个.PHONY:xxx这个是什么呢?
.PHONY:mytest //或者 .PHONY:clean
这其实是一个伪目标修饰符,Makefile中将.PHONY放在一个目标文件前就是指明这个目标文件是伪文件目标。可以起到,反复执行效果。
我们可能会有疑问,为什么要这两条语句能够反复执行呢?或者说不反复执行是否可以呢?
现在让我们看一下把伪目标去除以后会带来什么后果。
我们分析“mytest is up to date”,这是什么意思?
意思是从上一次编译完成,到现在,依赖对象没有发生过改动,不需要重新编译。我们可能会这样想不就是再重新编译嘛,使用 “ make clean ”删除以后再重新编译一下不就可以了?又不用多长时间。
其实,这种想法是错误的,现在我们写的文件中的代码非常少,少则几行,多则几十行,上百行,运行起来也就是不到一秒的时间。但是,如果是在大公司,写的大型程序,代码量都是几万行起步,编译起来就浪费时间了,几十分钟或者几个小时都有可能,我们知道Linux是一个极其重视效率的平台,既然已经编译过了,就不要做无用功了。
但是,gcc怎么知道这个文件通过makefile已经被编译过了呢?还别说,这是一个值得探索的问题!!
实际上我们的每个文件都对应有三个时间,这三个时间分别为Access,Modify,Change,接下来,我先说一下这三个时间分别指的是什么时间。
Access:
这个时间为:最近一次访问这个文件的时间,即访问了这个文件并未进行任何其他的操作,access就会发生了变化。
之前,在老的操作系统内核中,只要是我们访问了这个文件,Access时间就会发生变化。意思是如果我们一连多次的访问这个文件,Access时间就会连续发生变化,也就是Accesss时间更改的频率太高了。
对于一个文件来说,是修改这个文件的次数多,还是访问这个文件的次数多呢?
实际上访问这个文件的次数更多,因为修改文件一定会访问这个文件,但是访问这个文件并不代表修改这个文件;我们读取一个文件的内容,属于访问这个文件,但并未涉及到修改文件的内容。
在运行程序时,会涉及到反复访问大量的文件,每访问一下这个文件,就要更改Access时间。时间这个概念对文件来说也是属性,文件在磁盘中放着,修改文件的属性就等于大量的IO操作。这对操作系统来说,无疑是一个巨大的负担。
另外,有多少人会关心读取这个文件内容的时间呢?
相对于而言,我们更关心的是这个文件内容和权限被修改的时间,所以,只要文件的内容和权限发生修改,对应的时间就会立即发生变化。随着操作系统的更新换代,新的操作系统对这方面做了相应的优化,突出体现在Access修改的频率上。由原来的只要一访问就发生变化,变为现在的等到访问一定的次数或者时间,Access时间再发生一次修改。
Modify:
Modify时间为文件内容更改的时间,一般而言Modify发生变化Change时间也会跟着发生变化。
Change:
这个时间是指文件的属性发生变化的时间,因为文件的属性不止包括文件的权限,还有文件的大小,甚至包括文件的三个时间(access,modify,change)都是文件的属性,所以才会出现modify改变change也会跟着改变的现象。
介绍完了这三个时间,我们想想gcc怎么知道这个文件通过makefile已经被编译过了呢?
其实就是通过比较时间,这个现象的背后就是gcc比较时间,更准确的来说:就是比较了源文件和可执行程序的Modify时间。
我们先达成这样一个共识:在初次编译时,源文件的Modify时间一定比编译后生成的可执行程序的Modify时间早,这就好比父亲和儿子,先有父亲,再有儿子嘛,源文件就好比作父亲,可执行程序就是源文件的儿子。
在初次编译形成可执行程序后,源文件内容并未发生修改的情况下,Modify时间也未发生改变,依旧早于可执行程序的时间,这是再次输入make命令的话,gcc就通过比较时间得知:源文件没有发生修改,没有必要二次编译。但如果源文件内容发生修改,Modify时间发生修改,由原来的早于可执行程序的时间变得晚于可执行程序的时间,这时,gcc就会得知源文件发生改变,有二次编译的需要。
口说无凭,我们来通过一个例子来验证一下:
相信大家在刚学Linux时,学的前几个命令中,肯定touch这个命令,我们常用的touch命令为:在没有这个文件的情况下,创建文件文件;如果这个文件已经存在,那就更新这个文件为最新时间。
而当我们加上伪目标.PHONY时就可以让它修饰的伪目标不遵循这样的规则,所以“总能运行。顺便一提clean被.PHONY修饰了,那么它就可以反复执行它的依赖方法。所以只要你一直使用【make clean】,它便会反复地执行【rm -f test.o test.s test.i】。
❗️注意:clean即使不被伪目标.PHNOY修饰,一样可以反复执行。因为它的依赖对象为空,当我们需要生成这个【clean】目标文件的时候就不需一些文件必须要存在,因此就可以一直[clean]。总结一下:
🔸 .PHONY修饰后的目标文件,它可以被反复执行,每次都会执行它的依赖方法。
🔸 .PHONY修饰的一定能被反复执行,但是能被反复执行的不一定被.PHONY修饰。
总结
本篇博客到这里就结束了,感谢观看!
........................................................................................若是无缘再见,白堤柳帘垂泪好几遍
————《断桥残雪》