Linux进程优先级与环境变量
@[toc]
零.前言
本文前部分内容讲解进程优先级,后部分内容讲解环境变量以及命令行参数,理解了环境变量和命令行参数的内容,我们就可以理解为什么使用指令的时候可以不指定路径,为什么指令带不同选项的本质一样但是会有细微的差别,本文都将为您详细进行阐述。
1.进程优先级
(1)优先级的本质
进程获取CPU资源的时候是需要进行排队的,排队的顺序是由各个进程的优先级来决定的。优先级的本质其实就是合理地分配资源。
(2)PRI与NI
PRI:每个进程的PRI表示的就是该进程的优先级,值越小,优先级越高。
NI:也称为nice值,表示优先级的修正数值。其中 -20<=NI<=19
PRI(新)=PRI(旧)+NI
#include<stdio.h>
#include<unistd.h>
int main()
{
while(1)
{
printf("I am a child,pid:%d,ppid:%d\n",getpid(),getppid());
sleep(2);
}
return 0;
}
我们使用这个死循环程序来观察进程优先级。
通过ps -al来进行进程的查看:
其中我们就可以看到进程的PRI和NI,当前进程的优先级就为80。
(3)进程优先级的修改
修改优先级的方式
操作系统不是直接对PRI来进行修改,而是通过修改NI的方式来调整优先级。并通过 PRI(新)=PRI(旧)+NI来进行计算新的优先级。
我们可以通过调整优先级的接口,或者top指令来进行修改,不过不建议修改优先级因为对优先级的修改你肯定没有操作系统专业,默认的就很好。
top修改优先级
首先输入top,此时会出现进程列表,输入r,输入进程pid,就可以对该进程的优先级进行修改了。
比如我修改为10,注意这里是对NI进行修改,此时PRI=PRI+NI=90,我们再来查看进程,会发现该进程的PRI变成了90。
然而我再对其优先级进行修改,修改为-10,那么该进程的优先级是不是又回到80了呢?注意第二次修改需要在root下完成,不然会permission deny
答案是否定的,它变成了70。
我们可得到一个结论:无论哪一次修改,PRI(新)的值都是80。
(4)优先级的范围
我们为什么要对NI设限制呢?当我们对NI修改为100的时候,默认最终的NI是19,操作系统不允许它更大了。
这是因为优先级是一个相对的概念,操作系统设定优先级的目的是让进程合理的享受到CPU的资源,如果将进程优先级设得过小或过大,就会出现有进程无法享受到CPU资源的问题,造成"进程饥饿"。
(5)其他概念
竞争性:系统进程数目众多,而CPU资源只有少量,甚至一个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。
独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰。
并行:多个进程在多个CPU下运行。并行中包含并发。
并发:多个进程在一个CPU下运行,需要采用进程切换的方式。
2.环境变量
(1)基本概念
环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数,比如,我们在编写C程序的时候,在链接时,从来不知道我们的所谅解的动态静态库在哪里,但是仍然可以链接成功,生成可执行程序,原因就是相关环境变量帮助编译器进行查找。
环境变量通常具有某些特殊用途,还有在系统中具有全局性。
(2)环境变量的引入
#include<stdio.h>
#include<unistd.h>
int main()
{
int count=5;
while(count)
{
sleep(1);
printf("I am a cwd->process!\n");
count--;
}
return 0;
}
我们拿这段简单的程序来引入环境变量这一概念。
当我们使用gcc去编译这段代码时,会生成一个可执行文件test。
当我们想要执行该文件的时候,我们发现我们需要使用./test才能执行,其中./的作用是帮助系统确定该程序在哪里。
同时我们也知道,我们在命令行上敲的各个指令,其本质也是一个可执行文件,那么为什么这些指令就不需要使用类似./的路径呢?
因为环境变量的原因。
(3)环境变量PATH
我们可以使用echo语句来查看一下环境变量的内容:
echo $PATH
注意一定要使用$符号,否则看到的就是PATH这一字符串。
我们发现所谓的环境变量就是一堆用":“隔开的目录而已。
在这些目录下我们就可以找到我们现在所使用的命令行指令文件。
当我们使用指令时,操作系统会依次扫描这些用”:"隔开的文件,在这些文件的目录下找到该指令对应的可执行文件,并运行它。
(4)添加环境变量
如果我们想让可执行文件test,不添加./也可以执行的话,就需要将其添加到环境变量中。其实所谓的安装软件就是将软件的路径安装到PATH中。
有两种方式:
第一种是将test添加到PATH中任意一个目录下。
另一种是将test的路径添加到PATH中。
不建议使用第一种,因为这会污染运行环境。
我们使用第二种方式,注意,这并不是永久的,如果希望永久修改,可以更改配置文件,但是不推荐。
export PATH=$PATH:当前路径
注意一定要将PATH加上,直接加test的路径,会把原来的路径覆盖掉。
此时我们再使用test,就不需要使用./了。
注意,不能使用test作为可执行文件名,因为已经有这个命令了。博主写到这里才发现,我们将名字改成mytest
此时我们发现不用./程序也可以执行了,注意由于没有改配置文件,重新链接服务器后不会保留环境变量的更改。
(5)其他环境变量
HOME
当我们使用不同的用户登录系统时,会发现我们的初始目录是该用户的根目录,这是因为HOME环境变量来决定的。
环境变量分为系统变量和用户变量,有的环境变量比如PATH对于所有用户都是一样的,有的全局变量比如HOME对于不同用户是不同的。
我们可以分别在root和lhb的用户下查看HOME。
SHELL
我们还可以通过shell来查看shell版本:
(6)环境变量的本质
在语言上定义变量的本质是在内存上开辟空间,操作系统当然也可以在内存上开辟空间,环境变量的本质技术操作系统在内存或者磁盘开辟的空间,用来保持系统相关的数据。
环境变量=变量名(PATH)+变量内容(PATH的各个目录)
(7)和环境变量相关的指令
echo:显示某个环境变量的值。
export:设置一个新的环境变量。
env:显示所有的环境变量。
unset:清除环境变量。
set:显示本地变量和环境变量。
我们可以测试一下env:
其中这些都是环境变量。
(8)本地变量
在操作系统中还存在一种变量我们称为本地变量,它只在本地登录有效。
我们可以在命令行上直接定义:
本地变量只在本次登录有效,存在时间和没有写在配置文件中的环境变量是一样的。
我们可以使用export将本地变量变成环境变量,注意是没有写在配置文件中的那种。
我们可以先用echo查找一下环境变量myval:
我们发现set可以找到,但是env并不能找到,这说明myval是一个本地变量,而不是一个环境变量。
export myval
当我们将其设定成环境变量之后,我们发现我们可以找到了:
3.命令行参数
(1)命令行参数
在C语言程序中,其实主函数是可以进行传参的。
我们可以向主函数传入:int argc和char* argv[]
int main(int argc,char* argv[])
{
for(int i=0;i<argc;i++)
{
printf("argv[%d]->%s\n",i,argv[i]);
}
}
其中argc和argv表示的就是命令行参数。
其中argc表示的是命令行中指令的个数,argv是一个字符指针数组,它的每一个元素都指向命令行中的一个指令(把指令看成字符串)。
这段代码打印的结果是:
这是因为命令行中只有一个指令./mytest,所以argc的值为1,数组argv中第一个元素指向的就是字符串./mytest
当我们输入多条指令的时候:
此时argc的值为5,argv中的元素分别指向各个指令字符串。
注意,argv中最后一个元素指向空,也就是argv元素个数其实为argc+1。
(2)指令带选项的原理
通过环境变量,我们知道了为什么可以不指定路径来执行各个指令对应的可执行文件,那么指令是如何带选项的呢?
这就和命令行参数有关了:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char* argv[])
{
//指令带选项的原理
if(argc!=2)
{
printf("Usage:%s -[a][h]\n",argv[0]);
}
if(strcmp(argv[1],"-h")==0)
{
printf("-h done\n");
}
else if(strcmp(argv[1],"-a")==0)
{
printf("-a done\n");
}
else
{
printf("Usage:%s -[a][h]\n",argv[0]);
}
我们使用这一段代码来进行选项添加,当输入选项-a的时候,argv[1]指向的值是-a,此时执行-a的逻辑。-h同理。当没有输入-a或者-h的时候,执行提示的逻辑。
4.获取环境变量
(1) char* env[]
我们还可以将env[]传递给main函数,它也是一个指针数组,它的每个元素指向的是一个环境变量的字符串。
int main(int argc,char* argv[],char* env[])
{
for(int i=0;env[i];i++)
{
printf("%d->%s",i,env[i]);
}
return 0;
}
我们可以通过这一方式来将环境变量打印出来,和我们直接使用env的效果是一样的。
(2)environ
我们也可以使用特定的第三方变量environ来进行环境变量的查看。
我们可以打开man手册来看一下:
我们发现它是一个二级指针,它指向的其实就是env[]这个指针数组。
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char* argv[],char* env[])
{
//测试environ查看环境变量
extern char** environ;
for(int i=0;environ[i];i++)
{
printf("%d->%s\n",i,environ[i]);
}
}
此时也能显示所有的环境变量。
由于是第三方变量,所以我们需要对其进行extern声明。
注意,env数组最后一个元素指向的也是NULL。
(3)getenv
我们还可以使用getenv接口来接收环境变量。首先我们要明确的是函数没有参数,也是可以对其传参操作的,当我们不写char* env时,系统也会默认将其传给主函数。
我们就可以通过getenv来获取各个环境变量中的内容:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main()
{
printf("%s\n",getenv("PATH"));
printf("%s\n",getenv("HOME"));
}
此时我们就可以打印出环境变量的值了:
5.对环境变量全局属性的理解
我们还是拿出我们老生常谈的那个程序:
int main()
{
printf("I am a process,pid:%d,ppid:%d\n",getpid(),getppid());
}
我们对其运行多次,发现:pid总是在变化,但是ppid却从来都没有变:
我们可以查看一下这个父进程,发现它就是bash。
因此我们得出结论:凡是在命令行上启动的进程,它们的父进程都是bash。
在main函数中,会被传入环境变量的指针数组env。它所指向的环境变量,就是从父进程中继承过来的。也就是bash给的,bash的环境变量来自于配置文件。
我们可以来简单验证一下:
int main()
{
//验证环境变量的全局性
printf("%s\n",getenv("my_env"));
}
我们在bash中建立一个本地变量my_env,此时是程序是无法打印的,因为mytest中没有传入该环境变量。
但是我们将bash中的本地变量my_env转换为环境变量之后,发现mytest中传入的环境变量my_env。
而父进程可以创建子进程,环境变量又会被继承,因此所有进程的环境变量其实最开始都来自于bash。
环境变量具有全局属性的本质就是环境变量可以从父进程继承到子进程中。
6.总结
进程优先级的改变是由通过nice值对PRI进行修改的。
环境变量告诉我们在终端执行一个指令的本质是在环境变量中的各个目录下进行查找指令的可执行文件。命令行参数可以在程序中获取命令行的字符串,通过程序对其进行一定的操作。有了这两个基础,我们就可以自己来书写指令了。
- 点赞
- 收藏
- 关注作者
评论(0)