【Linux深入剖析】进程优先级 | 命令行参数 | 环境变量


📙 作者简介 :RO-BERRY
📗 学习方向:致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识
📒 日后方向 : 偏向于CPP开发以及大数据方向,欢迎各位关注,谢谢各位的支持


在这里插入图片描述


目录

  • 1.进程优先级
  • 2.Linux下的进程优先级
    • 调整优先级
  • 3.进程切换
    • 3.1进程特性
    • 3.2寄存器
    • 3.3 进程切换的过程
  • 4 命令行参数
  • 5.利用main函数参数实现简易计算器
  • 6.环境变量
    • 6.1 基本概念
    • 6.2 环境变量的分类
    • 6.3 查看环境变量


1.进程优先级

进程优先级就是进程要访问某种资源,进程进行通过一定的方式(排队),确认享受资源的先后顺序
CPU资源分配的先后顺序,就是值进程的优先权(priority)
优先权高的进程有优先执行的权力。

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

为什么要有优先级?

因为CPU资源有限,一台普通的电脑上CPU是4~8个,而要执行的进程少说也要20个以上,所以要让重要的进程优先执行,保证利益的最大化。


2.Linux下的进程优先级

在Linux或unix系统中,用ps -al指令则会类似输出以下几个内容:

在这里插入图片描述

  • UID:代表执行者的身份,用户标识符
  • PID:代表这个进程的代号
  • PPID:代表这个进程是由那个进程发展衍生而来的,亦即父进程的代号
  • PRI:代表这个进程可被执行的优先级,其值越小越早被执行
  • NI:代表这个进程的nice值

PRI 和 NI

  • PRI(priority),即进程的优先级,就是程序被CPU执行的先后顺序,此值越小进程的优先级越高
    每个普通进程的PRI默认值为80
  • NI(nice),表示进程可被执行的优先级的修正数值
  • nice值默认基本都是0
  • PRI值越小越快被执行,加入nice值后,将会使得PRI变为:PRI = 80 + nice
  • 当nice为负数时,该进程的优先级将会变小,即期优先级会变高,则其越快被执行
  • 调整优先级,在Linux下,就是调整进程的nice值
  • nice其取值范围是-20~19 ,一共40个级别(一般不会去改nice值,一直使用默认值)

nice值之所以有范围,为了防止优先级被调整过度,时每次先使用CPU都是同一批进程,其它进程没办法更好的调度执行,所以过渡器 不允许过度调整nice值

调度器主要功能:较均衡的让每个进程都可以使用CPU推进代码,而不能使一个或几个进程产生偏差
所以,优先级对于我们来说并不是很重要,我们一般写代码也几乎不回去调整优先级

注意

进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化。
可将nice理解为是进程优先级的修正数据

关于优先级PRI着重强调

Linux默认优先级是80
Linux的优先级是可以修改的,Linux的优先级的范围(60,99]
Linux优先级本质是数字,数字越小,优先级越高

调整优先级

调整方法非常多,可以使用代码去调整,也可以用指令去调,这里我们讲一下使用top去调整进程的优先级
写一个简单程序
myprocess.c

 1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 int main()
  5 {
  6   while(1)
  7   {
  8       printf("I am process, pid: %d\n",getpid());                                       
  9       sleep(1);
 10   }
 11 }

makefile

myprocess:myprocess.c
		gcc -o $@ $^ #-std=c99
.PHONY:clean
clean:
	rm -f myprocess

使用make指令生成可执行文件myprocess,之后按照下面的步骤做

  1. 执行该文件
  2. 使用ps -la查看对应进程的PID
  3. 使用top指令打开top
  4. 进入top后,按“r”
  5. 输入进程PID,回车
  6. 输入需要调整的nice值,回车
  7. 按q退出top
  • 使用ps -la查看对应进程的PID
    在这里插入图片描述
  • 使用top指令打开top
    在这里插入图片描述
  • 进入top后,按“r”
    在这里插入图片描述
  • 输入进程PID,回车
    在这里插入图片描述
  • 输入需要调整的nice值,输入10回车,然后按q退出top再次使用ps -la查看对应进程的PID
    在这里插入图片描述

PRI变为90,并且NI变为10

注意:
如果调整的nice值过大,那调整的值默认为19
如果调整的nice值过小,那调整值默认为-20
每次修改调整值,最终的PRI都是80加上nice值

Linux为什么调整优先级是有一个范围的?
进程饥饿问题

如果不加限制,那么将自己的进程优先级调整的非常高,别人的优先级调整的非常低,每个人都会想把自己的程序优先级调到最高,优先级较高的进程,会优先得到资源,但是后续还有源源不断的进程产生,常规进程就会很难享受到CPU资源,就会导致进程饥饿问题


3.进程切换

3.1进程特性

  • 竞争性:系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了搞小完成任务,更合理竞争相关资源,便具有了优先级。
  • 独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰
  • 并行:多个进程在多个CPU下分别、同时进行运行,这称之为并行。
  • 并发:多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发

3.2寄存器

一个CPU里面存在很多的寄存器寄存器,寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果以及一些CPU运行需要的信息。
寄存器主要分为:通用寄存器、标志寄存器、指令寄存器、段寄存器、控制寄存器、调试寄存器、描述符寄存器、任务寄存器、MSR寄存器

通用寄存器

eax: 通常用来执行加法,函数调用的返回值一般也放在这里面
ebx: 数据存取
ecx: 通常用来作为计数器,比如for循环
edx: 读写I/O端口时,edx用来存放端口号
esp: 栈顶指针,指向栈的顶部
ebp: 栈底指针,指向栈的底部,通常用ebp+偏移量的形式来定位函数存放在栈中的局部变量
esi: 字符串操作时,用于存放数据源的地址
edi: 字符串操作时,用于存放目的地址的,和esi两个经常搭配一起使用,执行字符串的复制等操作

标志寄存器

标志寄存器,里面有众多标记位,记录了CPU执行指令过程中的一系列状态,这些标志大都由CPU自动设置和修改:

  • CF 进位标志
  • PF 奇偶标志
  • ZF 零标志
  • SF 符号标志
  • OF 补码溢出标志
  • TF 跟踪标志
  • IF 中断标志

指令寄存器

eip: 指令寄存器可以说是CPU中最最重要的寄存器了,它指向了下一条要执行的指令所存放的地址,CPU的工作其实就是不断取出它指向的指令,然后执行这条指令,同时指令寄存器继续指向下面一条指令,如此不断重复,这就是CPU工作的基本日常。

段寄存器

段寄存器与CPU的内存寻址技术紧密相关。

控制寄存器

控制寄存器是CPU中一组相当重要的寄存器,我们知道eflags寄存器记录了当前运行线程的一系列关键信息。那CPU运行过程中自身的一些关键信息保存在哪里呢?答案是控制寄存器!

调试寄存器

在x86/x64CPU内部,还有一组用于支持软件调试的寄存器。

描述符寄存器

所谓描述符,其实就是一个数据结构,用来记录一些信息,‘描述’一个东西。把很多个描述符排列在一起,组成一个表,就成了描述符表。再使用一个寄存器来指向这个表,这个寄存器就是描述符寄存器。

任务寄存器

CPU内部设置了一个专用的寄存器——任务寄存器TR,它指向当前运行的任务

MSR寄存器

从80486之后的x86架构CPU,内部增加了一组新的寄存器,统称为MSR寄存器,中文直译是模型特定寄存器,意思是这些寄存器不像上面列出的寄存器是固定的,这些寄存器可能随着不同的版本有所变化。这些寄存器主要用来支持一些新的功能。

3.3 进程切换的过程

  1. 计算机调度某个进程时,CPU 会把这个进程的 PCB 地址加载到某个寄存器,也就是说,CPU内有寄存器可以只找到进程的PCB地址。
  2. CPU里有一个 eip 寄存器(PC指针),指向当前执行指令的下一条指令的地址。
  3. 当进程在运行的时候,一定会产生非常多的临时数据,这些临时数据只属于当前进程,这些临时数据会放在CPU的寄存器中。CPU内部的所有的临时数据我们称做为硬件上下文
  4. 进程在调度的时候占有CPU,但是却不是一直占有到进程结束,进程都有自己的时间片,有了时间片就可以实现高效率调度,因为时间片的存在,进程会出现没有被执行完就被拿下去的情况。
  5. 当进程被换下去的时候,进程的运行信息会被存在操作系统里面,以便下次CPU重新调度时进程能够正常运行,这叫做进程的上下文保护。
  6. 在进程第二次被CPU调度的时候,首先要做的第一件事情就是读取操作系统中进程运行的相关数据,这叫做进程的上下文恢复,然后进程就会继续上次没执行完的任务开始运行。

注意:

CPU内的寄存器只有一套,区分寄存器以及寄存器的内容,这两个是不一样的,我们运行进程使用这一套寄存器并且产生临时数据,当进程离开的时候,这些临时数据一并带走存入操作系统,当次进程再次运行的时候,数据重新拿出来。
但是寄存器内部保存的数据可以有多套,虽然寄存器数据放在了一个共享的CPU设备里,但是所有的数据,其实都是被进程私有的!进程和进程之间使用同一个CPU以及寄存器,但是其中的数据不是共享的。


4 命令行参数

请你回想一下写C语言代码的时候,我们的主函数main函数带参数吗?我们一般写main函数里面不带参数也就是void参数对吧?
其实main函数默认是带参数的函数

#include<stdio.h>
int main(int argc,char *argv[])
{
		return 0
}

我们这样直接运行代码是能跑的
这里的char *argv就是一个指针数组,int argc则代表了这个指针数组里有多少个成员
这里面存的是什么呢?
我们来试着打印一下

#include<stdio.h>
int main(int argc,char *argv[])
{
  for(int i=0;i < argc; i++)
  {
    printf("argv[%d]:%s\n",i,argv[i]);
  }
  return 0;
}

运行结果
在这里插入图片描述
其演变过程如下:
在这里插入图片描述
这就是我们bash维护的命令行参数表


那到底为什么要这样做呢?

请看下面的例子:

#include<stdio.h>
#include<string.h>

//要实现三种不同的功能
// ./myprocess -3
int main(int argc,char *argv[])
{
  if(argc != 2)
  {
    printf("Usage:\n\t%s -number[1-3]\n",argv[0]);
    return 1;
  }
  if(strcmp("-1",argv[1]) == 0)
  {
    printf("function 1\n");
  }
  else if(strcmp("-2",argv[1]) == 0)
  {
    printf("function 2\n");
  }
  else if(strcmp("-3",argv[1]) == 0)
  {
    printf("function 3\n");
  }
  else
  {
    printf("unKnow!\n");
  }
  return 0;
}

运行结果
在这里插入图片描述
通过这个代码片段我们已经实现了简单的功能:

我们可以通过不同的选项,让我们的同一个程序执行它内部不同的功能

我们这样使用程序,有没有觉得眼熟呢?
在这里插入图片描述
我们使用的这些Linux指令不也是这样使用的吗?
我们指令后面的这些选项让我们可以实现指令的不同的功能

所以指令后面的这些选项的本质是我们的命令行参数!!!!

命令行参数是我们Linux选项指令的基础

5.利用main函数参数实现简易计算器

既然main函数参数可以读到命令行
中输入的字符串,所以可以用代码实现
一个简易的计算器,代码如下:

#include<stdio.h>    
#include<string.h>    
#include<stdlib.h>    
int main(int argc,char* argv[])    
{    
    if(argc!=4)    
    {    
        printf("%s OP[add|sub|mul|div] d1 d2\n",argv[0]);    
        return 1;    
    }    
    int x=atoi(argv[2]);    
    int y=atoi(argv[3]);    
    if(strcmp(argv[1],"add")==0)    
        printf("%d + %d = %d\n",x,y,x+y);    
    else if(strcmp(argv[1],"sub")==0)    
        printf("%d - %d = %d\n",x,y,x-y);    
    else if(strcmp(argv[1],"mul")==0)    
        printf("%d * %d = %d\n",x,y,x*y);    
    else if(strcmp(argv[1],"div")==0)                                                                                                                                   
        printf("%d / %d = %d\n",x,y,x/y);    
    else    
        printf("输入操作符错误");    
    return 0;    
}    

使用方法:

  1. 用户必须先输入可执行程序:a.out
  2. 第二个字符串输入加减乘除其中一个
  3. 第三,第四个字符串输入操作数
  4. 若其中有一个环节输入错误会报提醒

在这里插入图片描述

6.环境变量

6.1 基本概念

环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数。
如:我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里,但 是照样可以链接成功,生成可执行程序,原因就是有相关环境变量帮助编译器进行查找;
环境变量通常具有某些特殊用途,还有在系统当中通常具有全局特性。
程序(操作系统命令和应用程序)的执行都需要运行环境,这个环境是由多个环境变量组成的。

6.2 环境变量的分类

按生效的范围分类

系统环境变量:公共的,对全部的用户都生效。
用户环境变量:用户私有的、自定义的个性化设置,只对该用户生效。

按生存周期分类

永久环境变量:在环境变量脚本文件中配置,用户每次登录时会自动执行这些脚本,相当于永久生效。
临时环境变量:使用时在Shell中临时定义,退出Shell后失效。

6.3 查看环境变量

我们来先思考一个问题

  • 为什么我们myprocess可执行文件前面需要加上./
  • Linux指令在使用的时候不需要在前面加上./

这是因为我们系统在执行myprocess文件的时候是去查找了,但是没找到,所以我们在前面加上./或者绝对路径就可以运行了,所以我们想执行一个程序系统需要先找到

这不得不引出一个概念: 环境变量
保存程序的默认搜索路径的环境变量
叫做:
PATH

在运行程序时,系统会去PATH中
找当前可执行程序在不在这些路径中
如果在就直接执行程序,不在就报错

使用指令查看PATH

echo $PATH

****加粗样式****
这个路径是以无数个子路径组成,路径之间以冒号进行分隔
系统在这些路径下都找不到你的程序就会报错
所以只能通过./告诉系统我们在这个路径下查找

所以要想我们的指令像系统指令一样运行
我们可以将自己写的程序的路径加入
到环境变量PATH中!
注意:我们进行拷贝需要root的权限,普通用户使用指令需要sudo

使用指令:

sudo cp myprocess /usr/bin/

**加粗样式**

我们刚刚是拷贝数据到默认路径下实现我们的程序可以像系统指令一样运行起来
那我们可不可以把我们的工作目录也加入到默认搜索路径下面呢?那我们就不用每一次都要一个一个程序去加入默认路径了

使用指令: PATH = $PATH:要添加的路径

在这里插入图片描述

请注意,当你将你的路径添加后
下次重启时又会恢复为默认路径
所以想一劳永逸的话可以将你自己
的可执行程序放入默认的路径中!

查看所有环境变量
使用指令

env

**加粗样式**
可以看到这里很多,看的眼花缭乱,我们并不需要每一个都认识,我们简单认识几个:

  • 1.环境变量PWD

为什么我们可以使用pwd查看当前目录呢?
这是因为存在pwd环境变量,当你访问目录这个环境变量会自动更新

  • 2.环境变量USER

为什么我们使用whoami指令就会打出当前用户的用户名呢?
这是因为我们的环境变量USER,它记录了我们登入Linux的用户的用户名信息

  • 3.环境变量HOME

为什么我们使用cd ~就可以访问我们的家目录呢?
这是因为我们有环境变量HOME,他记录着我们当前用户的家目录

学习之路还很漫长,如果我的文章对你有所帮助的话,不妨关注加三连给我一个支持,感谢各位IT大佬

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

hot100刷题记录-哈希

一、两数之和 题目&#xff1a;https://leetcode.cn/problems/two-sum/description/?envTypestudy-plan-v2&envIdtop-100-liked 方法1&#xff1a;枚举 class Solution:def twoSum(self, nums: List[int], target: int) -> List[int]:for id, num in enumerate(nums)…

jmeter 按线程数阶梯式压测数据库

当前版本&#xff1a; jmeter 5.6.3mysql 5.7.39 简介 JMeter 通过 bzm - Concurrency Thread Group 来实现阶梯式压测&#xff0c;它并不是JMeter的官方插件&#xff0c;而是一种由Blazemeter提供的高级线程组插件。可以在不同的时间内并发执行不同数量的线程&#xff0c;模拟…

相册图片怎么压缩?3种方法教你压缩图片

相册图片怎么压缩&#xff1f;相册图片压缩在日常生活中扮演着至关重要的角色。它不仅能够帮助我们节省手机或电脑的存储空间&#xff0c;避免设备因存储空间不足而运行缓慢&#xff0c;还能显著减少图片在上传、下载或分享时的时间。此外&#xff0c;压缩图片还能在一定程度上…

[算法沉淀记录] 排序算法 —— 选择排序

排序算法 —— 选择排序 基本概念 选择排序是一种简单的排序算法&#xff0c;它的工作原理是每次从待排序的列表中选择最小&#xff08;或最大&#xff09;的元素&#xff0c;将其与列表中的第一个位置交换&#xff0c;然后继续对剩余的元素进行排序&#xff0c;直到整个列表…

【Java程序员面试专栏 算法思维】四 高频面试算法题:回溯算法

一轮的算法训练完成后,对相关的题目有了一个初步理解了,接下来进行专题训练,以下这些题目就是汇总的高频题目,本篇主要聊聊回溯算法,主要就是排列组合问题,所以放到一篇Blog中集中练习 题目关键字解题思路时间空间岛屿数量网格搜索分别向上下左右四个方向探索,遇到海洋…

R绘图 | 单列数据的分布图,对A变量分bin求B变量的平均值

问题1&#xff1a;单个向量的 density 分布图&#xff1f; (1) 模拟数据 set.seed(202402) datdiamonds[sample(nrow(diamonds), 1000),]> head(dat) # A tibble: 6 10carat cut color clarity depth table price x y z<dbl> <ord> &l…

数据可视化引领智慧仓储新时代

随着科技的飞速发展&#xff0c;数据可视化已然成为智慧仓储领域的璀璨明珠&#xff0c;其强大的功能和多面的作用让智慧仓储焕发出勃勃生机。让我们一同探索&#xff0c;数据可视化究竟在智慧仓储中起到了怎样的作用。下面我就以可视化从业者的角度来简单谈谈这个话题。 在这…

Linux——进程概念

目录 冯诺依曼体系结构 操作系统 管理 系统调用和库函数 进程的概念 进程控制块——PCB 查看进程 通过系统调用获取进程标示符 通过系统调用创建进程 进程状态 运行状态-R ​编辑 浅度睡眠状态-S 深度睡眠状态-D 暂停状态-T 死亡状态-X 僵尸状态-Z 僵尸进程…

详解顺序结构滑动窗口处理算法

&#x1f380;个人主页&#xff1a; https://zhangxiaoshu.blog.csdn.net &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️&#xff0c;如有错误敬请指正! &#x1f495;未来很长&#xff0c;值得我们全力奔赴更美好的生活&…

WPF 附加属性+控件模板,完成自定义控件。建议观看HandyControl源码

文章目录 相关连接前言需要实现的效果附加属性添加附加属性&#xff0c;以Test修改FontSize为例依赖属性使用触发器使用直接操控 结论 控件模板&#xff0c;在HandyControl的基础上面进行修改参考HandyControl的源码控件模板原型控件模板 控件模板触发器完整样式简单使用 结论 …

光学3D表面轮廓仪微纳米三维形貌一键测量

光学3D表面轮廓仪(白光干涉仪)利用白光干涉原理&#xff0c;以0.1nm分辨率精准捕捉物体的表面细节&#xff0c;实现三维显微成像测量&#xff0c;被广泛应用于材料学领域的研究和应用。 了解工作原理与技术 材料学领域中的光学3D表面轮廓仪&#xff0c;也被称为白光干涉仪&am…

低价对品牌渠道的危害

品牌价值的体现主要在价格&#xff0c;比如要与竞品体现差异&#xff0c;除了产品功能上有做出差异&#xff0c;价格上也需要设置不同的阶梯&#xff0c;但如果经销商不遵守这个体系&#xff0c;或者非授权店铺随意低价&#xff0c;对于品牌来说都是非常不好的事情&#xff0c;…

Django后台管理(二)

一、自定义注册管理类介绍 官网:Django 管理站点 | Django 文档 | Django 注册模型除了使用 Django 默认的管理类admin,也可以自定义,比如: class StudentAdmin(admin.ModelAdmin):pass admin.site.register(Student, StudentAdmin)ModelAdmin 类是管理界面中模型的表示。…

微信小程序 wxs内联与外联的写法

内联写法 <!-- 内联wxs --> <view>大写字母{{m1.toUpper("xmly")}}</view> <wxs module"m1">module.exports.toUpperfunction(str){return str.toUpperCase()} </wxs> 外联写法 新建一个wxs文件 写一个函数&#xff0c;将…

架构(十五)Java字节码增强

一、引言 一般如果需要做增强类的架构工具会使用SpringBoot提供的切面&#xff0c;但是这逃不开两个问题&#xff1a;1、使用方需要加注解代码&#xff1b;2、版本更新导致的发布。 所以java还提供了字节码层面的增强方案&#xff0c;对使用的系统是无感的。 二、字节码增强选…

BLEU: a Method for Automatic Evaluation of Machine Translation

文章目录 BLEU: a Method for Automatic Evaluation of Machine Translation背景和意义技术原理考虑 n n n - gram中 n 1 n1 n1 的情况考虑 n n n - gram中 n > 1 n\gt 1 n>1 的情况考虑在文本中的评估初步实验评估和结论统一不同 n n n 值下的评估数值考虑句子长度…

Ubuntu上Jenkins自动化部署Gitee上SpringBoot项目

文章目录 安装安装JDK安装Maven安装GitNodeJS安装&#xff08;可选&#xff09;安装Jenkins 配置Jenkins为Jenkins更换插件源设置jenkins时区安装插件全局工具配置添加Gitee凭证Gitee项目配置 部署后端1.新建任务2.配置源码管理3.构建触发器4.到Gitee中添加WebHook5.构建环境6.…

C++——基础语法(3):内联函数、auto关键字、基于范围的for循环、空指针nullptr

6. 内联函数 在函数前加入inline修饰即可将函数变为内联函数。所谓内联函数&#xff0c;就是在编译时C编译器会将函数体在调用内联函数的地方展开&#xff0c;从而省去了调用函数的栈帧开销&#xff0c;提高程序运行效率。 inline int Add(int a, int b) {return a b; } int …

十一、计算机视觉-膨胀操作

文章目录 前言一、什么是膨胀二、膨胀操作的实现1.引入库 三、膨胀的原理 前言 上节我们学习了腐蚀操作&#xff0c;本节我们讲一下膨胀操作&#xff0c;膨胀和腐蚀实际上是相反的操作。上节我们把云峰这2个字周围没用的像素去掉了&#xff0c;但是云峰这2个字也变细了&#x…