Linux:进程优先级与命令行参数

news/2025/2/18 18:05:34/

目录

1.进程优先级

1.1 基本概念

1.2 查看系统进程

1.3 修改进程优先级的命令

2.进程间切换

2.1 相关概念

2.2 Linux2.6内核进程调度队列(了解即可)

3.命令行参数


1.进程优先级

1.1 基本概念

  1. cpu资源分配的先后顺序,就是指进程的优先权(priority)。
  2. 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
  3. 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。 

我们知道进程在内存中是需要排队的,比如运行队列,等待队列。那排队是干什么的:就是在确认优先级,来确定得到某种资源的先后顺序。为什么要确认优先级,本质就是资源不足。那么操作系统是怎么做到呢?我们下面讲解:

优先级其实就是PCB中的一个 int 字段数值越小,优先级越大,跟我们的考试排名一样。Linux进程的优先级数值范围:60~99。Linux中默认进程的优先级都是80。

1.2 查看系统进程

我们先编写一段代码

在linux或者unix系统中,用ps -la 命令则会类似输出以下几个内容:

我们很容易注意到其中的几个重要信息,有下:

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

PRI and NI:

PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高

那NI就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值

PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为PRI(new)=PRI(old)+nice。pri(old) ,都是从80开始的!

这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行所以,调整进程优先级,在Linux下,就是调整进程nice值

nice其取值范围是-20至19,一共40个级别。nice调整最小是:-20,超过部分统一当成-20。nice调整最大是:19,超过部分统一当成19。


PRI vs NI:

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

1.3 修改进程优先级的命令

用top命令更改已存在进程的nice:

  • top
  • 进入top后按“r”–>输入进程PID–>输入nice值

我们输入进程号讲 nice 改为19

普通用户是没有办法将优先级调大,让PRI减小的。需要使用su换成root,或使用sudo命令。 

2.进程间切换

2.1 相关概念

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

因为要并发,所以必定要考虑进程间切换。

每一个进程不是占有CPU就一直运行,每隔一段时间,会自动被操作系统从cpu上剥离下来。Linux内核是支持进程之间进行cpu资源抢占的!是基于时间片的轮转式抢占式内核。

为什么我们函数内定义的栈临时变量,会返回给外部,我们的程序/进程,它怎么知道我们当前运行到哪里里?如何做到函数间跳转? 因为cpu内存在大量寄存器,比如eip是程序计数器或者叫pc,会自动执行我们要执行的下一行代码。我们的进程在运行的时候,是会使用这些寄存器的,我们的进程,会产生各种数据,在寄存器中临时保存。

如果我们有多个进程呢?各个进程在CPU寄存器中形成的临时数据,都应该是不一样的!而cpu内寄存器只有一套,我们如何做到让每个进程使用cpu时能从上次时间片执行到的位置继续执行?我们可以先简单的理解为时间片结束后,cpu寄存器中的数据会先保存到进程PCB中,然后下次执行时先读取PCB中存储的寄存器的数据,然后继续执行,本质就是将CPU寄存器的内容,保存到内存中!

2.2 Linux2.6内核进程调度队列(了解即可)

上图是Linux2.6内核中进程队列的数据结构,之间关系也已经给大家画出来,方便大家理解

一个CPU拥有一个runqueue

  • 如果有多个CPU就要考虑进程个数的负载均衡问题

优先级

  • 普通优先级:100~139(我们都是普通的优先级,想想nice值的取值范围,可与之对应!)
  • 实时优先级:0~99(我们这里不关心)

活动队列

  • 时间片还没有结束的所有进程都按照优先级放在该队列
  • nr_active: 总共有多少个运行状态的进程
  • queue[140]: 一个元素就是一个进程队列,相同优先级的进程按照FIFO规则进行排队调度,所以,数组下标就是优先级!
  • 从该结构中,选择一个最合适的进程,过程是怎么的呢?

1. 从0下表开始遍历queue[140]
2. 找到第一个非空队列,该队列必定为优先级最高的队列
3. 拿到选中队列的第一个进程,开始运行,调度完成!
4. 遍历queue[140]时间复杂度是常数!但还是太低效了!

  • bitmap[5]:一共140个优先级,一共140个进程队列,为了提高查找非空队列的效率,就可以用 5*32 个比特位表示队列是否为空,即位段,这样,便可以大大提高查找效率!

过期队列

  • 过期队列和活动队列结构一模一样
  • 过期队列上放置的进程,都是时间片耗尽的进程,或者是刚到运行对列中的进程。
  • 当活动队列上的进程都被处理完毕之后,对过期队列的进程进行时间片重新计算

active指针和expired指针

  • active指针永远指向活动队列
  • expired指针永远指向过期队列
  • 可是活动队列上的进程会越来越少,过期队列上的进程会越来越多,因为进程时间片到期时一直都存在的。
  • 没关系,在合适的时候,只要能够交换active指针和expired指针的内容,就相当于有具有了一批新的活动进程!

总结

在系统当中查找一个最合适调度的进程的时间复杂度是一个常数,不随着进程增多而导致时间成本增加,我们称之为进程调度O(1)算法

3.命令行参数

 命令行参数是什么?

我们看下面一段代码看下面一段代码

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

运行输出: 

可以发现main函数是可以传参的,shell 会自动讲命令行输入的一大串字符,按空格分割成小的字串,第一参数argc存命令行字串个数,第二个参数argc是一个指针数组存字串内容。

命令行参数,可以支持各种指令级别的命令行选项的设置!这样就可以理解之前学的指令,选项是什么关系了。

我们来模拟实现一个计算器

#include <stdio.h>    
#include <string.h>    
#include <stdlib.h>    
int main(int argc, char *argv[])    
{    if(argc != 4)      {      printf("Use error\nUsage: %s op[-add|sub|mul|div] d1 d2\n",argv[0]);      return 1;      }      // 你的程序一定有4个命令行参数,第一个是程序名      int x = atoi(argv[2]);      int y = atoi(argv[3]);      if(strcmp(argv[1],"-add")==0)      {      int result = x+y;      printf("%d+%d=%d\n",x,y,result);      }      else if(strcmp(argv[1],"-sub")==0)      {      int result = x-y;      printf("%d-%d=%d\n",x,y,result);      }      else if(strcmp(argv[1],"-mul")==0)      {      int result = x*y;      printf("%d*%d=%d\n",x,y,result);                                                                                                       }  else if(strcmp(argv[1],"-div")==0)      {      if(0==y)      {printf("%d/%d=error! div zero\n",x,y);}else {int result = x/y;printf("%d/%d=%d\n",x,y,result);}}else {printf("Use error,you should use the right command line\nUsage: %s op[-add|sub|mul|div] d1 d2\n",argv[0]);}return 0;
}

 运行结果:

对于为什么系统命令不用加 ./指定位置 ,而我们写的程序就需要写 ./ ,这就涉及到环境变量了,下一篇文章我们讲解环境变量。

本篇结束!


http://www.ppmy.cn/news/1264280.html

相关文章

C++ 面向对象补充

目录 初始化列表 explicit关键字 单参数构造函数 多参数构造函数 static成员 友元 内部类(不常用) 匿名对象 初始化列表 以一个冒号开始&#xff0c;接着是一个以逗号分隔的数据成员列表&#xff0c;每个成员变量后面跟 一个放在括号中的初始值或表达式。 class Date…

打工人副业变现秘籍,某多/某手变现底层引擎-Stable Diffusion简介

Stable Diffusion是2022年发布的深度学习文本到图像生成模型,它主要用于根据文本的描述产生详细图像,尽管它也可以应用于其他任务,如

UVM建造测试用例

&#xff08;1&#xff09;加入base_test 在一个实际应用的UVM验证平台中&#xff0c;my_env并不是树根&#xff0c;通常来说&#xff0c;树根是一个基于uvm_test派生的类。真正的测试用例都是基于base_test派生的一个类。 class base_test extends uvm_test;my_env e…

人工智能原理复习--搜索策略(二)

文章目录 上一篇启发式搜索与或图搜索博弈下一篇 上一篇 人工智能原理复习–搜索策略&#xff08;一&#xff09; 启发式搜索 提高一般图搜索效率的关键是优化OPEN表中节点的排序方式 最理想的情况是每次排序OPEN表表首n总在解答路径上 全局排序–对OPEN表中的所有节点进行…

Python中函数的递归调用

函数调用自己的编程方式被称为函数的递归调用。递归通常能够将一个大型的复杂问题的递归条件&#xff0c;一层一层的回溯到终止条件&#xff0c;然后再根据终止条件的运算结果&#xff0c;一层一层的递进运算到满足全部的递归条件。它能够使用少量程序描述出解题过程中的重复运…

Git 更改remote repo 地址

Git 更改remote repo 地址 我们想更换远程 repo 地址, 因为我们仓库更换了名称. git remote rename origin 查看现在的 romote 地址 # 查看 remote 地址 git remote -v origin gitgitee.com:changyubiao/attrdict.git (fetch) origin gitgitee.com:changyubiao/attrdict.gi…

SpringMVC修炼之旅(2)基础入门

一、第一个程序 1.1环境配置 略 1.2代码实现 package com.itheima.controller;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;//定义…

入门Redis学习总结

记录之前刚学习Redis 的笔记&#xff0c; 主要包括Redis的基本数据结构、Redis 发布订阅机制、Redis 事务、Redis 服务器相关及采用Spring Boot 集成Redis 实现增删改查基本功能 一&#xff1a;常用命令及数据结构 1.Redis 键(key) # 设置key和value 127.0.0.1:6379> set …