(学习总结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 获取环境变量,但这是非标准扩展,在其它编译器中可能会报错。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/38829.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《Manus学习手册》.pdf(文末附完整版下载地址)

大家好&#xff0c;我是吾鳴。 吾鳴今天要给大家分享的一份比较全面详细的Manus学习手册&#xff0c;该学习手册主要包含Manus产品概述与核心理念、Manus功能与使用场景、Manus技术架构与工作流、Manus案例库与用户实践、邀请码获取与内测信息、Manus与传统AI对比与优势、用户评…

【MySQL】从零开始:掌握MySQL数据库的核心概念(三)

人生碌碌&#xff0c;竞短论长&#xff0c;却不道枯荣有数&#xff0c;得失难量。 前言 这是我自己学习MySQL数据库的第二篇博客总结。后期我会继续把MySQL数据库学习笔记开源至博客上。 上一期笔记是关于MySQL数据库的数据类型&#xff0c;没看的同学可以过去看看&#xff1a…

Web3智能合约与数据交互安全性探讨

Web3智能合约与数据交互安全性探讨 随着区块链技术的飞速发展&#xff0c;Web3的概念已经成为技术圈的热门话题。Web3不仅仅是技术迭代&#xff0c;它代表了一种全新的互联网交互方式&#xff0c;其中智能合约扮演着核心角色。智能合约是自动执行、控制或文档化法律事件和行动…

人工智能赋能山西乡村振兴:智能空间规划与可持续发展

摘要&#xff1a;随着人工智能技术的快速发展&#xff0c;山西乡村振兴面临着从传统农业到智能化现代化转型的重大机遇。本文探讨了人工智能在山西乡村振兴中的具体应用&#xff0c;重点分析了智能空间规划、生态保护与环境治理、产业转型以及基础设施升级的可能路径。文章从数…

QT三 自定义控件

一 自定义控件 现在的需求是这样&#xff1a; 假设我们要在QWidget 上做定制&#xff0c;这个定制包括了关于 一些事件处理&#xff0c;意味着要重写QWidget的一些代码&#xff0c;这是不实际的&#xff0c;因此我们需要自己写一个MyWidget继承QWidget&#xff0c;然后再MyWi…

【C++ 进阶】语句:从基础到实践

目录 一、输入输出体系的范式革命 1.1 C语言的格式化 1.2 C的流抽象革命 二、字符串处理的抽象跃迁 2.1 C语言的字符指针 2.2 C的string类革命 三、结构体到类的类型系统进化 3.1 C语言的结构体局限 3.2 C类的革命性演进 四、基础控制语句差异 4.1 条件语句&#xf…

C语言操作符

&#x1f31f; 各位看官好&#xff0c;我是maomi_9526&#xff01; &#x1f30d; 种一棵树最好是十年前&#xff0c;其次是现在&#xff01; &#x1f680; 今天来学习C语言的相关知识。 &#x1f44d; 如果觉得这篇文章有帮助&#xff0c;欢迎您一键三连&#xff0c;分享给更…

PostgreSQL:语言基础与数据库操作

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

KMP算法

KMP算法 为什么叫做KMP呢。 因为是由这三位学者发明的&#xff1a;Knuth&#xff0c;Morris和Pratt&#xff0c;所以取了三位学者名字的首字母。所以叫做KMP next数组就是一个前缀表&#xff08;prefix table&#xff09;。 前缀表是用来回退的&#xff0c;它记录了模式串与…

3D点云数据处理中的聚类算法总结

1.欧式聚类&#xff1a; 基于点的空间距离&#xff08;欧几里得距离&#xff09;来分割点云&#xff0c;将距离较近的点归为同一簇。 欧式聚类需要的参数&#xff1a;邻域半径R,簇的最小点阈值minPts&#xff0c;最大点数阈值maxPts。 实现效率&#xff1a; O(n * log n) 实现…

WRC世界机器人大会-2024年展商汇总

2024世界机器人大会 时间&#xff1a;2024年8月21日至25日 地点&#xff1a;北京经济技术开发区北人亦创国际会展中心 大会主题&#xff1a;共育新质生产力&#xff0c;共享智能新未来 2024世界机器人博览会亮点纷呈&#xff0c;20余款人形机器人整机将亮相博览会&#xff…

拉取镜像,推送到阿里云镜像仓库

需求背景&#xff1a;在学习k8s&#xff0c;虚拟机无法正常拉取 wangyanglinux/tools:busybox 镜像。 解决办法&#xff1a;将墙外镜像拉到国内&#xff08;阿里云&#xff09;再使用 准备工作需要创建对应的镜像仓库&#xff0c;然后再进行推送 1. 拉取镜像 docker pull …

DeepSeek和Kimi在Neo4j中的表现

以下是2个最近爆火的人工智能工具&#xff0c; DeepSeek:DeepSeek Kimi: Kimi - 会推理解析&#xff0c;能深度思考的AI助手 1、提示词&#xff1a; 你能帮我生成一个知识图谱吗&#xff0c;等一下我会给你一篇文章&#xff0c;帮我从内容中提取关键要素&#xff0c;然后以N…

哈尔滨工业大学DeepSeek公开课人工智能:大模型原理 技术与应用-从GPT到DeepSeek|附视频下载方法

导 读INTRODUCTION 今天继续哈尔滨工业大学车万翔教授带来了一场主题为“DeepSeek 技术前沿与应用”的报告。 本报告深入探讨了大语言模型在自然语言处理&#xff08;NLP&#xff09;领域的核心地位及其发展历程&#xff0c;从基础概念出发&#xff0c;延伸至语言模型在机器翻…

redis解决缓存穿透/击穿/雪崩

文章目录 1.缓存穿透1.1 概念1.2 解决方案1.2.1 缓存空对象1.2.2 布隆过滤 1.2 店铺查询使用缓存穿透解决方案1.2.1 流程 2.缓存雪崩2.1 什么是缓存雪崩&#xff1f;2.2 雪崩解决方案 3.缓存击穿3.1 什么是缓存击穿&#xff1f;3.2解决方案3.2.1 基于互斥锁解决缓存击穿问题&am…

不连续平面提取

不连续平面提取 提取流程 #mermaid-svg-Y87uP8WsVRmPYriG {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Y87uP8WsVRmPYriG .error-icon{fill:#552222;}#mermaid-svg-Y87uP8WsVRmPYriG .error-text{fill:#552222;s…

大语言模型-2.2/3-主流模型架构与新型架构

简介 本博客内容是《大语言模型》一书的读书笔记&#xff0c;该书是中国人民大学高瓴人工智能学院赵鑫教授团队出品&#xff0c;覆盖大语言模型训练与使用的全流程&#xff0c;从预训练到微调与对齐&#xff0c;从使用技术到评测应用&#xff0c;帮助学员全面掌握大语言模型的…

数据库操作练习

一.向heros表中新增一列信息&#xff0c;添加一些约束&#xff0c;并尝试查询一些信息 //向表中添加一列age信息 alter table heros add column age int;//id列添加主键约束&#xff0c;设置自增 alter table heros modify column id int auto_increment primary key;//name列…

CTF【WEB】学习笔记1号刊

Kali的小工具箱 curl www.xxx.com&#xff1a;查看服务器响应返回的信息 curl -I www.xxx.com:查看响应的文件头 一、cmd执行命令 ipconfig&#xff1a;ip地址配置等&#xff1b; 二、 Kali操作 1.sudo su&#xff1b; 2.msfconsole 3.search ms17_010 永恒之蓝&#xff…

在 SaaS 应用上构建 BI 能力的实战之路

SaaS 产品在持续运营过程中积累了大量数据&#xff0c;这些数据不仅是数字的记录&#xff0c;更是洞察市场趋势、优化产品功能、提升用户体验的宝贵资源。 因此&#xff0c;大部分的 SaaS 产品在发展到一定阶段后&#xff0c;都会开始构建自己的报表模块或分析模块&#xff0c;…