Linux学习:进程(3)与 环境变量

目录

  • 1. 进程的优先级
    • 1.1 什么是进程的优先级
    • 1.2 优先级的具体表示与查看方式
  • 2. 进程的切换与调度
    • 2.1 切换
    • 2.2 调度
  • 3. 环境变量
    • 3.1 main参数/命令行参数
    • 3.2 什么是环境变量
    • 3.3 环境变量的使用与特性
    • 3.5 本地变量与环境变量的脚本配置文件

1. 进程的优先级

  1. 在计算机运行的过程中,有着许许多多的需要被执行的进程,而就绪状态的进程被加载到内存中的运行队列上。那么,它们之间的先后是如何区分的,它们是照申请先后的顺序排列而后执行吗?接下来,让我们对进程的新属性,进程的优先级来进行学习与了解。

1.1 什么是进程的优先级

  1. 在现实生活中,各种事物的优先级代表着这些事物所要被执行的先后顺序。而在计算机中对于进程来说,也是如此,进程的优先级决定了其被CPU执行的先后顺序。
  2. 当我们去谈论优先级的概念时,就代表有了需要我们去讨论优先级的前提与场景,进程的优先级决定着进程被CPU的执行顺序。
  3. 而当我们提及到进程的优先级时,就一定代表着CPU不能够第一时间内处理所有需要被进行的进程,或者可以说CPU的资源无法满足支持可以第一时间处理每个进程,所以,此时就需要将各个进程进行排队等待。
  4. 内存中需要被执行的进程各种各样,我们应该怎么将它们进行排队,并且需要保证排队的方式足够合理与高效,可以支持操作系统与各个进程的正常运行。

1.2 优先级的具体表示与查看方式

  1. 优先级也属于进程PCB属性中的一个,我们使用指令ps -la [进程号],可以查看到指定进程的优先级等相关信息。(PRI:priority)

在这里插入图片描述

  1. 进程的优先级表示,PCB中以int类型的变量来表示进程的优先级,其可被修改的数据范围为 [60,99],一共40个数,数值越小优先级越高,数值越大优先级越低。
  2. Linux下我们创建的每个进程的优先级默认为80,每个进程的优先级是可以被我们手动更改的,进程优先级计算判定由两部分组成old PRI + NI(nice)
  3. 我们无法直接修改PRI,需要通过修改NI从而达到间接修改PRI的效果,修改指令为renice [n] [进程号],n的取值范围为 [-20, 19],每次计算的old PRI都是从默认PRI即80开始计算的,而每次对NI的写入都是覆盖式写入。

在这里插入图片描述

  1. 当使用renice指令调整NI的范围超过[-20, 19]时,renice只会取极值不会超过NI的限制范围。

补充:为什么要对能够修改的优先级划定范围

  1. 当进程优先级的优先级可以被随意修改时,可能会因为认为的操作出现大量的高优先级的进程,这些进程会大量占用CPU的资源,甚至可能会导致原本的进程因此无法被CPU执行,导致这些进程及其卡顿甚至不能够被运行。
  2. 这些因为高优先级泛滥问题而不能够被正常执行的进程,我们称之为饥饿进程

2. 进程的切换与调度

2.1 切换

  1. CPU具体是如何处理一个个进程的,每一个进程都是在CPU上一直被执行,直到彻底执行完毕吗?
  2. 事实并给如此,Linux操作系统中进程的执行方式是以时间片进行切换,每个进程都以时间片(1ms)为单位进行轮转执行。
  1. 进程相关概念补充:
    <1> 竞争性:进程会因为优先级的不同区分先后顺序
    <2> 独立性:各个进程之间不会相互干扰,独享各种资源
    <3> 并行:计算机中有多个CPU,各个进程被分散在不同CPU上同时执行
    <4> 并发:多个进程被一个CPU通过高频进程切换得方式执行,同时得以推进
  2. 多核:CPU有一个控制器,同时拥有多个运算器
  1. CPU存在着大量的寄存器,不同种类的寄存器担任着不同的职能:
    <1> eax/ebx/ecx/edx,通用寄存器:临时性的数据保存
    <2> eds/ecs/fg/gs,段寄存器:衡量区域调用数据的区域
    <3> eip/cr0-cr4,PC指针:记录标识程序执行到的进度与位置
    <4> 程序状态字
    <5> 浮点数存储器
    <6> ebp/esp(小盒子),函数栈帧开辟相关
  2. 根据前面的了解,我们直到进程是以时间片为单位在CPU上执行的,而进程被执行时会携带着大量的自身数据,这些数据是需要交给CPU进行处理计算的,CPU获取与存储这些数据的方式就是将它们拷贝至自身的寄存器中。(寄存器不等于寄存器内容,一套寄存器可以有多套寄存器内容)
  3. 进程被切换走,中断执行的步骤:
    <1> 剥离寄存器中的核心数据数据
    <2> 将核心数据全部带走,保存至进程的PCB中,记录执行进行执行到的位置,即,保护进程的硬件上下文数据(CPU内部所有的临时数据)
  4. 进程二次切换回来,继续执行的步骤:
    <1> 恢复上下文,覆盖是写入上下文数据
    <2> 从上次执行到的位置继续执行

在这里插入图片描述

2.2 调度

  1. 操作系统按照一定的方式去执行运行队列上的各种进程,这就被称为进程的调度,不同的操作系统对于进程有着不同的调度算法。
  2. 根据操作系统应用场景的不同,按照进程调度算法的偏向不同可以分为两类:
    <1> 分时优先级:操作系统会公平的执行每个进程
    <2> 时时优先级:操作系统对于用户的请求,发送的进程会进行优先处理,高响应(车载系统)
  1. Linux操作系统O(1)调度算法:

在这里插入图片描述

  1. O(1)调度算法操作步骤 与 变量解释:
    <1> struct task_struct* queue[140]:PCB指针数组,进程的可修改优先级范围为40,此数组开放[100, 139]范围的40个下标用来存储不同优先级队列的指针
    <2> int bitmap[5]:int类型数组,其容量大小为5,有40个bit位,每一位都标识着一个优先级运行队列中是否有进程,二进制位为1代表有,0代表没有。通过这种方式,可以位运算的方法快速检测并找到需要被执行有进程的优先级队列
    <3> int nr_active:一个整形变量,标识bitmap中8组优先级队列是否仍有进程剩余,若没有值为0,若有值为非0
    <4> 运行队列中存在着两个相同的结构,分别用来标识活跃队列与过期队列,CPU只会执行活跃队列中的进程,而新进的进程只会添加至过期队列中。
    <5> 通过活跃队列与过期队列的方式,O(1)调度算法很好的规避的优先级队列的进程饥饿问题
    <6> struct q* active:执行活跃队列的指针
    <7> struct q* expried:指向过期队列的指针
    <8> 当活跃队列中的进程执行完毕,active与expried指针就会进行值交换,这样就达到了运行队列切换的效果,又因为只是指针的交换,消耗极小,效率很高

3. 环境变量

3.1 main参数/命令行参数

  1. 从初始接触编程开始我们就识得了main函数,在使用时从来没有给main函数传递过参数,可是,main函数真的没有参数吗
  2. main函数其实存在着三个可以添加的隐藏参数,接下来就让我们来进行对它们的学习
int main(int argc, char* argv[], char* env[])
{return 0;
}
  1. 命令行参数argvargv
    <1> argv:字符串类型的数组,执行可执行程序时,从命令行中以空格为间隔获取字符串
    <2> argv:argv字符串数组的元素个数
int main(int argc, char* argv[])
{int i = 0;for(i = 0; i < argc; i++){printf("%s\n", argv[i]);}return 0;
}

在这里插入图片描述

  1. 我们在前面的学习中已经了解到,操作系统中的一个个指令其实都是可执行程序,而各种指令附带不同的参数选项就有不同的效果,其底层的实现就是利用了命令行参数实现的。类似操作,如下
 int main(int argc, char* argv[])                                                                                                                                                       
{if(argc != 2){printf("waring Usage:\n\t./process [a~c]\n\n");}else if(strcmp(argv[1], "-a") == 0){printf("-a usage\n");}else if(strcmp(argv[1], "-b")== 0 ){printf("-b usage\n");}else if(strcmp(argv[1], "-c") == 0){printf("-cx usage\n");}else{printf("no usage\n");}return 0;}

在这里插入图片描述

3.2 什么是环境变量

  1. 在编程语言中的变量是我们于内存中开辟的一块空间,我们赋予这块空间名称并以此调用这块空间进行各种数据的存储与运算,变量在运行期间也可以开辟。
  2. 环境变量也是变量,同样与编程语言中变量的属性相同,不同的只是环境变量是操作系统从内存中申请,用来存放一些必要的信息资源,系统内置具有特殊用途的变量。
  3. 系统中的环境变量有许多,环境变量的命名方式通常都为大写英文字母前加特殊符号:$环境变量名,我们可以通过指令:echo [环境变量]来查看对应环境内的具体内容。

在这里插入图片描述

  1. 下面罗列几个常见的环境变量:
    <1> $PATH:存放常用资源路径的环境变量
    <2> $USER:存放当前用户名的环境变量
    <3> $PWD:存放当前所处目录路径的环境变量
    <4> $HOME:存放当前用户家目录的环境变量
  1. 我们在前面的学习中了解到,可执行程序要想运行必须要给出可执行程序的绝对或相对路径,让操作系统可以找到对应可执行程序并执行。
  2. 可是,系统中的一些自带指令为什么调用时可以不加路径声明呢,这是因为系统每次启动时bash都会在$PATH环境变量中会默认添加入,系统自带指令的所在路径,这样我们在使用这些指令时系统可以直接从环境变量中拿到自己需要的资源信息。

在这里插入图片描述

  1. 我们若想让自己编写的可执行程序像系统自带指令一样使用,有两种方法:
    <1> 像操作系统登录时每次都会检索并以以此生成环境变量的路径里添加我们的可执行程序(不推荐,开发不完备可能会污染其他文件)
    <2> 向环境变量$PATH中临时添加我们可执行程序所在的路径,退出销毁,指令如下:
PAHT=[需添加路径]:$PATH
//:$PATH不可省,否则会将原有的内容全部覆盖

在这里插入图片描述

3.3 环境变量的使用与特性

  1. main函数的第三个命令行参数char* env[],是一个字符串数组以NULL结尾,其中存储内容为父进程传递给main函数所在可执行程序的环境变量。
int main(int argc, char* argv[], char* env[])
{int i = 0;for(i = 0; env; i++){printf("%s\n", env[i]);}return 0;
}

在这里插入图片描述

  1. 环境变量为全局属性,子进程可以继承父进程的环境变量,bash命令行解释器是我们所创建进程的父进程。
  2. 定义环境变量的方式:export [环境变量名]=[赋值]

在这里插入图片描述

  1. 子进程除开通过传递env环境变量参数列表的方式一次性获取父进程所有环境变量,我们还可以通过C语言库中函数getenv获得指定的环境变量。(stdlib.h头文件中)
//身份识别
int main()
{if(strcmp(getenv("USER"), "zyc") == 0){printf("this is a limited core process\n");}return 0;
}
  1. C语言库中存在着一个第三方变量environ,此变量的是一个二级指针数组,其中以字符串指针的形式存放着bash进程中所有的环境变量。
int main()
{//做符号声明extern char** environ;int i = 0;for( i = 0; environ[i]; i++){printf("%s\n", environ[i]);}return 0;
}

3.5 本地变量与环境变量的脚本配置文件

  1. 环境变量为bash定义,适用于不同应用环境具有特殊用途的信息。而系统中拥有的变量种类不止一种,还有很多其他种类。
  2. 本地变量:一种在当前bash进程内定义,不能被子进程继承只在bash内部有效的变量
  3. 定义本地变量的方式:[变量名]=[赋值],查看本地变量:echo [$本地变量]
  4. <1> 指令:set(查看所有变量,包括环境变量与本地变量)
    <2> 指令:unset(从上下文信息中移除变量)
  1. 经过前面的学习我们已经知道,环境变量是通过bash进程定义出来,我们此退出后重新登录Xshell后,之前定义的环境变量就会消失。
  2. 可是为什么bash自带的环境变量不会消失呢,其实这些环境变量并不是不会消失,而是存储在服务器磁盘中脚本与配置文件中,每次我们登录Xshell重新启动bash进程,都会导入脚本。
  3. 我们若想要让自己定义的环境变量等也可以登录加载,就可以通过在配置文件中添加定义的方式来实现。
  4. 每个用户的配置文件都不同,它们都处于当前用户的家目录下,文件名为.bash_profile,此文件又从bashrc中导入内容。
  5. 在配置文件.bash_profile中添加提示语,可以登陆时提示。

在这里插入图片描述
示例:
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

江协科技STM32:按键控制LED光敏传感器控制蜂鸣器

按键控制LED LED模块 左上角PA0用上拉输入模式&#xff0c;如果此时引脚悬空&#xff0c;PA0就是高电平&#xff0c;这种方式下&#xff0c;按下按键&#xff0c;引脚为低电平&#xff0c;松下按键&#xff0c;引脚为高电平 右上角PA0&#xff0c;把上拉电阻想象成弹簧 当按键…

nacos的各种类型的配置文件 yml 、json、 Properties、 text 等文件类型 发生变化怎么热更新,实现实时监听nacos配置文件变化

本文用的是 Nacos作为配置中心注册监听器方法 实现热更新 nacos 配置文件 从而不用重启项目 依赖、工具类 这边就不写了 因为项目用的是 Json 类型的配置文件 所以下文 主要是对json文件进行实现 别的文件大同小异 先说扯淡的东西 在nacos 的配置文件中 dataId 这两种声明 是…

深度学习语义分割篇——DeepLabV2原理详解篇

&#x1f34a;作者简介&#xff1a;秃头小苏&#xff0c;致力于用最通俗的语言描述问题 &#x1f34a;专栏推荐&#xff1a;深度学习网络原理与实战 &#x1f34a;近期目标&#xff1a;写好专栏的每一篇文章 &#x1f34a;支持小苏&#xff1a;点赞&#x1f44d;&#x1f3fc;、…

opencv 十九 python下实现多线程间rtsp直播流的复用

在多线程拉流的任务场景中&#xff0c;有时需要将一个rtsp拉取多次&#xff0c;每重新打开一次rtsp视频流就要多消耗一次带宽&#xff0c;为此基于类的静态对象实现rtsp视频流的复用。 1、实现代码 import threading import cv2,time #接收摄影机串流影像&#xff0c;采用多线…

前后端分离开发【Yapi平台】【Swagger注解自动生成接口文档平台】

前后端分离开发 介绍开发流程Yapi&#xff08;api接口文档编写平台&#xff09;介绍 Swagger使用方式1). 导入knife4j的maven坐标2). 导入knife4j相关配置类3). 设置静态资源映射4). 在LoginCheckFilter中设置不需要处理的请求路径 查看接口文档常用注解注解介绍 当前项目中&am…

MATLAB:优化与规划问题

一、线性规划 % 线性规划&#xff08;Linear programming, 简称LP&#xff09; fcoff -[75 120 90 105]; % 目标函数系数向量 A [9 4 7 54 5 6 105 10 8 53 8 9 77 6 4 8]; % 约束不等式系数矩阵 b [3600 2900 3000 2800 2200]; % 约束不等式右端向量 Aeq []; % 约束等式系…

docker:在ubuntu中运行docker容器

前言 1 本笔记本电脑运行的ubuntu20.04系统 2 docker运行在ubuntu20.04系统 3 docker镜像使用的是ubuntu18.04&#xff0c;这样拉的 docker pull ubuntu:18.04 4 docker容器中运行的是ubuntu18.04的系统&#xff0c;嗯就是严谨 5 这纯粹是学习笔记&#xff0c;实际上没啥价值。…

【spring】AbstractApplicationContext 的refresh() 方法学习

上一篇我们一起学习了【spring】FileSystemXmlApplicationContext 类学习 AbstractApplicationContext 的refresh() 方法介绍 AbstractApplicationContext的refresh()方法仍然是整个Spring应用程序上下文初始化的核心流程入口。大体上的刷新生命周期依然保持一致。 refresh(…

每日一练:LeeCode-48、旋转图像【二维数组+行列交换】

给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出…

Java认识泛型类

一、包装类 认识泛型类之前先来认识一下包装类 1、基本数据类型和对应的包装类 在Java中&#xff0c;由于基本类型不是继承自Object&#xff0c;为了在泛型代码中可以支持基本类型&#xff0c;Java给每个基本类型都对应了一个包装类型。 除了 Integer 和 Character&#xff0…

学点Java_Day12_JDBC

1 JDBC 面向接口编程 在JDBC里面Java这个公司只是提供了一套接口Connection、Statement、ResultSet&#xff0c;每个数据库厂商实现了这套接口&#xff0c;例如MySql公司实现了&#xff1a;MySql驱动程序里面实现了这套接口&#xff0c;Java程序员只要调用实现了这些方法就可以…

自动化测试 —— Pytest fixture及conftest详解

前言 fixture是在测试函数运行前后&#xff0c;由pytest执行的外壳函数。fixture中的代码可以定制&#xff0c;满足多变的测试需求&#xff0c;包括定义传入测试中的数据集、配置测试前系统的初始状态、为批量测试提供数据源等等。fixture是pytest的精髓所在&#xff0c;类似u…