Linux·进程概念(下)

1. 进程优先级

        优先级就是获得某种资源的先后顺序,因为CPU资源是有限的,因此各个进程之间要去争取CPU的资源。

        那么针对Linux操作系统下的PCB中,也就是task_struct结构体中,使用了int类型的变量记录了每个进程的优先级属性,其中优先级数字越小,代表该进程优先级越高。

        我先随便启动一个进程,来看看优先级

        我们打开一个进程,然后用另一个终端输入指令 ps -al 就可以。

        我们只关注PRI和NI的数值。其中 PRI(priority) 代表Linux进程的最终优先级,NI(nice) 是优先级的nice数据,或者说优先级的修正数据。

        nice在这里可以翻译成 "细微的"。把优先级设计成两个参数一起操控的原因是为了防止因为一些优先级的问题导致进程运行问题,比如如果一个程序的进程正在运行但是我们一下子把PRI调低了,那这个进程还要不要执行下去。因此在修改进程优先级的时候,可以现在nice值上修改,等到操作系统更新优先级的时候就统一把优先级从新设置了。

        ps:这里我们可以看到一个UID的值,这是user id的意思,表示目前是哪个用户在进行这个进程,就像一个学生的学号一样。进程也是通过这个值来判断一个用户对某文件是否有读写权限的。

1.1 修改优先级

        修改优先级在Linux中并不多用,而且不建议修改进程优先级。

        nice值的取值是 [-20, 19] 一共40个值,每次调整都是在PRI为80的基础上进行加减,而不是以原优先级为基础进行调整。也就是说调整优先级的操作更类似于是在重置并修改。

        我们下面用 top(Linux下的任务管理器) 来调整一下优先级

        首先随便启动一个进程,然后在另一台终端上输入top命令,再在top中敲 r ,此时可以看到pid to renie的提示,然后我们就可以输入进程id,再修改nice值了。 

        修改的时候直接输入新nice值

                  】

        然后我们退出top查看一下进程优先级,果然是被改变了。如果在改优先级的时候发现修改请求被拒绝了,那就是权限不够,直接 sudo top 提权打开任务管理器就能改优先级了。

        之所以这个nice值只能在一个可控范围内调整就是为了所有进程尽量均衡的调用,这也是分时操作系统的原则。

2. 进程切换

        每一个进程都有对应的时间片,时间片跑完了就要切换进程,这个就是进程的调度轮转。那时间片到了有可能这个进程还没有跑完,那如何让一个进程可以在任何地方重新调度切换就是关键。

        简单来讲,就是在一个进程将要结束自己的这一时间片时,会将自己的运行数据暂时存储起来带走,等重新轮转到自己的时候,再将运行数据给CPU,继续之前的结果计算。

        在CPU中有很多寄存器,例如ebp、esp、eax、ebx等等。本次我们只关注 eip(pc指针) ir

        ir 寄存器,也叫指令寄存器,其中保存的是正在执行的指令

        eip 也叫pc指针,其中存放的是当前正在执行的指令的下一条指令,若有call这种函数调用,其中也会对应记录。

        CPU内部的寄存器记录的是进程正在执行时的瞬时状态信息数据,我们将这个数据称为上下文数据

        那么进程切换的核心就是进程上下文数据的保存和恢复

        当一个进程要开始进入CPU计算了,首先把main函数入口处的地址放到pc指针处,然后在ir寄存器加载第一条命令,CPU控制器将这条命令拿去分析,有需要时交给运算器去计算。当这段进程的时间片跑完后,会将此时该进程在CPU中运算所留下来的所有痕迹拿出去存起来,痕迹比如ebp、esp中存的数据,ir、eip中运行到哪里的记录。这个存储的地方就是PCB中,以 tss_struct(任务状态段) 一种结构体存在,等重新轮转到这段进程的时候就把任务状态段中的数据重新加载到CPU的对应寄存器中就可以接着上次运行继续了。

3. Linux中的调度算法

        我们之前轮调进程的时候都是在一个运行队列中,按顺序从头到尾轮一个遍,这种调度算法虽然简单,但是完全表现不出进程优先级的效果,因此我们下面浅谈一下Linux内核中写的调度算法时什么样的。

        Linux中的调度算法是基于一种类似哈希表结构实现的。

        首先,Lunix的哈希表或者说运行队列中只有140个元素,其中 0到99 前100个元素位置是留给实时进程的,100到139 后40个元素是给分时进程的,也就是说我们只能通过优先级控制后40个元素的位置。

        其对应方案,也就是哈希函数为 i = pri - 60 + 100 真实进程优先级减60加100,真实进程优先级因为有nice值的限制,只能以80为基础在nice值 [-20, 19] 的范围限制下修改,也就是说真实进程优先级的范围是  [60, 99] ,这个范围经过哈希函数的映射后正好就是 [100, 139]

        之后不同优先级的进程就可以开散列式的打散到各个队列当中,相同优先级的进程就可以如哈希桶一样挂到一起。如此利用哈希表结构完成进程在O(1)时间复杂度内的入运行队列

        然而在实际的进程控制操作中可能会遇到3种情况:1. 进程正常退出    2. 进程跑完时间片  3. 新插入进程。

        进程正常退出就是走Z状态再走X状态,父进程释放其资源完成退出。但是到插入进程的话会有一点问题,如果有人一直恶意、频繁的插入优先级非常高的新进程,就会导致操作系统一直在执行新进程,而永远没有机会执行其他优先级较低的进程,这种情况下那些一直执行不到的进程就被称为饥饿进程

        那么为了避免饥饿进程的发生,Linux调度算法种引入了一个活跃进程过期进程的处理方案,正在进行的进程哈希表被称为活跃进程的哈希表新插入的进程和跑完时间片的进程都会被放入过期进程的哈希表中,最后活跃进程的哈希表完全都跑完了,也就是说活跃进程的哈希表为空后,交换活跃进程与过期进程的哈希表,如此循环。也就是说Linux调度算法中用两个哈希表作为基础,解决了饥饿进程的问题。

        但是两个哈希表仅仅是基础,它们分别又被外面包了一层结构体,两个结构体组成了一个2元素的数组用来集中控制,最后数组外面又包了一层结构体,最后这一层结构体才被叫做运行队列runqueue

        刚才说的包来包去的很复杂,我们照着上图捋一遍。

        首先存放着进程PCB的哈希表被包在结构体queue中,同时在这个结构体中有表示哈希表一共存放多少进程的个数的nr_active变量,位图bitmap[5],这是一个有5个int元素的数组,作用是帮助在O(1)时间复杂度之内找到一个有效的哈希桶,具体怎么操作的我们后面详谈。

        之后这个两个queue结构体被封装进一个两元素的数组,便于统一管理。

        最后数组、活跃进程指针、过期进程指针,被封装到运行队列结构体中。两个指针分别指向数组的两个元素,这两个元素中就是活跃进程的哈希表和过期进程的哈希表,此时完成逻辑闭环。

        位图bitmap[5]是利用比特位标识哈希表中某个位置有没有哈希桶,5个int一共40个字节,160个bit位,后20个比特位没有意义,不看,只看前140个bit位。第几号比特位为1,表示哈希表中第几位有哈希桶,这个哈希桶不一定只有一共,可能这个位置坠了一串哈希桶,就说明这个位置有一串优先级一样的进程。

        在寻找有效哈希桶的时候可以先看bitmap5个数组元素的值,如果5个元素都为0,说明现在没有进程在运行状态。如果不为0,就接用 n&(n-1) 的思路快速找到第一个为1的比特位,大概操作就是用 n&(n-1)的值 按位异或^n,就可以直接暴露出第一个为1的比特位,分析这个值,可以得到第几号比特位为1。

        n&(n-1)的思路在下面这个连接中

C语言·操作符详解-CSDN博客

        最终找到有效哈希桶,如果是一串哈希桶,就从第一个开始运算并头删,直到这组哈系统全部调度完毕。如此可以在O(1)时间复杂度内利用位图完成有效哈希桶搜索。

        如此O(1)级别的进程哈希桶插入,O(1)级别的哈希桶搜索,最成就为Linux的O(1)级别进程调度算法

4. Linux中的数据结构

        我们之前在学习数据结构的时候,比如链表,一共节点中既要存贮数据(属性字段),又要有next、prev指针(连接字段),但是在Linux源代码中对于数据结构的处理是只有连接字段,没有属性字段的。

        就是说只提供链条,而我们要把链条手动绑在各个结构体节点上,我们以双向链表举例子。

        在需要由数据结构链接起来的结构体节点之间用数据结构的链条绑定起来,而不是直接把节点绑定起来。

        这么做的意义是可以把同一种结构体节点同时不同的数据结构方案绑定起来,而不用写多份节点形式来适应不同的绑定方案。

        因为绑定的是结构体的成员变量,因此如何通过成员变量找到改成员所属的结构体就十分重要。解决方案就是计算该成员的偏移量,通过 所属结构体地址 = 该成员地址 - 偏移量 的公式可以计算出。C语言库中提供了一个宏来帮助计算偏移量 offsetof (type,member); <stddef.h>

        至于偏移量和结构体内存分配方案是什么可以转到:C语言·自定义类型:结构体-CSDN博客

        如果想要自己计算偏移量,可以参考这个思路

                ​​​​​​​        ​​​​​​​        

        以内存地址为0的地方做结构体基准点,直接取成员变量,其内存大小就是改成员的偏移量,如果用该成员的真实地址减去偏移量,就能得到结构体的起始地址。将得到的结构体真实起始地址拿去强转成对应类型,比如将地址强转成 struct A 类型就可以正常使用这个结构体了。

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

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

相关文章

“米哈游悄然布局未来科技:入股星海图,共绘具身智能机器人新篇章“

米哈游悄然入股具身智能机器人公司:技术布局与未来展望 近日,米哈游阿尔戈科技有限公司宣布入股具身智能机器人公司星海图,这一消息在行业内引起了广泛关注。米哈游,这家以游戏开发而闻名的企业,近年来正逐步扩大其在人工智能和新兴科技领域的投资布局,此次入股星海图正是…

数组指针和指针数组

引用&#xff1a;【数组指针】 仅此一篇 让你深刻理解数组指针-CSDN博客 b站&#xff1a;【动画讲解C语言指针-14-数组指针和指针数组】 https://www.bilibili.com/video/BV1Qj421U75U/?share_sourcecopy_web&vd_sourced59dcee6044af8fc880b46b581c3f58a 指向数组和指向…

Windows Ubuntu下搭建深度学习Pytorch训练框架与转换环境TensorRT

Windows Ubuntu下搭建深度学习Pytorch训练框架与转换环境TensorRT JetBrains2024&#xff08;IntelliJ IDEA、PhpStorm、RubyMine、Rider……&#xff09;安装包Anaconda Miniconda安装.condarc 文件配置镜像源查看conda的配置和源(channel)自定义conda虚拟环境路径conda常用命…

双指针:滑动窗口

题目描述 给定两个字符串 S 和 T&#xff0c;求 S 中包含 T 所有字符的最短连续子字符串的长度&#xff0c;同时要求时间复杂度不得超过 O(n)。 输入输出样例 输入是两个字符串 S 和 T&#xff0c;输出是一个 S 字符串的子串。样例如下&#xff1a; 在这个样例中&#xff0c…

在树莓派上部署开源监控系统 ZoneMinder

原文&#xff1a;https://blog.iyatt.com/?p17425 前言 自己搭建&#xff0c;可以用手里已有的设备&#xff0c;不需要额外买。这套系统的源码是公开的&#xff0c;录像数据也掌握在自己手里&#xff0c;不经过不可控的三方。 支持设置访问账号 可以保存录像&#xff0c;启…

C++中,如何使你设计的迭代器被标准算法库所支持。

iterator&#xff08;读写迭代器&#xff09; const_iterator&#xff08;只读迭代器&#xff09; reverse_iterator&#xff08;反向读写迭代器&#xff09; const_reverse_iterator&#xff08;反向只读迭代器&#xff09; 以经常介绍的_DList类为例&#xff0c;它的迭代…

QT--基础

将默认提供的程序都注释上意义 0101.pro QT core gui #QT表示要引入的类库 core&#xff1a;核心库 gui&#xff1a;图形化界面库 #如果要使用其他库类中的相关函数&#xff0c;则需要加对应的库类后&#xff0c;才能使用 greaterThan(QT_MAJOR_VERSION, 4): QT wid…

算法: 二分查找题目练习

文章目录 二分查找二分查找在排序数组中查找元素的第一个和最后一个位置搜索插入位置x 的平方根山脉数组的峰顶索引寻找峰值寻找旋转排序数组中的最小值点名 总结精华模版 二分查找 二分查找 没啥可说的,轻轻松松~ class Solution {public int search(int[] nums, int target…

栈的介绍与实现

一. 概念与结构 栈&#xff1a;⼀种特殊的线性表&#xff0c;其只允许在固定的⼀端进⾏插⼊和删除元素操作。进⾏数据插⼊和删除操作的⼀端称为栈顶&#xff0c;另⼀端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out的原则。 压栈&#xff1a;栈的插…

二叉树进阶学习——从前序和中序遍历序列构造二叉树

1.题目解析 题目来源&#xff1a;105.从前序与中序遍历序列构造二叉树——力扣 测试用例 2.算法原理 首先要了解一个概念 前序遍历&#xff1a;按照 根节点->左子树->右子树的顺序遍历二叉树 中序遍历&#xff1a;按照 左子树->根节点->右子树的顺序遍历二叉树 题目…

在 Kali Linux 中安装 Impacket

步骤 1&#xff1a;更新系统 打开终端并确保你的系统是最新的&#xff1a; sudo apt update && sudo apt upgrade -y 步骤 2&#xff1a;安装依赖 在安装 Impacket 之前&#xff0c;你需要确保安装了 Python 和一些必要的依赖。通常&#xff0c;Kali 已经预装了 Pytho…

影刀RPA实战:Excel拆分与合并工作表

1.影刀操作excel的优势 Excel&#xff0c;大家都不陌生&#xff0c;它是微软公司推出的一款电子表格软件&#xff0c;它是 Microsoft Office 套件的一部分。Excel 以其强大的数据处理、分析和可视化功能而闻名&#xff0c;广泛应用于商业、教育、科研等领域。可以说&#xff0…

YOLO11改进|注意力机制篇|引入ELA注意力机制

目录 一、【ELA】注意力机制1.1【ELA】注意力介绍1.2【ELA】核心代码 二、添加【ELA】注意力机制2.1STEP12.2STEP22.3STEP32.4STEP4 三、yaml文件与运行3.1yaml文件3.2运行成功截图 一、【ELA】注意力机制 1.1【ELA】注意力介绍 这篇论文的作者通过分析Coordinate Attention(C…

Java Supplier和Consumer接口

Supplier 在Java中&#xff0c;Supplier接口是一个重要的函数式接口&#xff0c;它属于java.util.function包&#xff0c;Supplier通常用于延迟计算或生成值的场景。Supplier接口是一个泛型接口&#xff0c;其get()方法不接受任何参数但返回一个泛型类型T的值。 这个接口被注解…

STM32新建工程-基于库函数

目录 一、创建一个新工程 二、为工程添加文件和路径 三、创建一个main.c文件&#xff0c;并调试 四、修改一些配置 五、用库函数进行写程序 1、首先加入一些库函数和头文件 2、编写库函数程序 一、创建一个新工程 我这里选择STM32F103C8的型号&#xff0c;然后点击OK。 …

Maven下载、安装与环境配置详解:从零开始搭建高效Java开发环境

下载 官方网站&#xff1a;http://maven.apache.org/ 下载页面&#xff1a;http://maven.apache.org/download.cgi 官网 下载页面 注&#xff1a;本教程使用的是3.3.9版本的maven。 安装 maven安装包下载完成后是一个压缩文件&#xff0c;如下图所示&#xff1a; 我们需要将…

java 数据存储方式

1. 变量存储 这是最基本的数据存储方式&#xff0c;通过声明变量来存储数据。变量可以是基本数据类型&#xff08;如int、float、char等&#xff09;&#xff0c;也可以是引用数据类型&#xff08;如对象、数组等&#xff09;。变量存储的数据通常存储在内存中&#xff0c;随着…

Redis --- 第三讲 --- 通用命令

一、get和set命令 Redis中最核心的两个命令 get 根据key来取value set 把key和value存储进去 redis是按照键值对的方式存储数据的。必须要先进入到redis客户端。 语法 set key value &#xff1a; key和value都是字符串。 对于上述这里的key value 不需要加上引号&#…

【D3.js in Action 3 精译_028】3.4 小节 DIY 实战:使用 Observable 在线绘制 D3 条形图

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…

关于Fake Location定位,运动世界校园问题

不好意思&#xff0c;之前那个文章其实是很早之前的&#xff0c;不知道为什么审核了很久一直没有通过&#xff0c;然后前几周莫名其妙点了一下重新发布&#xff0c;竟然发布成功了&#xff0c;这个方法已经失效了&#xff0c;要可以稳定&#xff0c;我建议是买一台root的手机&a…