🧑作者: @情话0.0
专栏:《Linux从入门到放弃》
个人简介:一名双非编程菜鸟,在这里分享自己的编程学习笔记,欢迎大家的指正与点赞,谢谢!
文章目录
前言
提到环境变量,大家应该都不会陌生,不管学习Java还是Phython都需要去安装环境变量,尤其是我自己在做深度学习时配置环境变量简直了,那叫一个惨,真的是莫名其妙不知道是哪里出的错,总之就是很难!那么今天的目的就是解释一下什么是环境变量。
一、环境变量是什么?
1.1 概念
环境变量是在操作系统当中由系统在开机之后帮我们维护的一些系统运行时的动态参数。
1.2 从Linux角度认识环境变量
环境变量因为所针对的场景不同,应用的软件不同而会有不同的种类。
- 我们在Linux中写的代码在编译之后形成的可执行程序为什么运行的时候要加上
./
呢? - 可执行程序它是工具吗?是指令吗?比如我们可以执行指令
file /user/bin/ls
其实发现我们平时用的指令本质就是拿C写的可执行程序。当然我们自己写的可执行程序也可以通过这样的方式来查看。
那么这两种有什么区别呢? 答案是没有任何区别,只不过那些指令被纳入到了Linux基本指令的范畴中了。
- 我们可以从这样的一个角度出发,为什么系统的指令(可执行程序)不用加
./
,而运行我自己写的非得加上./
呢?反过来说,我也想让我的程序不用加上./
就可以运行该怎么做呢?
我们自己运行程序的方式其实是采用相对路径的方式来完成的,当然绝对路径的方式也是可以的。而之所以默认的指令可以不加
./
是因为在系统中存在一个环境变量,可以通过这个环境变量帮助我们在系统中特定路径下去搜索对应的命令。而这个环境变量是PATH,可以通过echo $PATH
查看变量内容。
//我自己机器对应的环境变量
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/zhuxu/.local/bin:/home/zhuxu/bin
系统的指令在执行时会在这个环境变量中一个个查找,找到了就直接执行,而我们对应的可执行程序对应的路径肯定不在里面,因此在执行时就得加上路径。
通过上图可以看到ls
命令在/user/bin
目录下,而这个就在PATH环境变量中(包含多个路径,以冒号作为分隔符),但是自己的可执行程序并不在其中。那我们将这个路径添加到环境变量中应该就可以直接执行了吧。
可以通过上面这条指令给环境变量中添加那条路径,再查看环境变量明显发现多了一条路径,而这就是我们自己写的可执行程序对应的路径。这样就可以不用加./
而直接运行程序了。
- 除了上面的做法,那我们还可以直接将可执行程序添加到环境变量中的系统默认路径下——而这种方式就相当于Linux下的软件安装。当然不要了的话可以直接删除——卸载。
1.3 查看环境变量
env //很简单,这个命令就可以查看所有的环境变量
常见环境变量:
PATH : 指定命令的搜索路径
HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
SHELL : 当前Shell,它的值通常是/bin/bash。
1.4 获取环境变量
我们一般写main函数时都是不写参数的,但是人家其实是有参数的,而且还有三个参数,分别是:int argc、char *argv[]、char *envp[]
,前两个大家应该还挺熟悉,第三个参数被称为环境变量表。
这个环境变量表是一个指针数组,每一个位置存放着地址数据,基本上指向一个个字符串,最后必须以NULL结尾。
1.4.1 通过命令行第三个参数
#include <stdio.h>
int main(int argc,char *argv[],char *envp[])
{
int i=0;
for(;envp[i];++i)
{
printf("envp[%d]:%s\n",i,envp[i]);
}
return 0;
}
1.4.2 通过第三方变量environ
environ是一个二级指针,和命令行的第三个参数是一样的,都指向那张环境变量表。
libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时 要用extern声明。
#include <stdio.h>
#include <unistd.h>
int main()
{
extern char **environ;
int i=0;
for(;environ[i];++i)
{
printf("environ[%d]:%s\n",i,environ[i]);
}
return 0;
}
到这里我们就能明白在进程内部本身就有环境变量。然后呢?有什么用呢?我们是可以拿到一张环境变量表,难道我们要用某个环境变量的时候还得从这个表一个个去查找吗?那岂不是太挫了。
1.4.3 通过函数获取
//通过环境变量命来获取对应的环境变量
char *getenv(const char *name);
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *path=getenv("PATH");
if(path==NULL) perror("getenv");
else printf("PATH:%s\n",path);
return 0;
}
- 当然可以写一个获取当前路径的方法,那最终出来的效果是和系统本身指令
pwd
是一样的。 - 如果你想写一个只允许你自己执行的进程,那么可以获取USER这个环境变量,只要它的用户和你设置的用户名不同那就不能够执行。
1.5 环境变量概念再总结
- 环境变量本质就是一张内存级的一张表,这张表由用户在登陆系统的时候,进行给特定用户形成属于自己的环境变量表。
- 环境变量中的每一个都有自己的用途:有的是进行身份认证的,有的是进行确认当前路径的,有的是进行路径查找的,而有的是进行动态查找的等等。
- 每一个环境变量都有自己特定的应用场景。
- 每一个元素都是K-V类型的,不同名字对应不同的内容。
1.6 环境变量的数据从哪来?
它是一张内存级的表,那么内存中的数据在没有加载到内存之前是从哪来的呢?都是从系统的相关配置文件中读取进来的。
在家目录下,有这么两个文件: .bash_profile
和.bashrc
,它们本质都是 shell 脚本,每次重新登陆的时候,系统都会重新读取配置文件,将配置文件中的脚本一执行从而形成对应的环境变量。
我们在执行命令时都是bash去执行的,除了这个,它也支持命令行式的定义变量export myval=100
,这就相当于在bash内部定义了myval和100的字符串。
每当我们登陆了系统之后,操作系统就会创建shell进程,它的功能是读取命令和命令行的。当我们在命令行中设置了刚才那个变量为100,那么shell就会将该命令以字符串的形式读入其中,并且在shell内部会维护一张环境变量表,然后通过一个指针指向那个字符串,也就相当于把这个命令导进了环境变量中使之成为环境变量中的一员。要知道,所有的命令都是shell的子进程,所以当执行命令的时候shell就会将自己的表传给它的子进程。
这也间接说明了环境变量具有全局属性,它是可以被相关子进程继承下去的。
那怎么证明呢?
- 先将该命令导入环境变量中:
export hello="world"
- 查看环境变量是否导入成功。
- 编写代码通过函数调用的方式看是否能获取到刚设置的环境变量。
int main()
{
char *path=getenv("hello");
if(path==NULL)
perror("getenv");
else printf("%s\n",path);
return 0;
}
注:
命令行式的导入环境变量不加export
的话同样也是可以通过命令行的方法将设置的对应变量打印出来,但是函数调用的方法是不行的,为什么呢?原因就是这种方法定义出来的变量属于本地变量,只在shell内部有效,不能被子进程继承。
二、main函数的参数列表
2.1 参数介绍
int main(int argc,char *argv[])
argv:一张表,指针数组,
argc:指针数组元素的个数。
int main(int argc,char *argv[])
{
int i=0;
for(;i<argc;++i)
{
printf("argv[%d]->%s\n",i,argv[i]);
}
return 0;
}
- 当我们在命令行中这样输入,在shell看来他就是一个字符串,那就以空格为分隔符拆成一个个子串,
./myproc
为可执行程序,后面的一个个子串为参数选项。而那张参数表分别指向这一个个子串,最后NULL结尾。 - 那谁制作的这张表呢?当然是bash,也就是在执行命令的时候就将这张表做好了,而我们写的这个进程(子进程)使用这张表。
- bash(父进程)创建出来了一个个参数,然后子进程使用。
2.2 命令行参数的含义
可以根据命令行的参数选项不同而表现出不同的功能。
看下面的代码:
void Usage(const char* name)
{
printf("\nUsage: %s -[a|b|c]\n\n",name);
exit(0);
}
int main(int argc,char *argv[])
{
if(argc!=2) Usage(argv[0]);
if(strcmp(argv[1],"-a")==0) printf("打印当前目录下的文件名\n");
else if(strcmp(argv[1],"-b")==0) printf("打印当前目录下的文件的详细信息\n");
else if(strcmp(argv[1],"-c")==0) printf("打印当前目录下的文件名(包含详细信息)\n");
else printf("其他功能,待开发\n");
}
代码功能:实现了一个必须按照正确命令行参数规范输入而执行打印效果的demo。
当命令行参数个数为两个时才进行打印,若不是,就会进入Usage函数,这个函数功能就是告诉你应该怎样去正确的输入命令行。
从上图可以看出,当没有按照规范输入就会给出应该输入的规范模板,当正确输入时就会根据参数选项的不同而表现出不同的功能。这也就和
ls
命令和不同的选项而表现出不同的功能输出。
三、进程优先级
3.1 概念
优先级指的是CPU资源分配的先后顺序。
当然优先级高的进程首先获取到CPU资源并执行,就可以把那些不重要的进程安排在其他位置,让优先级高的进程先运行,这样可以大大改善系统的整体性能。
3.2 为什么有优先级呢?
举个简单例子,在学校里,每到中午的时候我们都要去食堂买饭,往往伴随着排队买饭的现象,原因就是卖饭窗口少,学生人数多。而优先级的出现就在于CPU资源有限。
正是因为CPU资源的有限,导致了众多进程需要竞争获取资源,必定会有先后顺序,进而会有优先级的出现让进程使用CPU资源有一定的先后顺序。
3.3 查看系统进程优先级 (ps -l
)
PRI :代表这个进程可被执行的优先级,其值越小越早被执行
NI :该进程的nice值,修正数据
每个进程都有自己的PRI,而这个数字则代表着自己的执行优先级,也就是该程序被CPU执行的先后顺序,此值越小进程的优先级别越高,NI表示修正数据,这个东西是Linux操作系统独有的,在其它操作系统中是没有的。
当然,一个进程最终的优先级不只是仅仅看PRI的大小,还有需要看Ni的值。PRI(new)=PRI(old)+NI
NI值为负值的时候,那么该程序将会优先级数值将变小,即其优先级会变高,则其越快被执行。所以,在Linux下调整进程优先级就是调整进程NI值,NI其取值范围是-20至19,一共40个级别。
3.4 更改进程优先级
用top命令更改已存在进程的nice:top
进入top后按“r”–>输入进程PID–>输入nice值
当然除了这种还有其它方式,具体是那种在这里不多介绍,毕竟这个点不是很重要。有兴趣可以自己了解一下。
总结
此篇博客主要是对操作系统中的环境变量以及参数列表,进程优先级做了陈述,重点还是放在环境变量的理解上。
文章评论