前言:本节内容还是linux进程, 主要讲解里面的环境变量——我们首先要知道的就是环境变量其实就是操作系统维护的一组kv值, 环境变量是系统提供的一组 变量名=变量值 形式的变量。不同的环境变量之间具有不同的用途, 并且具有全局属性。 本篇内容主要分为两个大板块——第一个大板块主要是具体举几个环境变量的例子、第二个大板块主要是讲解环境变量的概念与知识点。
ps:本节内容适合了解进程PCB的友友进行观看
目录
PATH
HOME
SHELL
getnev
什么是环境变量
命令行参数
命令行参数里的环境变量
本地变量与内建命令
PATH
我们以前说过, 指令的本质就是一个进程。 而且, 我们以前也说过, 我们自己写的程序必须加上./, 而指令不需要./——请问这是为什么呢?
这是因为, 系统当中, 针对我们指令的搜索, 我们的系统会为它提供一个环境变量——PATH, 我们可以使用echo $PATH打印。 (注意, 这里的$符号不能去掉, 因为不加$就意味着echo会将PATH当作一个字符串进行打印)。这个时候, 打印的内容里面包含一串串路径, 并且这些路径用“:”进行分割, 这里面的每一条路径都对应着我们的指令进行搜索的路径。如下图;
也就是说, 我们的系统怎么知道我们都是用的指令在哪里寻找呢? 就是因为PATH, 当我们调用某个指令的时候, 系统会进入PATH显示的每一个路径里面一个一个进行查找。 没找到就继续下一个, 没找到就继续下一个。 而如果某个程序被放到这些路径下面, 那么系统默认就会找到(就比如我们使用的指令 就是放到这里面的), 而如果某个程序没有被放到这些路径下面, 系统默认就找不到, 就会返回commend no found。 而如果想要运行这个程序就要输入这个程序的相对路径(就比如我们运行我们自己写的程序, 就是使用./程序, 这就是相对路径)
想要在环境变量的路径中添加路径怎么做呢?
就是使用PATH=$PATH:新添路径——其实这里的等号后面的路径就相当于PATH的新路径, 如果等号后面的PATH不要了, 只要添加新的路径, 那么新路径就不是现在这样既有PATH原本的路径, 又有新添路径了, 而是只有一个新路径。
注意:此时所修改的环境变量是内存级的环境变量, 想要变回来只需要重新启动一下xshell。
综上:PATH:linux系统指令的搜索路径。
windows下面的同样有环境变量, 事实上, 当我们使用python、java这些解释型语言的时候, 都需要配置环境变量, 而windows下面的环境变量的修改方法如下:
HOME
如图我们登陆了普通用户, 并且可以使用pwd查看当前当前目录, 同时我们使用echo $HOME;
也可以看到当前的目录。
一个用户默认登录的进入的是用户的家目录, 那么为什么会这样呢?
因为一个用户在登录时, 系统默认识别$HOME命令, 查看当前用户的家目录在哪里, 当我们登录的时候, 就默认执行了cd $HOME指令进入了用户的家目录下。
SHELL
$SHELL可以查看当前命令行用的是什么, 即, 当前shell用的是哪一个可执行程序。
其他的, 还有很多的环境变量, env指令可以查看当前自己的进程和bash进程从系统继承下来的所有环境变量
getnev
通过c语言, c++我们也可以通过getenv函数获取某个环境变量。
那么环境变量在权限中的应用?
如何就是通过getnev获取当前用户, 获取用户后和root账户做对比, 根据if else语句判断是不是root账户, 并在if或者else代码块里面执行对应的动作。 通过这个, 我们就能更好的理解权限问题——对于一个文件或者某个指令的权限判断, 是拥有者, 还是所属组, 亦或者是其他人。
什么是环境变量
关于什么是环境变量, 在前言中博主已经提到。 但是这里还要补充一点:环境变量其实是有一定的用途的, 比如当前用户的家目录在哪, 当前的用户是谁,指令的搜索路径是哪里, shell的可执行程序是哪个等等。
而要具体的理解环境变量, 需要先理解命令行参数。 下面我们先来理解一下命令行参数的概念。
命令行参数
对于main函数来说, 其实它是可以有参数的, 如下:
int main(int argc, char* argv[]){}
这里面的argv是一个指针数组, argc代表argv的数组元素个数。 argv里面的元素个数由argc决定。
这里可以使用一个循环, 将argv里面的元素全部打印出来:
然后我们运行这个程序, 或者运行这个程序的时候加上一些选项, 就能看到打印出来的结果分别是这样的:
ps:这里有一个题外的问题——main函数是第一个被系统调用的函数吗?——不重要, 友友们自行跳过即可。
为了回答这个问题, 我们就要思考如果main函数是第一个被调用的函数, 那么它是不是就势必不能被传参? 而我们的main函数是有参数的; 而且我们从c语言也说过第一个被系统调用的函数其实是叫做starUp的函数, 这个函数真正的无参。
回到上面的图中, 想要知道为什么我们输入的选项越多, 打印的内容就越多,就涉及到了bash的知识点——其实我们在bash上面输入任何指令, 本质上都是在输入字符串。 就比如我们上面输入./process-7-11.exe -a -b -c, 这个其实就是输入了一个"./process-7-11.exe -a -b -c"字符串而已。 而bash在进行命令行解释的时候会将这些字符串按照空格进行分割, 这些字符串分成一个新的字符串一起传给main函数作为形参, 也就产生了下面这种效果, 然后for循环再依次打印。
bash命令行为什么要这么做呢?
我们来看一个程序, 就能知道bash为什么会这么做了:
我们运行这个程序, 就可以得到如下结果:
对比我们使用ls加选项的时候:
对比上面的两个选项, 我们是不是就能发现, 我们常用的指令, 为什么要打上选项?是不是就是因为指令加选项 本质上都是运行一个程序, 只是根据不同的选项, 走到了不同的else if里面, 然后执行了对应的功能模块。 这就是指令选项的本质, 这就是命令行参数。——也就是说, 命令行参数的一个重要功能就是为指令、工具等提供命令行选项的支持!!!
而只要需要选项, 那么就要用到命令行参数, 所以要有命令行参数——即命令行参数的意义就是, 为了让我们使用程序的时候根据不同的选项, 执行不同的操作。
我们使用ls --help就能查看ls的所有选项
也就是相当于我们自己编写的程序, 加上了一个--help选项
最后要补充一点的就是对于argv来说, 它的最后一个元素默认是NULL,也就是说我们在上面程序模拟的时候, 其实是不需要使用if判断, 如下图:
现在来看一个叫做向量表的概念。 这个向量表其实就是上面的argv, 类似下图:
知道了这些东西以后, 那么, 这里就可以回归正题——>环境变量。
命令行参数里的环境变量
我们学习命令行参数不是无理头的, 命令行参数和环境变量具有一定的关系,在哪呢?——请问, 有没有人规定命令行参数只有argc, argv? 没有, 也就是说, main的参数可能还有别的。 那么, main函数还真有一个别的参数, 这个参数就叫做int* env[], 这个env[]里面保存的是main当前进程的所有环境变量。 而且, 这个env[]的结构这里也可以提一下——就是向量表。 也就是说, main函数的参数里, 有两张核心向量表, 一张用来保存选项, 一张用来保存环境变量!!!
下面是实验:
运行后, 就可以发现和我们使用env指令一样
综上, 我们以后再看待运行程序的时候, 就可以这么考虑——》一定是有一个startUp直接或者间接调用了main函数, 并将两张向量表, 一个选项表, 一个环境变量表传给了main函数。 然后main函数再跑起来。
那么这里就有了结论, 首先我们知道我们自己运行的这个程序是一个子进程, 而bash是一个父进程。 但是我们在bash下使用env的时候和我们在子进程下打印的环境变量是一样的。 ——这个结论就是——我们所运行的程序, 都是子进程, bash本身在启动的时候,会从操作系统的配置文件中读取环境变量信息, bash再在自己的上下文中构建一个环境变量表。 当我们的子进程需要的话, 就可以将环境变量信息给子进程一份。也就是说, 子进程会继承父进程交给他的所有环境变量!!!这就是为什么环境变量具有全局属性——因为他会在子进程中一个一个地完整的继承下去。
但是这里有一个问题, 就是环境变量也是数据, 我们知道进程之间是独立的。 而如果一个子进程想要修改环境变量, 那么就一定不能够影响父进程的环境变量(写时拷贝)
那么,我们如何验证这些环境变量可以一个一个的传承下去呢?
在这里我们可以自己添加一个环境变量——这里的变量有两种, 一种是直接变量名=变量, 这个是本地变量。 另一种是export变量名=变量, 这个是环境变量。 我们需要使用后者。
这样, 就可以看到我们的环境变量了!但是, 这个环境变量是我们在bash上面创建的, 那么要验证环境变量的全局性, 就要看一下我们的子进程中有没有这个环境变量。那么有没有呢?答案是有的, 如下图:
取消环境变量使用unset环境变量:
本地变量与内建命令
set指令可以查看当前shell中所有变量, 包括本地变量和环境变量
本地变量不会被继承, 只会在bash中被使用。 创建本地变量的方式上面已经提到, 就是变量名=变量
我们如何验证本地变量不会被继承呢? 这里, 我们来写一个程序:
这个程序可以打印环境变量
我们可以使用set, 查看当前到当前具有这个本地变量, 但是我们使用子进程进行查看的时候, 查看不到!
然后, 当我们把本地变量改成环境变量的时候, 就能查看到了:
以上, 就说明了本地变量不能被继承.
那么, 看第二个问题:
既然MY_VALUE是一个本地变量, 那么它就不能被子进程继承, 我们也知道, bash里面的指令, 其实就是一个个bash的子进程, 问题是, 为什么图中的echo可以打印出MY_VALUE呢?
那么, 没有错, echo不是一个bash的子进程。 这里就可以纠正之前的一个认知错误, 那就是——bash里面的指令, 不全都是bash的子进程。
命令其实分为两种:一种是常规指令, 通过创建子进程完成。
一种是内建命令, 这种命令bash不回去创建子进程, 而是自己亲自执行(就相当于我们自己写的程序, 但是没有fork子进程, 或者说bash调用了自己写的或者系统提供的函数。)
我们可以通过cd来理解这个内建命令的概念:这里我们自己实现一个cd命令, 这里需要用到系统调用接口——chdir(const char* path)
原因就是因为我们自己写的程序是bash创建的子进程。 而cd是bash自己调用的本身代码块。 两者本质不一样, 但是我们同样可以看到我们自己写的程序的工作目录——就是proc进程查看里面的工作目录。 代码如下, 两次睡眠是为了我们进行更好的观察。
如图是刚刚运行程序, 这个时候工作路径还没有发生变化:
下图就是工作路径没有发生变化的时候子进程工作路径:
工作路径变化!说明子进程工作路径变化只改变自己, 而cd是是父进程, 它改变工作路径, 就意味着我们的命令路径变了!!!
在系统里面, 有一个变量, 可以让我们不适用main参数就打印全部的环境变量——environ, 使用如下:
打印结果如下:
以上, 就是本节全部内容, 下面是本节笔记: