一、进程的优先级
-
什么是优先级?
- 指定一个进程获取某种资源的先后顺序
- 本质是进程获取cpu资源的优先顺序
-
为什么要有优先级
ps -lA
-
其中PRI值越低优先级越高
-
因为PRI值是由操作系统内核动态调整的,我们无法直接去调整这个值,所以我们必须通过nice值去调整它。nice值就是上图PRI后面NI。
-
因为PRI是系统内核去动态调整的,我们修改后需要经过内核的允许,如果这个PRI值超过了内核的最大限度,那么这个值就会保留在临界值。
-
我们的计算公式为:新的PRI = 进程默认PRI + nice值,这个nice值有正负数,我们可以举一个例子:一个进程的PRI为80,我们给NI值为-10,再根据上面的公式得出新的PRI为70
#include<stdio.h>
int main()
{
while(1){
}
return 0;
}
renice [number] [PID]
二、进程的四个重要概念
- 竞争性:因为cpu资源优先,所以进程难免会存在竞争行为,具体体现在优先级上。
- 性:进程运行期间,各个进程是不会相互干扰的,即使是父子进程。
- 并行:当有多个cpu时,这些cpu同时处理多个进程的行为叫做并行。
- 并发:在一段时间内,每个进程都可以被cpu处理一部分指令,这种行为称为并发。
假设cpu处理一个进程的时间为1秒,那么1个cpu处理99个进程的时间就是99秒。但是当有一台拥有3个cpu的计算机处理这99个进程时,只需要33秒。这就是并行,多个cpu同时处理多个进程。
三、上下文切换
- 每个任务运行前,CPU 都需要知道任务从哪里加载、又从哪里开始运行,这就涉及到 CPU 寄存器 和 程序计数器(PC):
如何切换?
- 将前一个 CPU 的上下文(也就是 CPU 寄存器和程序计数器里边的内容)保存起来;
- 然后加载新任务的上下文到寄存器和程序计数器;
- 最后跳转到程序计数器所指的新位置,运行新任务。
被保存起来的上下文会存储到系统内核中,等待任务重新调度执行时再次加载进来。
CPU 的上下文切换分三种:进程上下文切换、线程上下文切换、中断上下文切换。
- 把临时数据转存到操作系统的行为叫做上下文保护,把临时数据写回寄存器内的行为叫做上下文恢复。
四、环境变量
- 我们对于Linux的理解,指令就是程序,我们写的C语言代码也是一个程序,那么有一个问题,为什么Linux的指令他直接就可以在bash(终端)上运行,为什么我们写的代码生成的可执行文件
- 因为【PATH】变量没有记录我们输入的指令的位置信息,所以我们必须手动指定指令的位置。那么我们可以总结出指令(程序)是如何执行的
echo $PATH
- 可以看到上面是有各种路径每个路径是一下【:】分割,我们可以看到有一个
/usr/bin目录,那么我们写的这个程序也就可以拷贝到这个目录下就可以不指定路径直接执行了 - 第二个方法是将我当前这个目录的路径添加到这个环境变量中,这样也可以
export PATH=路径
- 那么我们如何正确的向[PATH]添加一个路径呢?我们用到下面的指令:
export PATH=$PATH:路径
-
那么为什么新开了一个终端它就又恢复了呢?
- 这是因为在我们默认查看的环境变量是内存级的
- 最开始的环境变量不是在内存中,是在对应的配置文件中,登录Linux系统的时候它会首先加载到bash进程中(内存)
-
那么这个配置文件在哪?
.bash_profile
.bashrc
/etc/bashrc
查看当前shell环境下的环境变量与内容
env
-
环境变量是随着启动操作系统时生成的,也就是说,环境变量是属于bash的。
-
指令是一个程序,在bash上执行,那么这个程序就是bash的子进程
-
我们平时所用的pwd命令就是有一个环境变量叫pwd,这个环境变量存储着用户当前的所在位置
#include<stdio.h>
#include<stdlib.h>
int main()
{
char* ret = getenv("PWD");
printf("%s\n",ret);
return 0;
}
- 我们在bash上运行的程序,是bash的子进程,而环境变量是属于bash的,子进程为什么能用父进程的环境变量?这是因为,子进程可以继承父进程的环境变量!并且,环境变量一定是全局属性的!
- 在子进程是如何继承环境变量的?子进程是不是有一个主函数?这个主函数我们平时使用时是没有参数的,但实际上它是可以带参数的!还能带三个!
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char* argv[], char* environ[])
{
return 0;
}
- 第一个参数代表的意思为:指令参数的个数(包括指令);
- 第二个参数代表的意思为:指令参数的指针数组(因为指令参数是一个字符串);
- 第三个参数代表的意思为:环境变量的指针数组(因为环境变量是一个字符串)。我们一般不使用第三个参数,而是使用操作系统提供的外部的指针数组指针【char** environ】或者是系统提供的接口函数
getenv()。
#include <stdio.h>
#include <string.h>
int main(int argc,char* argv[])
{
if(argc < 2)
{
printf("指令参数太少!\n");
return 1;
}
if(strcmp(argv[1],"-a")==0)
{
printf("执行-a\n");
}
else if(strcmp(argv[1],"-b")==0)
{
printf("执行-b\n");
}
else
{
printf("指令有误!\n");
}
return 0;
}
#include <stdio.h>
#include <string.h>
int main(int argc,char* argv[])
{
printf("%d\n",argc);
int i=0;
for(i=0;i<argc;i++)
{
printf("%s\n",argv[i]);
}
return 0;
}
#include <stdio.h>
#include <string.h>
int main(int argc,char* argv[],char* environ[])
{
int i = 0;
for(i = 0; environ[i]; i++)
{
printf("[%d]-->%s\n",i, environ[i]);
}
return 0;
}
#include <stdio.h>
#include <string.h>
int main(int argc,char* argv[])
{
extern char** environ;
int i=0;
for( i=0;environ[i];i++)
{
printf("[%d]-->%s\n",i,environ[i]);
}
return 0;
}
[变量名]=[内容]
-
我们发现使用env来查看我们设置的变量,并不能显示出结果,证明了我们刚刚设置的变量是本地变量
-
但是使用【echo】命令还可以查看到,因为echo是可以操作环境变量的,所用echo命令是可以操作所有的变量的,不管是本地变量还是环境变量。
-
子进程并没有继承父进程的本地变量,那我们如何使本地变量变成环境变量呢?我们输入下面这个指令:
export [变量名称]
- 现在我们学会了如何设置本地变量和如何把本地变量转换成环境变量了。那么如何查看本地变量呢,或者说如何查看所有的变量呢?我们使用下面这条命令:
set
unset [变量名]
五、Linux2.6内核进程调度队列
一个CPU拥有一个runqueue
如果有多个CPU就要考虑进程个数的父子均衡问题。
优先级
queue下标说明:
- 普通优先级:100~139。
- 实时优先级:0~99。
我们进程的都是普通的优先级,前面说到nice值的取值范围是-20~19,共40个级别,依次对应queue当中普通优先级的下标100~139。
注意: 实时优先级对应实时进程,实时进程是指先将一个进程执行完毕再执行下一个进程,现在基本不存在这种机器了,所以对于queue当中下标为0~99的元素我们不关心。
活动队列
时间片还没有结束的所有进程都按照优先级放在活动队列当中,其中nr_active代表总共有多少个运行状态的进程,而queue[140]数组当中的一个元素就是一个进程队列,相同优先级的进程按照FIFO规则进程排队调度。
调度过程如下:
bitmap[5]:queue数组当中一共有140个元素,即140个优先级,一共140个进程队列,为了提高查找非空队列的效率,就可以用5 × \times× 32个比特位表示队列是否为空,这样一来便可以大大提高查找效率。
总结: 在系统当中查找一个最合适调度的进程的时间复杂度是一个常数,不会随着进程增多而导致时间成本增加,我们称之为进程调度的O(1)算法。
过期队列
- 过期队列和活动队列的结构相同。
- 过期队列上放置的进程都是时间片耗尽的进程。
- 当活动队列上的进程被处理完毕之后,对过期队列的进程进行时间片重新计算。
active指针和expired指针
- active指针永远指向活动队列。
- expired指针永远指向过期队列。
由于活动队列上时间片未到期的进程会越来越少,而过期队列上的进程数量会越来越多(新创建的进程都会被放到过期队列上),那么总会出现活动队列上的全部进程的时间片都到期的情况,这时将active指针和expired指针的内容交换,就相当于让过期队列变成活动队列,活动队列变成过期队列,就相当于又具有了一批新的活动进程,如此循环进行即可。