【Linux】进程优先级 | 进程的切换 | 环境变量详解_linux

🤣 爆笑教程 👉 《看表情包学Linux》👈 猛戳订阅  🔥

【Linux】进程优先级 | 进程的切换 | 环境变量详解_服务器_02


💭 写在前面:我们先讲解进程的优先级,探讨为什么会存在优先级,以及如何查看系统进程、进程优先级的修改。然后讲解进程的切换,首次介绍进程的竞争性、独立性,以及并行和并发的概念,在通过讲解进程抢占引出可见寄存器与不可见寄存器。最后我们讲解环境变量,介绍环境变量 PATH,并且做一个 "让自己的可执行程序不带路径也能执行"的实践,讲解环境变量的到如何删除,最后再讲几个常见的环境变量。


Ⅰ. 进程优先级(Process Priority)

0x00 引入:什么是优先级?

我们先思考思考 权限 是什么?权限的本质是谈论 "能" 还是 "不能" 的问题。

那什么是 优先级 ?优先级是进程获取资源的先后顺序!

【Linux】进程优先级 | 进程的切换 | 环境变量详解_服务器_03


CPU 资源分配的先后顺序,就是指进程的优先权(priority)

优先权高的进程有优先执行权利。配置进程优先权对多任务环境的 Linux 很有用,可以改善系统性能,还可以把进程运行到指定的 CPU 上,这样一来就可以把不重要的进程安排到某个 CPU,可以大大改善系统整体性能。

0x02 为什么会存在优先级?

我们不妨先思考下我们日常生活中排队的本质,排队的本质可以说是 "确定优先级" ,

而插队行为就是更改优先级。因为排队造就了优先级,那我们为什么要排队?

【Linux】进程优先级 | 进程的切换 | 环境变量详解_服务器_04

  可以不排队吗?可以,结果就是大家一窝蜂抢呗,全部用抢的。

"无理由暴力抢占式,优胜劣汰,我挤死你"


【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_05


(前后时间来确认先后顺序) 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_运维_06

 (谁能挤谁能撞谁就排到前面)

现实生活中一旦出现了抢,就难免会引发争执。

在比如说食堂买饭如果大家都不排队,如果你就是挤不过别人,就会一直买不到,

你就每次都要饿肚子,这在操作系统中叫做 "饥饿问题" 。

所以,排队主要是换了一种竞争方式,不以那么残酷的方式竞争,让进程都能 井然有序

我们之所以要排队,其实最主要的原因是因为资源不够!

如果资源是无限的,就像希尔伯特旅馆一样,该旅馆拥有无限多的房间,那也不需要排队的了。

200 名学生要去食堂吃饭,但窗口就 20 个,当然需要排队。如果窗口有 200 个,理论上不用排。

因为 系统里面永远都是进程占大多数,资源是少数。 这就导致了进程竞争资源是常态!

排队和进程资源竞争都是一定要确认先后的,它们的本质都是 确认优先级

本章我们要讲的是 Linux 的进程优先级,Linux 下的优先级有很多方式,包括设置和修改。

我们不建议修改优先级,如果你不懂调度器的调度算法,你随便修改优先级其实就是变相地 "插队" 了。你可以让你的进程尽快地得到了某种 CPU 资源或其它资源,凡是可能会打破调度器的平衡。其实你在用户层再怎么设置,也不会对调度器的调度策略产生什么影响。

再加上设置优先级没有什么意义,所以本章我们就不去讲解了。

0x03 查看系统进程:ps -l

🔍 查看系统进程:在 Linux 或者 Unix 系统中,输入  ps -l 


$ ps -l    # 查看进程的优先级


【Linux】进程优先级 | 进程的切换 | 环境变量详解_优先级_07


我们写一个简单的 Hello 程序,令其每隔一秒发送一次 Hello:


#include <stdio.h>
#include <unistd.h>

int main(void) {
    while (1) {
        sleep(1);
        printf("Hello!\n");
    }
}


我们把它运行起来,此时我们使用  ps -l 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_运维_08

因为  ps -l  只能显示当前终端下进程的相关信息,我们可以使用给它加上

【Linux】进程优先级 | 进程的切换 | 环境变量详解_运维_09

 选项:

$ ps -la


 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_运维_10

【Linux】进程优先级 | 进程的切换 | 环境变量详解_linux_11

【Linux】进程优先级 | 进程的切换 | 环境变量详解_优先级_12


0x04 进程优先级的修改

【Linux】进程优先级 | 进程的切换 | 环境变量详解_linux_13

其实,我们系统中是存在   nice  命令的,对应的还有   renice 


$ nice  
$ renice


它们可以让我们在启动一个进程时直接指定优先级,或者启动中或启动前设置优先级。

感兴趣可以自行查阅,我们还是主要学习如何使用   top 


$ top


【Linux】进程优先级 | 进程的切换 | 环境变量详解_服务器_14

【Linux】进程优先级 | 进程的切换 | 环境变量详解_优先级_15

【Linux】进程优先级 | 进程的切换 | 环境变量详解_linux_16

然后会询问:PID to renice [default pid=1] 问你要设置哪个进程的 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_优先级_17


【Linux】进程优先级 | 进程的切换 | 环境变量详解_运维_18


这里居然提示  Failed  修改失败了!Permission denied (么得权限) !

【Linux】进程优先级 | 进程的切换 | 环境变量详解_linux_19

 因为我们刚才说过:

"一个进程的优先级不能轻易被修改,因为会打破调度器的平衡"

如果你执意修改,你须具备 👑 超级用户 的权限 —— 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_20

 !这里我们 sudo top就行:

$ sudo top


 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_服务器_21


【Linux】进程优先级 | 进程的切换 | 环境变量详解_运维_22

 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_优先级_23

 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_24


Ⅱ. 进程的切换(Process Switch)

0x00 竞争与独立

竞争性:僧多粥少!系统进程数目众多,而 CPU 资源只有少量,甚至一个,所以进程之间是具有竞争属性的。为了高效的完成任务、更合理竞争相关资源,便具有了优先级。

独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰。

进程运行具有独立性,不会因为一个进程挂掉或者异常而导致其它进程出现问题!

内核结构 + 代码和数据

❓ 思考:那么操作系统是如何做到进程具有独立性的呢?

(我们将在后续讲解进程地址空间时揭晓)

0x01 并行和并发

并行:多个进程在多个 CPU 下分割,同时进行运行,我们称之为并行。
并发:多个进程在单个 CPU 下采用进程切换的方式,在一段时间内,让多个进程都得以推进,称之为并发。

下面我们来理解一下并行与并发。

【Linux】进程优先级 | 进程的切换 | 环境变量详解_服务器_25

一般服务器都是双 CPU 的,所以双 CPU 的系统是存在的,就会存在多个进程同时在跑的情况。

如果存在多个 CPU 的情况,任何一个时刻,都有可能有两个进程在同时被运行 —— 并行 

但我们大家接触的、用的笔记本电脑基本都是单核的,单 CPU 的任何时刻只允许一个进程运行。

我的电脑是单 CPU 的,但是我的电脑中有各种进程都可以在跑啊?怎么肥事啊?

它是怎么做到的呢?

【Linux】进程优先级 | 进程的切换 | 环境变量详解_linux_26

不要认为进程一旦占有 CPU,就会一直执行到结束,才会释放 CPU 资源。

所以一直让它跑,直到进程执行完,是不存在的,我们遇到的大部分操作系统都是 分时 的!

操作系统会给每一个进程,在一次调度周期中,赋予一个 时间片 的概念。

例:一秒钟之内每一个进程至少要被调度20次,每一次调度就是自己代码得以推进的时候。

在一个时间段内,多个进程都会通过 "切换交叉" 的方式,当多个进程的代码,在一段时间内都得到推进 ——  并发

0x02 进程抢占

❓ 思考:OS 就是简单的根据队列来进行前后调度的吗?有没有可能突然来了一个优先级更高的进程?

抢占式内核!我们现在的计算机基本都是支持 抢占 的。正在运行的低优先级进程,可能正在享受着它的时间片、推进着代码,但是如果来了优先级更高的进程,我们的调度器会直接把对应的进程从 CPU 上剥离,放上优先级更高的进程,这个操作就叫做 进程抢占

了解

a. 不允许不同优先级的进程存在的 
b. 相同优先级的进程,是可能存在多个的

task_struct* queue[5];

根据不同的优先级,将特定的进程放入不同的队列中!这其实就是一张简单的哈希表,后面列入的都是队列,其原理是通过哈希根据不同的哈希值确定队列的优先级,每一种优先级 Linux 都会维护一个队列。

举个最简单的例子,下面的 z 是如何得到已经释放的临时变量 a 的数据的?

int func() {
    int a = 10 + 20;
    return a;
}

int z = func();

【Linux】进程优先级 | 进程的切换 | 环境变量详解_linux_27

寄存器分为 可见寄存器 不可见寄存器

当进程在被执行的过程中,一定会存在大量的临时数据,会暂存在 CPU 内的寄存器中。

寄存器上数据的重要性:

我们把进程在运行中产生的各种寄存器数据,我们叫进程的硬件上下文数据。

  • 当进程被剥离:需要保存上下文数据
  • 当进程恢复时:需要将曾经保存的上下文数据恢复到寄存器中。

上下文在哪里保存?task_struct !

📌 注意事项:要准确区分,"寄存器" 和 "寄存器里的数据" 的区别。

  • 寄存器只有一套,但是寄存器里的数据有多份。

Ⅲ. 环境变量(Environment Var)

0x00 引入:思考一个问题

【Linux】进程优先级 | 进程的切换 | 环境变量详解_服务器_28

❓ 思考:为什么我们的代码运行要带路径,而系统的指令不用带路径?

【Linux】进程优先级 | 进程的切换 | 环境变量详解_linux_29

如果我们直接输入我们的可执行程序,会显示 bash: process: command not found 

我们说过,执行系统的指令实际上也是程序,系统的指令你也是可以带上路径的:

【Linux】进程优先级 | 进程的切换 | 环境变量详解_linux_30

其实,我们可以通过它的报错 "command not found" 发现些什么!

要执行一个可执行程序,前提是要先找到它。

现在我们的问题就可以转化成:为什么系统的命令能找到,而我们自己的程序找不到?

💡 真相

系统中是存在相关的 环境变量,保存了程序的搜索路径的!

为什么我们的代码运行要带路径,而系统的指令不用带?其本质是由环境变量

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_31

 引起的!

0x01 环境变量 PATH 

我们可以通过 env 指令查看环境变量:

$ env

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_32

这些变量每一个都有它特殊的用途,系统中搜索可执行程序的环境变量叫做 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_31


我们可以通过 grep 去抓一下:

【Linux】进程优先级 | 进程的切换 | 环境变量详解_服务器_34

如何查看环境变量的内容?我们可以使用 echo  去显示:

$ echo $PATH

 在 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_31

 前加上 $

【Linux】进程优先级 | 进程的切换 | 环境变量详解_linux_36

环境变量 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_31

 中会承载多种路径,中间用冒号 ( : ) 作为分隔符。

我们再执行某一个程序时,比如执行 ls 时,我们的系统识别到 ls 的输入时,会在上面路径中逐个搜索,只要在特定的路径下找到了 ls,就会执行特定路径下的 ls 并停止搜索。

换言之,

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_31

 就提供了环境变量,可执行程序搜索的路径。我们的 ls 在  usr/bin  路径下,这说明当前的 ls 在 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_31

 中是可以被找到的,

所以执行 ls 的时侯自然可以不带路径,所以我们自己的  process 不带路径自然就不能执行。

因为当前的  process 所在的路径并没有这里的环境变量,程序在搜索的时侯找了路径也没有找到你这个可执行程序,搜索完找不到,自然就报 "command not found" 了。

0x02 实践:让自己的可执行程序不带路径也能执行

那我现在就想让我的可执行程序 process 不带路径直接执行起来,可以吗?

可以!我们先讲述一种简单粗暴的方式,直接把我们的可执行程序 cp 拷贝到系统的路径中:

$ sudo cp process /usr/bin/

既然系统的所有命令都在 usr/bin 路径下,那我们把我们的 process

实际上,刚才那个操作我们可以称之为 "软件被安装到系统上",但是我们不建议你去自己安装。

也更不建议你将你的指令拷贝到 Linux 系统路径下,因为这会污染 Linux 下的命令池。

经常这么干时间久了你可能都忘了这个是干什么的,半年之后:

"诶!我这系统里怎么还有个 process?"

可能就分不清是你写的还是系统的了,所以我们不建议这么做!

更好的方式是将 process

前置:在 Linux 命令行中,我们也是可以定义变量的,命令行变量分为两种:

  • 普通变量
  • 环境变量(具备全局属性)

【Linux】进程优先级 | 进程的切换 | 环境变量详解_运维_40

命令行上直接写,变量名等于值,你所定义的这个变量 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_41

,就是 本地变量

(我们这里先对本地变量做一个小小的理解,稍后我们还会讲解的)

用系统查看环境变量的命令 env去查看一下这个本地变量,会发现根本找不到,

因为它不以环境变量的形式存在,但是它是存在的!

如果你想让一个变量变成环境变量,你可以通过 export 导出一个在系统中可以查看的环境变量:

$ export []=[]

通过 env 并 grep 一下这个变量,我们就能找到我们导出的环境变量了:

【Linux】进程优先级 | 进程的切换 | 环境变量详解_linux_42

(至于环境变量和本地变量之间的差别,我们稍后再讲)

现在我们知道该如何导环境变量了,现在我想执行我的程序不想再带路径该怎么办呢?

💭 操作演示:下面我们来做个好玩的:

把我们的环境变量,当前路径导入到 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_31

 路径中看看会发生什么:

$ export PATH=[路径]

这么一导之后,我们发现我们的 process

【Linux】进程优先级 | 进程的切换 | 环境变量详解_优先级_44

但是好像我们的系统指令全都寄了!!!

【Linux】进程优先级 | 进程的切换 | 环境变量详解_服务器_45

【Linux】进程优先级 | 进程的切换 | 环境变量详解_优先级_46

 啊这,怎么会这样呢?!因为你把 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_31

 里的环境变量都搞没了,只剩你自己的路径了,所以这些指令自然都找不到了。

出现了你刚才自己可执行程序不带路径后 Enter 的报错 "command not found" 。

【Linux】进程优先级 | 进程的切换 | 环境变量详解_优先级_48

 这……难道环境污染了(紧张)?我的要服务器坏掉了(害怕)?

不用担心!在命令行上设置的环境变量是具有临时性的,只在你登陆期间有效。

你刚才的修改只是在内存中的修改,不会修改系统当中的相关配置文件。

所以你只需要关掉重开就行了,随便搞,不会影响。

如果你想让你的环境变量设置永久有效的话,是需要更改配置文件的,该配置文件在系统当中,跟云服务器没有关系。

那我们该怎么做呢?来,这么做:

$ export PATH=$PATH:[路径]

【Linux】进程优先级 | 进程的切换 | 环境变量详解_优先级_49

0x03 环境变量的导入和解除

刚才我们通过 export 去导入变量,如果想取消一个变量,就可以使用 unset 来取消变量设置:

【Linux】进程优先级 | 进程的切换 | 环境变量详解_运维_50

此时我们使用 unset 环境变量,就可以解除 foo:

【Linux】进程优先级 | 进程的切换 | 环境变量详解_优先级_51

" 这些东西实际上都是 shell 命令,export 是导出,unset 是取消 "

0x04 介绍几个常见的环境变量

刚才我们介绍了环境变量 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_31

,它是用来指定命令的搜索路径的。 

下面我们来详细介绍一下常见的环境变量,刚才我们就是用 env 指令去查看环境变量的:

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_32

我们能看到有很多环境变量,比如下面这个 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_linux_54

 就是表示 "对应这台主机的主机名" 。

【Linux】进程优先级 | 进程的切换 | 环境变量详解_linux_55

我们同样也是可以通过 echo 指令带上 $

echo $HOSTNAME

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_56

再比如 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_服务器_57

,它可以告诉你你的 shell 在哪里,通常是 /bin/bash 

echo $SHELL

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_58

得益于 Linux 存在历史命令的记录功能,我们可以在 Xshell 里 ↑ ↓ 显出历史命令,就像这样: 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_运维_59

"总不能一直记吧?肯定是有个指令记录的阈值的!"

没错!Linux 最多允许你记录的历史命令条数是 3000。

而我们接下来要介绍的 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_运维_60

  (History Size),就是定义一共记录多少历史指令的环境变量:

$ echo $HISTSIZE

【Linux】进程优先级 | 进程的切换 | 环境变量详解_服务器_61

顺便一提,我们可以通过 history 命令令去查看我们历史敲过的所有命令:

【Linux】进程优先级 | 进程的切换 | 环境变量详解_优先级_62

【Linux】进程优先级 | 进程的切换 | 环境变量详解_服务器_63

 再来!我们再说几个 ~我们一般是通过 whoami 指令去查看当前是谁正在使用系统的,而 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_服务器_64

 就记录了当前谁在用。

$ echo $USER

 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_优先级_65

不知道大家有没有关注过,每次登陆服务器默认所处的路径?就是默认所处的工作目录。

root 用户的工作目录和普通用户的工作目录不同,那 Linux 是如何知道的呢? 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_66

:指定用户的主工作目录(即用户登陆到 Linux 系统中时,默认的目录)

【Linux】进程优先级 | 进程的切换 | 环境变量详解_环境变量_67

当然还有很多,比如 

【Linux】进程优先级 | 进程的切换 | 环境变量详解_运维_68

 记录了谁登的服务器、地址、端口号等。

(环境变量实在多,全部讲完不太现实,上面我们讲的都是一些常用的,欢迎补充)

0x05 尾记

命令行中启动的进程,父进程全部都是 bash 。

环境变量具有全局属性,环境变量是会被子进程继承下去的。

所谓的本地变量,本质就是在 bash 内部定义的变量,不会被子进程继承下去。

Linux 下大部分命令都是通过子进程的方式执行的,但是还有一部分命令不通过子进程的方式执行,而是由 bash 自己执行(调用自己的对应的函数来完成特定的功能,比如 cd 命令),我们把这种命令叫做 内建命令

【Linux】进程优先级 | 进程的切换 | 环境变量详解_运维_69


📌 [ 笔者 ]   王亦优
📃 [ 更新 ]   2022.3.
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,
              本人也很想知道这些错误,恳望读者批评指正!


📜 参考资料 

C++reference[EB/OL]. []. http://www.cplusplus.com/reference/.

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

百度百科[EB/OL]. []. https://baike.baidu.com/.

比特科技. Linux[EB/OL]. 2021[2021.8.31