欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 建筑 > (学习总结30)Linux 进程优先级、进程切换和环境变量

(学习总结30)Linux 进程优先级、进程切换和环境变量

2025/3/26 16:17:56 来源:https://blog.csdn.net/qq3304968099/article/details/146425837  浏览:    关键词:(学习总结30)Linux 进程优先级、进程切换和环境变量

Linux 进程优先级、进程切换和环境变量

  • 进程优先级
    • 基本概念
    • 查看系统进程
    • PRI 和 NI 解释
    • 进程优先级调整
      • 命令行调整进程优先级
        • 调整新进程调度优先级命令 nice
        • 调整已运行进程调度优先级命令 renice
      • 使用 top 调整进程优先级
      • 使用系统调用调整进程优先级
    • 进程的竞争、独立、并行、并发概念
  • 进程切换
    • Linux 内核进程调度队列
    • 优先级处理
    • 活动进程队列
    • 过期进程队列
    • active 指针和 expired 指针
    • 总结
  • 环境变量
    • 基本概念
    • 查看或设置环境变量相关操作
      • 显示当前环境变量或在指定环境运行程序命令 env
      • 删除环境变量、shell 变量 或 函数命令 unset
      • 其它命令
    • 常见环境变量
      • PATH 作用
      • HOME 作用
    • 代码层面操作环境变量
      • main 函数命令行与环境参数(非标准扩展)
      • 使用 environ 全局变量
      • 使用 getenv 函数
    • 环境变量特性与其它

以下代码环境为 Linux Ubuntu 22.04.5 gcc C语言

进程优先级

基本概念

进程优先级:获得 CPU 资源分配的先后顺序,也可以说是进程的优先权(priority)。

进程优先级是基于时间片的分时操作系统,每个进程规定使用 CPU 的时间,时间结束后切换到其它进程使用。

进程优先级出现的本质是 CPU 资源相对于进程数量的稀缺,导致多个进程需要通过优先级确认谁先谁后的情况。

优先权高的进程有优先执行权利,配置进程优先权对多任务环境的 Linux 很有用,可以改善系统性能。

还可以把进程运行到指定的 CPU 上,把不重要的进程安排到某个 CPU,可以大大改善系统整体性能。

当然,进程的优先级可能会变化,但幅度不会太大,不然会导致优先级低的进程长时间得不到 CPU 资源,形成 进程饥饿

查看系统进程

在 Linux 系统中,用 ps ‒l 命令则会类似输出以下几个内容:
在这里插入图片描述
我们很容易注意到其中的几个重要信息:

  • UID : 代表执行者的身份

  • PID : 代表这个进程的代号

  • PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号

  • PRI :代表这个进程可被执行的优先级,其值越小越早被执行

  • NI :代表这个进程的 nice 值

另外用户访问文件或资源的时候,都是使用进程访问,进程代表用户,查看当前用户权限其实是查看进程的权限。

PRI 和 NI 解释

PRI(Priority) 即进程的优先级,通俗点说就是程序被 CPU 执行的先后顺序,此值越小进程的优先级别越高。

而 NI 意思为 nice 值,其表示进程可被执行的优先级的修正数值。

PRI 值越小越快被执行。加入 nice 值后,进程优先级公式为PRI(new) = PRI(old) + nice

Linux 为方便计算,PRI(old) 默认设置为 80,每次只能调整 nice 值。而 nice 值的取值范围为 [ -20, 19 ],一共 40 个级别。当 nice 值为负值时,该程序优先级值将变小,反之变大。

则可以明确的是,Linux 进程的优先级范围为 [ 60, 99 ]。

所以在 Linux 下调整进程优先级,就是调整进程的 nice 值。

需要强调的是,进程的 nice 值不是进程的优先级,两者不是一个概念,但是进程 nice 值会影响到进程的优先级变化,可以理解 nice 值是进程优先级的修正数据。

进程优先级调整

我们可以从三个方面调整进程的优先级。

命令行调整进程优先级

调整新进程调度优先级命令 nice

语法:nice [选项] [命令]
功能:启动一个进程并设置其 nice 值,从而调整进程的调度优先级。

常用选项:

  • -n :设置 nice 值为 n,如果未指定,默认为 10(只有 root 账号能使用负优先级)。
  • --help :显示帮助信息。
  • --version :显示版本信息。

[命令]:代表执行的具体命令或可执行文件

调整已运行进程调度优先级命令 renice

语法:renice [优先级] [选项] [PID]
功能:调整已经运行的进程 nice 值,从而改变进程的调度优先级。

常用选项:

  • -n :指定进程新的 nice 值 n(只有 root 账号能下降优先级的值)
  • -p [PID]:指定进程的 PID 调整
  • -u [用户名] :指定用户名的所有进程
  • -g [进程组] :指定进程组的所有进程
  • --help :显示帮助信息
  • --version :显示版本信息

使用 top 调整进程优先级

使用 top 命令进入其界面后,按 r -> 输入进程 PID -> 输入 nice 值,即可调整进程优先级。

使用系统调用调整进程优先级

通过 man 手册可以查看进程优先级系统调用:
在这里插入图片描述
我们可以用代码测试一下:

#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>int main()
{int ret = fork();           // ret 同时接收 fork 返回的两个值if (ret < 0)    {   perror("fork");         // 小于 0 表示调用失败return 0;}   else if (ret == 0){   printf("子进程当前优先级 %d\n", getpriority(PRIO_PROCESS, getpid()));setpriority(PRIO_PROCESS, getpid(), 15);printf("子进程修改之后的优先级 %d\n", getpriority(PRIO_PROCESS, getpid()));while(1);}   else{   printf("父进程当前优先级 %d\n", getpriority(PRIO_PROCESS, getpid()));  setpriority(PRIO_PROCESS, getpid(), 9); printf("父进程修改之后的优先级 %d\n", getpriority(PRIO_PROCESS, getpid()));  while(1);                                                                                                                                                            }   return 0;
}

在这里插入图片描述

进程的竞争、独立、并行、并发概念

竞争性:系统进程数目众多,而 CPU 资源只有少量,甚至 1 个。则进程之间出现竞争属性是不可避免的。为了高效完成任务,更合理竞争相关资源,便出现优先级概念。

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

并行:多个进程在多个 CPU 下分别同时运行。如:两个进程在自己对应 CPU 下运行,此时两者为并行。

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

进程切换

CPU 上下文切换,其实际含义是任务(任务 == 进程)切换,或者 CPU 寄存器切换。

当多任务内核决定运行另外的任务时,它保存正在运行任务的当前状态,也就是 CPU 寄存器中的全部内容。这些内容被保存在任务自己的堆栈中,入栈工作完成后就把下一个将要运行的任务的当前状况从该任务的栈中重新装入 CPU 寄存器,并开始下一个任务的运行,这一过程就是 context switch
在这里插入图片描述
当代计算机都是分时操作系统,每个进程都有它合适的时间片(其实就是一个计数器)。时间片到达,进程就被操作系统从 CPU 中剥离下来。

我们可以参考 Linux 内核 0.11 版本:
在这里插入图片描述
在这里插入图片描述
进程切换最核心的是保存和恢复当前进程的硬件上下文的数据,即 CPU 内寄存器的内容。

Linux 内核进程调度队列

以下是 Linux 2.6 内核中进程队列的数据结构简化图:
在这里插入图片描述

Linux 对每个 CPU 分配一个 runqueue ,如果有多个 CPU 就会考虑进程个数的负载均衡问题。当一个 CPU 的 cpu_load 过高,会将部分进程分担给其它 CPU 来优化效率。

Linux 不仅是一个分时操作系统,里面也集成了实时操作(实时优先级),其功能通过条件编译裁剪。实时操作具体在应用领域,这里省略。

优先级处理

其中的 queue[140] 的类型为指针数组,140 个元素类型都是 task_struct*,用于指向 task_struct。

实时优先级:0〜99(这里不讲解)

普通优先级:100〜139。

我们使用的都是普通的优先级,nice 值的取值范围刚好与之对应。

活动进程队列

时间片还没有结束的所有进程都按照优先级放在活动进程队列中

nr_active 表示总共有多少个运行状态的进程。

queue 访问方式:

queue[140] 中一个元素就是一个进程队列,相同优先级的进程按照 FIFO 规则进行排队调度,所以数组下标就是优先级。访问 queue 对应的下标只需通过计算 下标 = 优先级 - 60 + (140 - 40) 即可。

queue 查询:

从该结构中选择一个最合适的进程,并不会遍历 queue,而是使用 bitmap[5]。

140 个优先级,则有 140 个进程队列,为了提高查找非空队列的效率,Linux 采用 5 * 32 个比特位表示队列是否为空,对 queue 的遍历转为对位图 bitmap[5] 的查询,便可以大大提高查找效率。

新进程加入

当一个新进程分配完数据后,Linux 会根据此进程的优先级分配到活动队列对应下标的 task_struct* 队列,从而实现了进程优先级高的先使用 CPU 的资源。

进程时间片结束

当进程时间片适用完,Linux 会将此进程转移到过期进程队列。这样的处理方式更易维护。

过期进程队列

过期进程队列和活动进程队列结构一模一样,但 CPU 是不会调度其中的进程的。

而过期队列上放置的进程,时间片都耗尽了,直到活动队列上的进程都被处理完毕,才会对过期队列的进程进行时间片重新计算。

active 指针和 expired 指针

active 指针永远指向活动队列,expired 指针永远指向过期队列

随着 CPU 调度 active 指向的活动队列进程,活动队列上的进程会越来越少,过期队列上的进程也会越来越多。

当活动队列没有进程了,只需要交换 active 指针和 expired 指针的内容,就可以快速具有一批新的活动进程!

总结

Linux 的进程优先级调度方案本质是从数据结构的哈希桶中获取,添加、移除和调用都是 O(1) 时间。则可以说,在 Linux 中查找一个最合适调度的进程的时间复杂度是一个常数,不随着进程增多而导致时间成本增加,称之为进程调度 O(1) 算法

环境变量

基本概念

环境变量(environment variables) 一般是指在操作系统中用来指定操作系统运行环境的一些参数

如:我们编写 C/C++ 代码链接时,编译器不知道我们的所链接的动态静态库在哪里,但是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找。

环境变量通常具有某些特殊用途,在系统当中一般具有全局特性。

查看或设置环境变量相关操作

显示当前环境变量或在指定环境运行程序命令 env

语法:env [选项] [名称=值] ... [命令 [参数]...]
功能:显示当前的环境变量,或在指定的环境中运行程序。

常用选项:

  • -i :启动一个清除所有环境变量的新环境
  • -u :删除指定的环境变量

其它操作:

  • env :表示只打印当前 shell 环境变量

删除环境变量、shell 变量 或 函数命令 unset

语法:unset [选项] [变量名或函数名]
功能:删除环境变量、shell 变量 或 函数。

常用选项:

  • -v :删除变量(默认行为)
  • -f :删除函数

其它命令

使用 set 命令不仅可以打印当前 shell 环境变量,还可以打印本地变量等。

使用 echo $[环境变量名] 可以查看单独一个环境变量。

使用 export [变量名]=[值] 可以设置一个环境变量。

使用 set [变量名]=[值] 可以设置一个本地变量。

使用 export [变量名] 可以将本地变量提升为环境变量,供子进程使用。

常见环境变量

PATH :指定命令的搜索路径。

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

SHELL :当前使用的 shell,它的值通常是 /bin/bash。

PWD :当前工作目录。

OLDPWD :上一次所处目录。

LOGNAME :登录时的用户名。

LANG :系统的默认语言和区域设置。

LS_COLORS :定义 ls 命令输出文件和目录的颜色。

SSH_CONNECTION :当前 SSH 连接的源和对应的网络信息。

SSH_TTY :表示当前 SSH 会话关联的终端设备。

USER :表示当前用户。

HISTSIZE :当前 shell 会话中历史命令列表的最大数量。

HOSTNAME :当前系统的主机名。

PATH 作用

我们注意到,系统命令可以直接执行不需要带路径,而我们的二进制程序需要带路径才能执行。

这是因为执行命令会在 PATH 标明的路径进行查找,找到执行,反之报错。

一种方法是将我们的执行文件放入 /bin 文件中,但这个可执行文件不确定有没有 bug,会污染系统执行文件。

保险的方法是将对应执行文件的路径加入到 PATH 中,使用 PATH=$PATH:[当前文件所在绝对路径] 可以在内存级别记录路径。

如果想在用户级别存储路径,在当前用户家目录的 .bashrc 文件末尾加入 export PATH=$PATH:[绝对路径] 即可。

HOME 作用

每个用户的 HOME 都不一样,其记录的是对应用户的家目录,执行 cd ~cd 命令会返回到 HOME 记录的目录。

代码层面操作环境变量

main 函数命令行与环境参数(非标准扩展)

在 C语言 的 main 函数中其实是有参数的,分别为 int argcchar *argv[]char *env[],分别表示 参数个数参数字符指针数组环境变量字符指针数组

#include <stdio.h>int main(int argc, char* argv[], char* env[])	// argc 和 argv 是一起使用的,env 单独使用
{for (int i = 0; i < argc; ++i)				// argc 代表命令行参数个数{   printf("命令行参数 argv[%d] == \"%s\"\n", i, argv[i]);  // 打印命令行参数}   printf("\n");for (int i = 0; env[i]; ++i)				// env 和 argv 一样是个指针数组,都规定最后一个元素后面为 NULL{   printf("环境变量 env[%d] == \"%s\"\n", i, env[i]);      // 打印环境变量                                                                                              }   return 0;
}

在这里插入图片描述

使用 environ 全局变量

通过第三方变量 environ 获取,在 libc 中定义的全局变量 environ 指向环境变量表,environ 没有包含在任何头文件中,所以在使用时要用 extern 声明:
在这里插入图片描述

#include <stdio.h>extern char **environ;			// 使用指向环境变量的 environ 全局变量                                                                                                                                            int main()
{for (int i = 0; environ[i]; ++i){   printf("env[%d]-> %s\n", i, environ[i]);}   return 0;
}

在这里插入图片描述

使用 getenv 函数

可以在 man 手册中查找 getenv 函数的使用方法:
在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>int main()
{char* PATH = getenv("PATH");if (PATH == NULL)	// 获取失败则退出{   return 1;}   else{   printf("当前 PATH 为 %s\n", PATH);}                                                                                                                                                                        return 0;
}

在这里插入图片描述
三种方式中,使用 getenv 函数比较方便,并且我们可利用环境变量来制作一个只有特定用户才能使用的程序。

putenv 函数也可以访问环境变量。

环境变量特性与其它

环境变量通常具有全局属性,可以被子进程继承下去。在 main 函数中使用的 env 就 " 继承 " 自 bash 进程。

环境变量存储在 bash 中,不同用户 bash 进程对用户有关环境变量设置是不同的。

另外在 bash 中会记录两张表,一个是环境变量表,另一个是本地变量表。

对于本地变量,直接在命令行中使用 [变量名]=[值] 即可创建本地变量,本地变量不会传给子进程。

一般情况下,子进程创建的环境变量父进程是接收不到的,而 export 命令是一个内建命令(built-in command),会让 bash 自己亲自执行或让系统来调用。

如何做到 C语言 main 函数参数可变?main 函数被执行之前会先执行一个函数 _start ,其会统计 main 函数中的参数,条件编译选择对应参数的 main 函数即可。但注意, gcc 支持第三个参数 env 获取环境变量,但这是非标准扩展,在其它编译器中可能会报错。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词