目录
七、进程优先级
基本概念
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 查看进程优先级和更改进程优先级
测试代码
intmain() { while(1) { printf("PID:%d\n", getpid()); sleep(2); } return0; }
运行进程,查看优先级
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命令提升权限
sudotop
有权限之后就可以调高进程的优先级了
注意: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 命令查看一下
1. file mytest 2. //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用户
intmain() { char*who=getenv("USER"); printf("%s\n", who); return0; }
运行结果
8.8 环境变量通常是具有全局属性的
我们可以随便定义一个环境变量,比如
但是这个环境变量叫做本地环境变量,它是一个局部变量,在 env 全部环境变量里面查找是没有的
export: 设置一个新的环境变量,可以使用这个指令设置新的环境变量,再次查找就已经在 env 里面有了
[fy@VM-4-14-centos d2]$ export aaa
下面进行代码测试
intmain() { char*myval=getenv("myval"); if(myval==NULL)//getenv 查询为没有返回 NULL { printf("myval no found\n"); return1; } printf("%s\n", myval); return0; }
没有设置环境变量 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[]
测试代码:
intmain(intargc, char*argv[]) { inti=0; for(i=0; i<argc; ++i) { printf("argv:[%d] -> %s\n", i, argv[i]); } return0; }
运行结果,指针数组的 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 结尾,与第二个参数一致
测试代码:
intmain(intargc, char*argv[], char*env[]) { inti=0; //env 也是以 NULL 结尾,NULL 就是 0 for(i=0; env[i]; ++i) { printf("env:[%d] -> %s\n", i, env[i]); } return0; }
运行结果,说明 char *env[] 就是用于获取环境变量的,这也说明我们写的程序为什么能够获取到环境变量
8.10 第三方变量 environ 获取环境变量
除了使用 main 函数的第三个参数和 getenv 来获取环境变量以外,我们还可以通过第三方变量 environ 来获取
man 查看 environ,它是一个二级指针,它其实指向的是 char *env[]
测试代码:
intmain() { externchar**environ; inti=0; while(environ[i]) { printf("environ:[%d] -> %s\n", i, environ[i]); ++i; } return0; }
运行结果
注:libc 中定义的全局变量 environ 指向环境变量表,environ 没有包含在任何头文件中,所以在使用时要用 extern声明 总结一下:
在进程的上下文中,获取环境变量的三种方式为:
- getenv
- char *env[]
- extern char **environ
---------------我是分割线---------------
文章就到这里,下篇即将更新