目录
1.进程优先级
1.1 基本概念
1.2 查看系统进程
1.3 修改进程优先级的命令
2.进程间切换
2.1 相关概念
2.2 Linux2.6内核进程调度队列(了解即可)
3.命令行参数
1.进程优先级
1.1 基本概念
- cpu资源分配的先后顺序,就是指进程的优先权(priority)。
- 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
- 还可以把进程运行到指定的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;
}
运行结果:
对于为什么系统命令不用加 ./指定位置 ,而我们写的程序就需要写 ./ ,这就涉及到环境变量了,下一篇文章我们讲解环境变量。
本篇结束!