7_【Linux】四、Linux 进程概念(三)
「前言」文章的内容大致是进程优先级和环境变量。
七、进程优先级
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)+nicenice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行- 调整进程优先级,在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
HOME:指定用户的主工作目录(即用户登陆到Linux系统中时,默认的目录)
HOME环境变量就是 cd ~ 这个命令
普通用户:

超级用户root:

SHELL
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 里面有了
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
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声明
总结一下:
在进程的上下文中,获取环境变量的三种方式为:
getenvchar *env[]extern char **environ
--------------- END ---------------
「 作者 」 枫叶先生
「 更新 」 2022.12.10
「 声明 」 余之才疏学浅,故所撰文疏漏难免,
或有谬误或不准确之处,敬请读者批评指正。
7_【Linux】四、Linux 进程概念(三)
http://114.132.213.38:6250/archives/70d0bd88-7ab4-40f3-af2e-c61b5379df1f
评论