目录
七、进程优先级
7.1 基本概念
7.1.1 什么是优先级
7.1.2 为什么存在优先级
7.1.3 Linux 优先级特点
7.2 查看系统进程
7.3 PRI 和 IN
7.4 查看进程优先级和更改进程优先级
7.5 其它概念
7.6 进程切换
八、环境变量
8.1 环境变量基本概念
8.2 常见环境变量
8.3 查看环境变量
8.4 和环境变量相关的命令
8.5 测试PATH
8.6 测试HOME和SHELL
8.7 通过系统调用获取环境变量
8.8 环境变量通常是具有全局属性的
8.9 命令行参数
8.10 第三方变量 environ 获取环境变量
七、进程优先级
7.1 基本概念
7.1.1 什么是优先级
优先级实际上就是获取某种资源的先后顺序,而进程优先级实际上就是进程获取CPU资源分配的先后顺序,就是指进程的优先权(priority)
注:优先级和权限是不同的概念,权限决定的是一件事情能不能做,优先级是在权限允许的前提下,该事情先做还是后做
7.1.2 为什么存在优先级
优先级存在的主要原因就是资源是有限的,而存在进程优先级的主要原因就是CPU资源是有限
7.1.3 Linux 优先级特点
优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能
进程优先级本质就是 PCB 结构体里面的一个数字(其他OS也可能是几个),比如吃饭排队,给排队的每一个人一个编号,到哪个编号服务员就喊哪个编号
7.2 查看系统进程
在Linux或者Unix操作系统中,用 ps -l 命令会类似输出以下几个内容
ps -l
注意到其中的几个重要信息,有下:
- UID : 代表执行者的身份
- PID : 代表这个进程的代号
- PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
- PRI :代表这个进程可被执行的优先级,其值越小越早被执行(priority)
- NI :代表这个进程的nice值
进程的优先级就与 PRI 和 NI 有关,Linux支持进程进行中进行优先级的调整,调整的策略是通过更改 NI(nice值)完成的
最终优先级 = 老的优先级 + nice值
7.3 PRI 和 IN
- 进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高
- nice值 其表示进程可被执行的优先级的修正数值
- PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为: PRI(new)=PRI(old)+nice
- ice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行
- 调整进程优先级,在Linux下,就是调整进程nice值
- nice其取值范围是-20至19,一共40个级别
- 在Linux中,PIR 的默认值是 80
所以,在Linux中,最终优先级PIR = 80 + nice值,PRI(old)默认是 80
注意:
- 进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化
- 可以理解nice值是进程优先级的修正修正数据
7.4 查看进程优先级和更改进程优先级
测试代码
#include<stdio.h>
#include<unistd.h>
int main()
{
while(1)
{
printf("PID:%d\n", getpid());
sleep(2);
}
return 0;
}
运行进程,查看优先级
ps -al
在Linux操作系统中,初始进程一般优先级PRI默认为80,NI默认为0
然后通过 top 命令更改进程的nice值,top进入top后按“r”–>输入进程PID–>输入nice值
top
top 命令就相当于Windows操作系统中的任务管理器
然后按 r
输入你要调整进程的优先级的 PID,然后回车
之后输入 nice值后按“q”即可退出,如果我这里输入的nice值为15,再次查看,发现 6845进程的进程优先级 PIR 发生了改变,IN 也发生了改变
注意:
若是想将NI值调为负值,也就是将进程的优先级调高,需要使用sudo命令提升权限
sudo top
有权限之后就可以调高进程的优先级了
注意:nice其取值范围是-20至19,你写 nice值超过19,默认为19,比如你输入50,nice 值最高依旧是19,-20也是如此。PRI(old)默认值是 80,不会发生改变
7.5 其它概念
- 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
- 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰。就比如你运行的抖音崩溃了,但是不会影响 qq的进程运行,进程运行各不干扰,不会说抖音运行崩溃了会导致你QQ运行崩溃。独立性后面讲到进程地址空间的时候再深入理解
- 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
- 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发
解释并发:
假设在只有一个 CPU 的情况下,我们发现也能同时运行几个或多个进程,比如你运行了浏览器,但是又可以运行QQ ,也可以打开PDF阅读器...不是说一个CPU只能运行一个进程么?为什么你又可以运行多个进程?
一个运行中的进程,CPU 不会说一直等它运行结束才运行下一个进程。当代计算机采用的是 “时间片轮转” 的策略,规定每个进程在 CPU 上运行是有一定时间限制的。假设每个进程运行时间片是 10毫秒,CPU 一秒钟就能运行 100 个进程。即使进程没有运行完,也要把进程从 CPU 上拿下来,重新放在运行队列尾部继续排队,再次等待 CPU 运行。这就解释了一个CPU 可以运行多个进程的 “现象”,这种现象就叫并发
7.6 进程切换
CPU 内是有一套寄存器硬件的,其中一个 pc 或叫 eip 寄存器的作用是:保存当前正在执行指令的下一条指令的地址
CPU 永远做着三件事:
- 取指令
- 分析指令
- 执行指令
当进程在 CPU 上运行的时候,会产生很多的临时数据,这些数据是属于当前进程的,这些数据并且保留在 CPU 的寄存器上
注意区分:CPU 内部虽然有一套寄存器硬件,但是,寄存器内保留的数据是属于当前进程的。寄存器硬件 != 寄存器内的数据
上面的并发谈到,进程在 CPU 上运行是,该进程占用 CPU,进程不是一直占用 CPU 到进程结束,而是说,进程运行的时候,有属于自己运行的时间片。当进程运行时间达到时间片后,就会从 CPU上拿下来。
进程被拿下来到下一次进入CPU 运行也要被恢复,怎么恢复?
进程被 CPU 拿下来了,该进程运行时产生的临时数据也要保留,这里就暂且说临时数据保留在 PCB 里面吧(这里保留在PCB不太对,这里暂且这么理解)
等到进程下一次运行的时候,之前保留在的数据要被恢复。
举个栗子:比如你在大一要去当兵,去当兵你的学校会把你的学籍保留下来,等你从部队回来了,你的学籍就要被恢复才能继续读书。这里的学校就相当于 CPU,你就相当于一个进程,部队就相当于一个等待队列,学籍就相当于在上一次运行是产生的临时数据。你从部队回到学校继续读书就相当于进程再一次进入 CPU 内运行,这一个过程就叫进程切换!
进程在切换的时候,要对进程的上下文保护,这里的上下文指的就是寄存器内的临时数据,不是寄存器。当进程在恢复运行的时候,要进行上下文恢复
在任何时刻,CPU 内里面寄存器的数据,看起来是在大家都能看见的寄存器上,但是,寄存器内的数据只属于当前进程。寄存器被所有进程共享,但寄存器内的数据,是每个进程各自私有的,这些私有的数据叫上下文数据
八、环境变量
8.1 环境变量基本概念
我们所写的代码生成的可执行程序,这个可执行程序就是一个命令,不过我们要加 “./” 才能运行它。我们使用的 Linux命令本质就是一些可执行程序,不过执行它们,不用加 “./” 就可以直接运行这些可执行程序
我们可以使用 file 命令查看一下
file mytest
//mytest 是你的可执行程序
也可以使用 file 命令查看一下Linux 的命令
为什么我们写的可执行程序要加 “./” 才能运行,而Linux的命令却不用加 “./” 就能运行?它们同样是可执行程序
这就与环境变量有关系了,我们也可以把 mytest 可执行程序放入 /usr/bin/ 路径下,不用加 “./” 也能直接运行 mytest,但是这种做法不推荐,你写的程序没有经过测试,可能会污染命令池
环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数
如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。
环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性
8.2 常见环境变量
- PATH : 指定命令的搜索路径
- HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
- SHELL : 当前 Shell,它的值通常是/bin/bash
这些下面解释
windows下也有环境变量,“我的电脑” -> 右键 -> 属性 -> 高级系统设置
8.3 查看环境变量
env 命令
env //显示所有环境变量
查一个具体的环境变量路径
echo $NAME //NAME:你的环境变量名称
比如查看,PATH、HOME,找到的话会打印出来,以冒号分隔
8.4 和环境变量相关的命令
- echo: 显示某个环境变量值
- export: 设置一个新的环境变量
- env: 显示所有环境变量
- unset: 清除环境变量
- set: 显示本地定义的shell变量和环境变量
8.5 测试PATH
PATH : 指定命令的搜索路径
我们所在 Linux 执行的每一条指令,都会通过这个环境变量 PATH 进行检索,找到了指令就运行它,找不到就说没有这个命令
上面也提到,为什么我们写的可执行程序要加 “./” 才能运行,而Linux的命令却不用加 “./” 就能运行?它们同样是可执行程序
系统就是通过环境变量 PATH 来找到相关命令的,比如上面的 ls、mytest,环境变量会在系统的路径下查找该命令, 找到就执行,找不到就说没有
用 echo $ 查看环境变量PATH我们可以看到如下内容
[fy@VM-4-14-centos d1]$ echo $PATH
可以看到环境变量PATH当中有多条路径,这些路径由冒号隔开,比如当你使用 ls命令时,系统就会查看环境变量 PATH,然后默认从左到右依次在各个路径当中进行查找,而 ls 命令实际就位于 PATH当中的某一个路径下,所以就算 ls 命令不带路径执行,系统也是能够找到的,这就是因为 ls 命令处于环境变量 PATH 的路径下
这也说明了为什么我们直接执行我们写的可执行程序 mytest 不可以,因为我们写的可执行程序 mytest 不在环境变量 PATH 的路径下,我们想执行我们写的可执行程序就必须加上 “./” ,它的作用就是帮我们在当前路径下找到该可执行程序并执行它
那可不可以让我们自己的可执行程序也不用带路径就可以执行呢?
当然可以,下面给出两种方式:
方式一:将可执行程序拷贝到环境变量PATH的某一路径下
上面也说了,这种做法不推荐,你写的程序没有经过测试,可能会污染命令池
方式二:将可执行程序所在的目录导入到环境变量PATH当中
export: 设置一个新的环境变量
使用命令 export PATH=$PATH:当前路径
export PATH=$PATH:/home/fy/CODE_lqh/code_linux/code_12_05/d1
这样就可以直接运行当前路径下的可执行程序了
更改环境变量,只对本次登录有效,只要不动配置文件,咋搞都没问题,下次登录又会重新生成新的环境变量
那问题又来了,系统的环境变量哪里来的呢?
进入系统目录 cd ~
系统定义环境变量就在这两个文件里面定义
我们在登录的时候,当前系统的 bash 进程默认会把这两个文件执行一次,也就是把环境变量导入到你当前的环境当中,你把当前的环境变量覆盖了,重新登录又会生成新的环境变量,这些环境变量都是具有全局属性的,每一个环境变量都有自己的功能
8.6 测试HOME和SHELL
HOME : 指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
HOME环境变量 就是 cd ~ 这个命令
普通用户:
超级用户:
SHELL : 当前 Shell,它的值通常是/bin/bash
我们在Linux操作系统当中所敲的各种命令,实际上需要由命令行解释器进行解释,而在Linux当中有许多种命令行解释器(例如bash、sh),我们可以通过查看环境变量SHELL来知道自己当前所用的命令行解释器的种类
每一个环境变量都有自己特定的功能
8.7 通过系统调用获取环境变量
有两种方法:putenv 和 getenv,putenv 后面讲解,这里使用的是 getenv
man 查看一下
man 3 getenv
测试代码
USER:标识当前使用的 Linux用户
#include<stdio.h>
#include<stdlib.h>
int main()
{
char* who = getenv("USER");
printf("%s\n", who);
return 0;
}
运行结果
8.8 环境变量通常是具有全局属性的
我们可以随便定义一个环境变量,比如
但是这个环境变量叫做本地环境变量,它是一个局部变量,在 env 全部环境变量里面查找是没有的
export: 设置一个新的环境变量,可以使用这个指令设置新的环境变量,再次查找就已经在 env 里面有了
[fy@VM-4-14-centos d2]$ export aaa
下面进行代码测试
#include<stdio.h>
#include<stdlib.h>
int main()
{
char* myval = getenv("myval");
if(myval == NULL)//getenv 查询为没有返回 NULL
{
printf("myval no found\n");
return 1;
}
printf("%s\n", myval);
return 0;
}
没有设置环境变量 myval 之前,进行运行
定义一个新的本地环境变量 myval,并将 myval 设置成一个新的环境变量
再次运行程序
结果说明了:环境变量是可以被子进程继承下去
bash 是一个系统进程,mytest 运行后也是一个进程,这个进程是 bash 的一个子进程,子进程能找到并打印环境变量 mytest,说明了子进程可以集成环境变量,子进程的环境变量是从 bash 来的,所以说环境变量具有全局属性
环境变量具有全局属性,根本原因是环境变量是可以被子进程继承下去
环境变量为什么要被子进程继承下去?
为了适应不同的场景,让 bash 帮找指令路径,进行身份验证
上面的 myval 没有设置成环境变量,当它只是一个本地变量时,它只会在当前进程 bash 内有效,它无法被子进程继承,所以它就是一个局部变量
----------------我是分割线---------------
- unset: 清除环境变量
- set: 显示本地定义的shell变量和环境变量
定义一个本地变量 yourval,不设置成环境变量
使用命令 set,里面会显示所有的环境变量并且包括本地变量
grep 查一下 yourval
如果这个环境变量不想要,使用命令 unset,再次查找这个环境变量或本地变量已经没有了
8.9 命令行参数
你是否知道 main 函数有参数吗?
答案是:有,而且是三个,只是我们平时基本不用它们,所以一般情况下都没有写出来
main 函数的三个参数为:int argc,char* argv[],char* env[]
main 函数也是函数,也是被调用的,这些参数一般是系统或父进程传的
这里我们先测试前面两个参数: int argc,char* argv[]
测试代码:
#include<stdio.h>
int main(int argc, char *argv[])
{
int i = 0;
for(i = 0; i < argc; ++i)
{
printf("argv:[%d] -> %s\n", i, argv[i]);
}
return 0;
}
运行结果,指针数组的 0 下标指向的就是 ./mytest
那再给它增加一些参数(程序后面是可以带参数的)
再给它增加一些参数,我们传的参数越多,它的 argv指针数组就越大
先不急解释上面的,我们先来看看一个经常使用的命令 ls
我们都知道命令后面是可以带选项的,ls 就是程序名,后面的 -a -b -l...是这个程序的选项,后面的选项参数有多少,char *argv[] 这个数组就有多大,argc 传的就是数组的大小
比如:“ls -a -b -c -d -e”,命令行解析会把这个长的字符串拆分成一个个子字符串:"ls" "-a" "-b" "-c" "-d" "-e",这些子字符串就存在指针数组 char *arvg[] 里面,"ls" "-a" "-b" "-c" "-d" "-e" 共有 6个,所以 int argc 的大小就为 6
一个命令就被解析成这样,这些工作一般是系统和 shell 做的,argc 代表你的命令行一共有多少个子字符串,argv 就是一张映射表,把每个子字符串一一映射在 argv 里面
为什么要解析成这样子?
为了执行不同的功能,比如“ls -a”,它就执行 -a 的功能,“ls -a -b”它就执行 -a -b 的功能,这就是命令行参数最大的意义
上面的 mytest 也是如此
接下来测试第三个参数 char* env[],它是用于获取环境变量的,它也是一个指针数组,以 NULL 结尾,与第二个参数一致
测试代码:
#include<stdio.h>
int main(int argc, char *argv[], char *env[])
{
int i = 0;
//env 也是以 NULL 结尾,NULL 就是 0
for(i = 0; env[i]; ++i)
{
printf("env:[%d] -> %s\n", i, env[i]);
}
return 0;
}
运行结果,说明 char *env[] 就是用于获取环境变量的,这也说明我们写的程序为什么能够获取到环境变量
8.10 第三方变量 environ 获取环境变量
除了使用 main 函数的第三个参数和 getenv 来获取环境变量以外,我们还可以通过第三方变量 environ 来获取
man 查看 environ,它是一个二级指针,它其实指向的是 char *env[]
测试代码:
#include<stdio.h>
#include<unistd.h>
int main()
{
extern char **environ;
int i = 0;
while(environ[i])
{
printf("environ:[%d] -> %s\n", i, environ[i]);
++i;
}
return 0;
}
运行结果
注:libc 中定义的全局变量 environ 指向环境变量表,environ 没有包含在任何头文件中,所以在使用时要用 extern声明
总结一下:
在进程的上下文中,获取环境变量的三种方式为:
- getenv
- char *env[]
- extern char **environ
----------------我是分割线---------------
文章就到这里,下篇即将更新