目录
- 环境变量
- 1.基本概念
- 2.常见环境变量
- 3.我们写的程序和命令行指令有什么区别?
- 4.自己的程序为什么要用 ./ 执行,而命令行指令可以直接执行?
- 5.如何追加环境变量?
- 6.Linux如何查看环境变量
- 7.如何在代码层面获取环境变量
- main函数的参数解析
- 通过代码获取环境变量
- 8.对环境变量的理解
- 9.suorce命令与.命令
- 10.命令行参数
- 命令行参数有什么用呢?
- 进程的优先级
- 11.为什么会有优先级呢?
- 12.PRI与NI
- 13.如何调整进程优先级(了解)
- 14.进程的几个概念
- 学习的指令
环境变量
1.基本概念
环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数。
2.常见环境变量
- PATH : 指定命令的搜索路径
- HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
- SHELL : 当前Shell,它的值通常是/bin/bash
- USER:记录了当前登录的用户是谁
主要以PATH进行学习。
3.我们写的程序和命令行指令有什么区别?
我们自己写的程序本质和命令行指令(ls…)没有本质区别,只是命令行指令被纳入了linux基本指令的范畴,本质都是可执行程序。
4.自己的程序为什么要用 ./ 执行,而命令行指令可以直接执行?
如果没有区别,为什么我们自己的程序要使用./文件名
的形式来执行,而命令行直接输入指令就可以执行?
——.
表示在当前路径,/
是路径分隔符,所以./文件名
是以相对路径的方式来执行可执行程序的,所以如果想执行自己的程序,我们也可以通过输入绝对路径来执行自己的程序,只是很麻烦。
但是我们发现自己写的程序只能用上面两种方式执行,不能像命令行指令一样直接输入来执行,这是因为默认的程序会在OS中存在一个环境变量,可以通过该变量在特定的目录下搜索命令行指令。
这个环境变量就是PATH。
使用echo $PATH
可以查看该环境变量:(其中各个路径用“ : ”作为分隔符)
当使用默认的命令行指令的时候,会在特定的环境变量所指明的路径中查找,如果找到了就会自动执行,所以不用带路径。当然如果指明路径来执行命令也当然可以正常运行。
而我们自己编写的程序,并不存在于环境变量内的路径中,所以需要用户自行指明路径然后再执行。
5.如何追加环境变量?
1.通过export
命令可以追加环境变量:
export PATH=$PATH:[要添加的环境变量路径]
//$PATH表示在原有的环境变量基础上追加新的路径。
//如果直接=[要添加的环境变量路径]的话会覆盖原有的。
这样就可以将自己的程序或其他的命令添加到环境变量中,这就是配置环境变量。
如果将自己的程序路径添加到环境变量中,就可以不用 ./ ,直接输入文件名执行进程了。
2.直接将想要添加的文件移动到PATH中原有的默认路径下也可以直接运行:
在linux中,将可执行程序直接拷贝到系统的默认路径下,让我们可以直接访问的方式,相当于linux下的软件安装。(安装软件的本质就是将可执行程序、配置文件拷贝到系统的特定路径下)
补充理解:
Linux是如何区分登录用户权限的呢?
——通过USER环境变量,访问文件时与其对比可得。
6.Linux如何查看环境变量
env:查看当前linux用户的环境变量
特定的用户有特定的环境变量,不是通用的,需要配置。
7.如何在代码层面获取环境变量
main函数的参数解析
我们平时使用的mian函数有参数吗?有几个参数?分别是?
——有参数;3个参数;分别是argc、argv、envp。
我们平时写代码的时候并没有添加这三个参数,是因为编译器会自动给我们添加,并不代表没有。
int main(int argc, char* argv[], char* envp[])
{
//...
return 0;
}
1.argc
第一个参数,存储argv中元素的个数。
2.argv
第二个参数,存储命令行的字符串(可执行程序字符串+参数选项字符串,下面有讲解)。
3.envp
其中第三个参数:envp是环境变量表。
envp是一个指针数组,指向一个个的字符串(环境变量字符串),envp的最后一个指针必须以NULL结尾。(不是整个数组的最后一个,而是第一个无效内容必须指向NULL)
通过代码获取环境变量
1.方法一:通过envp获取:
//方法一:
#include<stdio.h>
#include<unistd.h>
int main(int argc, char* argv[], char* envp[])
{
for(int i = 0; envp[i]; i++)
{
printf("envp[%d]->%s\n", i, envp[i]);
}
return 0;
}
2.方法二:通过environ获取:
但是没有envp怎么获取呢?
——一般使用environ来获取,它是一个二级指针,也指向环境变量表,头文件为unistd.h。
//方法二:
#include<stdio.h>
#include<unistd.h>
int main()
{
extern char** environ;//使用前声明一下
for(int i = 0; environ[i]; i++)
{
printf("environ[%d]->%s\n", i, environ[i]);
}
return 0;
}
结果:
方法3:通过函数获取:
主要使用这种方法!
getenv()——获取指定环境变量
返回值:char*
参数:const char* name——想要获取的环境变量名
在函数中用字符指针接收即可,比如要获取环境变量PWD:
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char* argv[], char* envp[])
{
char* pwd = getenv("PWD");
if(pwd == NULL)
{
perror("getenv");
}
else
{
printf("%s\n", pwd);
}
return 0;
}
执行程序即可获取当前路径(PWD环境变量中存储的是当前路径,%s打印即打印当前路径),效果和直接使用pwd命令是一样的。
所以其实我们平时使用的有些命令本质就是对环境变量的获取程序。
8.对环境变量的理解
其实环境变量就是内存级的一张表,由用户在登录系统时,给特定用户形成属于自己的环境变量表。并且每一个环境变量都有自己的应用场景。
1.那么环境变量所对应的数据,是从哪里来的呢?
——系统的相关配置文件中读取。
2.那么配置文件又在哪里呢?
——cd~进入家目录,可以找到.bash_profile
和.bashrc
这两个文件(脚本),其中.bash_profile会调用.bashrc,.bashrc会加载/etc/bashrc下的配置文件,一些全局的配置文件就在etc下的bashrc中配置。
在登录用户时,会自动执行这些配置文件,加载的时候PATH环境变量也在.bash_profile中被加载进去了。
全局的一些配置文件则在/etc/bashrc中配置。(命令行提示符 $ / # 就是在这里配置的)
3.前面说环境变量是内存级的,怎么解释?
——bash也支持命令行式的自定义变量,如下图解析:
如上图所示,export会将其添加到环境变量表中,不加export也在shell中,但是没有添加到环境变量表中。
即环境变量表在shell中,由shell维护,是内存级的。
当我们在创建执行新的子进程时,就会将环境变量表交给子进程。
那么如何证明呢?
——比如刚才在环境变量表中导入了hello=“abcdefg”,此时在自己的程序中用getenv打印hello这个环境变量,就会发现能够获取abcdefg这个字符串。
但是如果没有使用export,则不会添加到环境变量表中,这种变量被称之为shell的本地变量。
本地变量只在shell内部有效,不会被继承到子进程中,也可以使用export将本地变量添加到环境变量表中。
所以export所做的就是将本地变量添加到环境变量表中。
总结:
环境变量是可以被所有相关的子进程继承的——环境变量具有全局属性。
本地变量在能在shell内部有效,不会被继承到子进程中。
4.echo也是一个子进程,按理来说不会继承shell的本地变量,为什么能够打印shell的本地变量呢?
——echo是一个内建命令。
9.suorce命令与.命令
source [配置文件]
. [配置文件]
这二者的效果都是让后面跟的配置文件立马生效,比如source .bash_profile;
可以自己在文件中添加一些环境变量,然后source或.让其生效。
10.命令行参数
int main(int argc, char* argv[], char* envp[])
其中argc表示的是argv数组元素的个数,argv数组也和envp一样最后一个元素是NULL。
那么argv数组中存储的是什么呢?
——编写一个程序查看:
int main(int argc, char* argv[])
{
for(int i = 0; argv[i]; i++)
{
printf("argv[%d]->%s\n", i, argv[i]);
}
return 0;
}
结果:
解析:
在shell看来,我们输入的./myfile -b -c -d -e
就是以空格分隔的字符串。
其中第一个字符串./mufile称之为可执行程序,后续的子串-a -b -c称之为参数选项。
即:在shell内部,会将我们的输入的命令行切割成若干个子串,以空格作为分隔符,按顺序放在argv数组里。当我们给的参数越来越多,表也会越来越大。
这个表是由父进程bash制作的,传给子进程由子进程使用这个表。
总结:
argc表示可执行程序+参数选项的个数。
argv表示储存这些数据的表。
命令行参数有什么用呢?
——可以通过使用不同的选项来让同一个程序执行不同的功能。
一些命令是可以有自己的选项的,比如ls -a,而它的这些选项就是以字符串形式传递给程序,对应的程序内部会对选项做判断来执行不同的功能,这样就可以实现让同一个程序通过不同的选项来完成不同的执行结果。
比如:
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");
return 0;
}
进程的优先级
cpu资源分配的先后顺序,就是指进程的优先权(priority)。
优先权高的进程有优先执行权利。
11.为什么会有优先级呢?
——因为CPU的资源是有限的,而进程的个数是非常多的。必定会出现多进程竞争资源的情况,必须确认谁先谁后的问题。
12.PRI与NI
ps -l
查看进程属性时,PRI属性就是进程的优先级,NI属性是进程优先级的修正数据。
其中:
PRI的数字越小,表示优先级越高。
调整优先级,在Linux下一般是调整NI的值。
通过下面的公式来计算:
RPI(new) = PRI(old) + NI
注意调度器不允许用户对优先级过度调整,NI的取值范围为-20~19。
在平时作为普通的进程,很少会去调整进程的优先级。
13.如何调整进程优先级(了解)
在进程运行时,首先在在命令行输入top
,然后输入选项r
(renice),再输入想要调整进程的PID选中进程,最后输入想要调整的NI值即可(比如-10,如果输入太小,会自动变为下限-20)。
再次查看进程属性的时候,会发现PRI变为60,而NI变为-20。
如果在此基础上,再次修改NI为20,查看结果会发现PRI变成了99,这是为什么?
——因为RPI(new) = PRI(old) + NI,其中老的PRI指的是初始值80,而不是上次改变后的值。
总结:
计算PRI的时候,每次计算PRI的值都是从80开始的。
14.进程的几个概念
- 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级;
- 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰;
- 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行;
- 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为;
对于自己的单CPU电脑,其实平时任何时刻都只有一个进程在运行,只是在很短的时间内,很多个进程在被疯狂调度,使得在一个时间段内多个进程都得以推进。所以单CPU的电脑是并发而不是并行。
学习的指令
echo [环境变量名]:可以读取指定的名称
echo $[环境变量名],可以读取指定文件的内容( $可以理解为指针)
which [指令名]:查看命令所存在的路径(会在环境变量列表中查找)
export PATH=$PATH:[要添加的环境变量路径] :追加环境变量
通过export添加的环境变量只在本次有效,重新登录服务器后就恢复了。
不能写成export PATH=[要添加的环境变量路径],这样会覆盖原有的环境变量。
env:查看当前linux用户的环境变量