【Linux】进程第二课:进程优先级和环境变量

引言

上一篇文章我们已经学习了关于进程的一些基础概念和进程状态,接下来这篇文章,我将继续来讲解Linux系统中进程的相关知识。

1、进程优先级

进程优先级是什么?简单来说,进程优先级就是进程得到CPU资源的先后顺序。进程优先级在task_struct内是以一个整型变量来表示的,值越小优先级越高,反之则优先级越低。

进程优先级的值

在命令行中输入以下指令,我们可以看到一个进程的详细属性,其中就包括它的优先级信息。

bash 复制代码
ps al | head -1 && ps al | grep // +进程pid

上面我用红框框出来的两个数值,其中PRI指的就是进程的优先级,这里显示20是因为编译器的原因,将前面的100省略掉了,实际默认的PRI值应该是120 。而NI则表示进程优先级的修正数据,也叫做nice值,通过修改nice值可以修改进程的最终权限。进程的真实优先级 = (默认)PRI + NI。

nice值的取值范围为 -20 ~ 19,所以Ubuntu系统中进程的优先级范围为0到39,因为要考虑公平性,差值不能太大,一旦差值太大,很容易导致优先级低的进程长时间得不到CPU资源,进而导致进程饥饿。

进程切换

1、死循环程序是如何运行的

我们来思考一个问题:一个进程占有CPU时,会一次性将代码全部跑完吗?答案是不会,如果每次当一个进程占有CPU时都把代码全部执行完再退出,那么如果这个程序是个死循环程序,后面的其他进程岂不是都不能执行了。所以为了避免这种情况,系统会给每个进程分配一个叫做时间片的东西,给每个进程都规定一个时间,进程只有在这个时间内能占用CPU,如果在这个时间内进程没有跑完,操作系统就会将进程从CPU上拿下来,插入调度队列里重新排队,再排到它时就继续跑,重复这个过程直到跑完,这样就不会出现一个死循环程序独占CPU的情况。

2、CPU和寄存器

上文我们了解了,一个进程在CPU上运行时并不会一次执行完,那它下一次再占有CPU时,该怎么知道要从哪里开始执行呢?上文执行产生的数据又怎么办呢?CPU内会有很多个寄存器,它们都被用来存储程序运行时产生的一些临时数据,其中有一个寄存器里储存的是代码执行到了哪一行,CPU会根据这些寄存器里储存的信息来执行程序。

3、如何切换进程

当一个进程暂时被切下来时,需要该进程顺便带走并保存自己在执行过程中产生的上下文数据,就比如一些临时数据以及当前执行到的行数。在下一次回来的时候,进程就会将上次执行保存下来的数据又重新存进CPU里对应的寄存器中,CPU就又能根据寄存器中的内容来接着执行。这就是为什么我们上文要稍微介绍一下CPU和寄存器的原因。

所以,进程切换最核心的就是,保存和恢复当前进程的上下文数据,即CPU寄存器内的内容。这些上下文数据也会被存在task_struct中,里面有一个叫做TSS(任务状态段)的变量,也是一个系统定义的结构体类型,这些信息就被保存在里面。

进程调度

上文我们已经学习了进程是如何切换的,但是我们还不知道:进程优先级到底是怎么影响进程切换的?在切换进程时调度器是按什么规则来决定下一个要执行哪个进程的?要解决这些问题,我们需要知道进程调度的底层原理到底是什么

在每个CPU内部,都会有一个运行队列,它是一个名为runqueue的数据结构,其内部的组成大致如下:

第一张表

1、queue

接下来我们就来解析一下这张图。我们可以看到runqueue中有一个名为queue的成员,它是一个容量为140的指针数组,就对应我们的进程优先级。这时候又有一个问题了:上面我们在讲解进程优先级的值的时候,不是说了进程优先级的范围为0~40吗?各位还记不记得上文我在讲解时提到过,默认进程优先级其实是120而不是20,只是前面的数字被省略了而已。

在想通这个问题之前,我们需要先了解我们的操作系统,操作系统分为分时操作系统和实时操作系统,目前我们使用的操作系统是分时操作系统,分时操作系统与实时操作系统最大的区别就是,分时操作系统会使用时间片来实现多个进程的同时执行,而实时操作系统只会执行单一进程,上一个进程没执行完就不会执行下一个进程。

所以其实queue这个数组中的前100个位置都是实时优先级的位置,只有后40位是我们的分时优先级,这40个才是我们接下来要了解的。在这40个格子中,每个格子内都存在一个tast_struct的指针,每当有一个进程的优先级与某个格子的下标相等时,这个进程就会尾插到这个格子的链表下,再有一个优先级一致的进程就接着链在后面。未来在执行进程时,系统就会按数据结构从上到下进行查找,查到什么位置开始有进程,就调度那个进程去执行。

2、bitmap

这个数组虽然是一个哈希表,但是被之上还是要从上往下进行遍历,如果两个进程中间隔着很长的一块空白区域,效率还是很低,那么调度器该如何快速的找到下一个要执行的进程在哪呢?

我们可以再看看runqueue的结构,我们可以发现在queue上方还有一个名为bitmap的成员,这个成员中有5个元素,这个成员是一个位图结构,什么是位图呢?首先,bitmap的类型为unsigned int类型,这就意味着它每有一个元素就有32个比特位,5个元素就是160个比特位,是不是刚好覆盖140个queue元素个数呢?所以在bitmap中,如果第一个比特位为0,就代表queue中第一个位置无进程,如果第120个比特位为1,就代表queue中第120个优先级位置有进程。这样,有进程的位置显示为1,无进程的位置显示为0,调度器只要读一下bitmap,就能准确的知道下一个进程在哪个位置,直接找到,缩短了查找的时间。

3、nr_active

这个成员的功能很简单,就是用来记录当前链表中有多少个待执行的进程,调度器在找调度进程时,就可以先查看nr_active,看表中有没有进程,如果有再去调度。

第二张表

调度算法讲到这里还没有结束,因为现在还有一个问题:我们之前说过,进程如果第一次没有执行完代码,就会被重新链回调度链表中等待下一次执行,那如果我们假如在100的位置有一个进程,在第一次执行完后,它又被链回了100的位置,这样一来,岂不是永远都无法执行后面的进程了?该进程就会被重复的调度,直到彻底执行完为止,这就构成了我们之前说的进程饥饿状态,这就与我们上面所讲的不符。

所以只有一张表还不够完成整个调度算法,而是还有第二张表,看图我们就能直到,第二张表就在第一张表下面,并且结构与第一张表完全一样。当一个进程被调度,但是没有执行完时,回来时就会被插入第二张表中,这张表就是用来存储已经执行过的进程的。

而在这两张表上方,还有两个特殊的成员*active 和 *expired,这两个指针分别指向第一张表和第二张表,当第一张表所有进程都已执行过时,系统就将两个指针的内容交换一下,数据就又回到了第一张表中,又开始新一轮的调度。如此,才构成了完整的进程调度算法,这套算法的效率非常的高,只有O(1),不得不说设计出这个算法的人真是个天才。

现在还剩最后一个问题:如果在第一张表执行到一半的过程中,来了一个新进程,它是会被插入在第一张表还是第二张表中呢?答案是它会被链入第一个队列中,这个过程就被称为进程抢占。

2、环境变量

环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数。相信大家在对Linux系统的学习中都有这么一个疑问:为什么我们在执行自己的命令时要在前面加上./呢?因为要想执行一个程序,系统首先要找到它,./就代表告诉操作系统在当前目录里找。那为什么我们在执行系统命令时就不需要呢?因为在系统当中存在环境变量,来帮助系统来找到目标文件。

在Linux操作系统中,存在一个叫做PATH的环境变量,里面存储的是系统搜索指令的默认搜索路径,我们可以通过在命令行中输入命令来查看

bash 复制代码
env //显示当前系统中的所有环境变量

这些就是系统的默认搜索指令路径,多个路径之间通过冒号隔开。那如果我们想改变环境变量,将我们自己的文件路径加上去呢?我们也可以通过命令做到,不过需要注意的是,如果采用这种方式来更改环境变量,会直接将之前的环境变量覆盖掉,只剩我们自己修改的路径。

bash 复制代码
echo PATH=//+新路径  //覆盖原值

如果想在原本的环境变量后面添加内容,可以使用export这个命令,这个命令也可以用来添加一个全新的环境变量,要删除一个环境变量时,可以使用unset

bash 复制代码
export PATH=$PATH://要添加的路径
export NEW=1122 //增加新的环境变量
unset NEW //删除环境变量

环境变量,最开始是从哪里来的呢?环境变量最开始是从系统的相关配置文件来的,每一个用户的家目录里都存在两个隐藏的家目录文件:.bashrc 和 .bash_profile。.bashrc 会加载.bash_profile,而 bash_profile会去加载系统的 .bashrc配置文件。环境变量是可以被子进程继承的,为什么要让环境变量可以被子进程继承呢?因为如果子进程获取了环境变量,就可以根据环境变量做个性化的操作。

认识其他的环境变量。HOME:当前用户的家目录路径;HISTSIZE:记录最近执行过的1000条命令;HOSTNAME:记录当前计算机的主机名;PWD:记录下当前所处的工作路径;OLDPWD:记录上一个所在的工作路径。

结语

这篇文章带大家系统了解了进程优先级和环境变量的问题,重点讲解了进程优先级的相关内容,因为环境变量并不算一个特别难的内容,所以我就大致介绍一下,希望大家通过这篇文章能有所收获

相关推荐
maosheng11461 小时前
【无标题】linux中企业容易考的的小知识点1(rhcsa)
linux·运维·服务器
牢姐与蒯1 小时前
Linux进程(九).自定义shell——深度理解命令行和shell
linux·运维·服务器
前端 贾公子1 小时前
Milvus使用指南 (下)
java·服务器·前端
snow@li1 小时前
服务器运维:mysql 安装笔记
运维·服务器·mysql
天道jimmy1 小时前
VulnHub 系列:View2aKill,1
linux·运维·服务器
荀彧原名苟或1 小时前
SAP 执行 CMD命令(GUI_EXEC 执行电脑本机命令、SXPG_COMMAND_EXECUTE函数执行服务器上的外部程序)<转载>
运维·服务器·sap·abap
帷幕落秋1 小时前
Nginx 系列实战(三):LNMP 完整搭建,动静分离的学习总结
运维·自动化运维
Jae den2 小时前
服务器 CPU 核心越多越好吗?
运维·服务器
先吃饱再说2 小时前
从本地跑通到公网访问:前后端项目部署实战
运维·全栈