Linux进程的优先级&&环境变量&&上下文切换&&优先级队列 ...

打印 上一主题 下一主题

主题 1856|帖子 1856|积分 5572

一、进程的优先级



  • 什么是优先级?

    • 指定一个进程获取某种资源的先后顺序
    • 本质是进程获取cpu资源的优先顺序

  • 为什么要有优先级

    • 进程访问的资源(CPU)是有限的

   操作体系关于调治和优先级的原则:分时操作体系,根本的公平,如果进程由于长时间不被调整,就造成了饥饿问题
  

  • Linux的优先级特点以及检察方式



  • 检察进程的优先级
  1. ps -lA
复制代码



  • PRI:进程优先级
  • NI:进程优先级的修正数据



  • 此中PRI值越低优先级越高
  • 由于PRI值是由操作体系内核动态调整的,我们无法直接去调整这个值,所以我们必须通过nice值去调整它。nice值就是上图PRI后面NI。
  • 由于PRI是体系内核去动态调整的,我们修改后必要经过内核的答应,如果这个PRI值超过了内核的最大限度,那么这个值就会保留在临界值
  • 我们的盘算公式为:新的PRI = 进程默认PRI + nice值,这个nice值有正负数,我们可以举一个例子:一个进程的PRI为80,我们给NI值为-10,再根据上面的公式得出新的PRI为70



  • 那么如何修改呢?我们可以写一个代码来看一下:
  1. #include<stdio.h>
  2. int main()
  3. {
  4.   while(1){
  5.   }
  6.   return 0;
  7. }
复制代码


  • 我们可以检察它的PRI和NI值



  • 我们可以通过下面的下令进行修改
  • number为想要的nice值,PID为要操作的进程
  1. renice [number] [PID]               
复制代码


  • 首先检察一下该进程的id



  • 然后进行修改~



  • 修改后就变成了PRI是70,NI是-10

二、进程的四个告急概念



  • 竞争性:由于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(终端)上运行,为什么我们写的代码天生的可执行文件



  • 在我们运行步伐的时候,必要知道此步伐在哪个位置
  • 在Linux的中的下令,它为什么不必要指定路径来执行呢?是由于有个叫【PATH】的环境变量,在我们输入指令后,会在指定路径下查找,如果找不到要执行的指令就会返回错误【command not found】



  • 由于【PATH】变量没有记录我们输入的指令的位置信息,所以我们必须手动指定指令的位置。那么我们可以总结出指令(步伐)是如何执行的



  • 我们可以检察一下PATH下有哪些路径
  1. echo $PATH
复制代码



  • 可以看到上面是有各种路径每个路径是一下【:】分割,我们可以看到有一个/usr/bin目录,那么我们写的这个步伐也就可以拷贝到这个目录下就可以不指定路径直接执行了
  • 第二个方法是将我当前这个目录的路径添加到这个环境变量中,这样也可以



  • 我们可以用下面的这条指令来修改体系变量
  1. export PATH=路径
复制代码



  • 发现我们刚刚检察的变量不在了,ls也无法执行了



  • 这个时候不要慌,我们可以另外再开一个终端再看



  • 那么我们如何精确的向[PATH]添加一个路径呢?我们用到下面的指令:
  1. export PATH=$PATH:路径
复制代码


  • 这就完成了添加一个环境变量的操作



  • 那么为什么新开了一个终端它就又恢复了呢?

    • 这是由于在我们默认检察的环境变量是内存级
    • 最开始的环境变量不是在内存中,是在对应的设置文件中,登录Linux体系的时候它会首先加载到bash进程中(内存)

  • 那么这个设置文件在哪?
  1. .bash_profile # 当前登录用户环境变量
  2. .bashrc       # 当前登录用户环境变量
  3. /etc/bashrc   # 全局环境变量
复制代码

检察当前shell环境下的环境变量与内容

  1. env
复制代码



  • 环境变量是随着启动操作体系时天生的,也就是说,环境变量是属于bash的。
  • 指令是一个步伐,在bash上执行,那么这个步伐就是bash的子进程
  • 我们平时所用的pwd下令就是有一个环境变量叫pwd,这个环境变量存储着用户当前的所在位置




  • 我们也可以自己实现一个pwd指令
  • 在实现的时候必要了解一个函数getenv
    ,我们用man手册检察一下

  1. #include<stdio.h>#include<stdlib.h>int main(){  char* ret = getenv
  2. ("PWD");  printf("%s\n",ret);  return 0;}
复制代码


  • 可以看到我们就实现了这个



  • 我们在bash上运行的步伐,是bash的子进程,而环境变量是属于bash的,子进程为什么能用父进程的环境变量?这是由于,子进程可以继承父进程的环境变量!并且,环境变量一定是全局属性的!



  • 在子进程是如何继承环境变量的?子进程是不是有一个主函数?这个主函数我们平时利用时是没有参数的,但实际上它是可以带参数的!还能带三个!
  1. #include<stdio.h>#include<stdlib.h>int main(int argc, char* argv[], char* env
  2. iron[]){  return 0;}
复制代码


  • 第一个参数代表的意思为:指令参数的个数(包括指令);
  • 第二个参数代表的意思为:指令参数的指针数组(由于指令参数是一个字符串);
  • 第三个参数代表的意思为:环境变量的指针数组(由于环境变量是一个字符串)。我们一样平常不利用第三个参数,而是利用操作体系提供的外部的指针数组指针【char** env
    iron】大概是体系提供的接口函数getenv
    ()。



  • 我们就可以实现一个带参数的指令,就像ls类似的
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main(int argc,char* argv[])
  4. {
  5.     if(argc < 2)
  6.     {
  7.         printf("指令参数太少!\n");
  8.         return 1;
  9.     }
  10.     if(strcmp(argv[1],"-a")==0)
  11.     {
  12.         printf("执行-a\n");
  13.     }
  14.     else if(strcmp(argv[1],"-b")==0)
  15.     {
  16.         printf("执行-b\n");
  17.     }
  18.     else
  19.     {
  20.         printf("指令有误!\n");                                                                                                                                                        
  21.     }
  22.     return 0;
  23. }
复制代码



  • 我们可以再写一个代码来验证一下
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main(int argc,char* argv[])
  4. {
  5.     printf("%d\n",argc);
  6.     int i=0;                                                                                                                                                                           
  7.     for(i=0;i<argc;i++)
  8.     {
  9.         printf("%s\n",argv[i]);
  10.     }
  11.     return 0;
  12. }
复制代码



  • 从上面 可以看出 [argc]是存储指令参数的个数的(包括指令),[char* argv[]]这个指针数组是存储指令参数的(包括指令)
  • 对于第三个参数,是一个指针数组,存储的是各个环境变量的内容,由于这些内容是字符串常量,而体现字符串常量通常利用其首字符地址
  • 我们是很少利用第三个参数的,由于这个数组存储了所有的环境变量,想要找到特定的环境变量还是挺困难的,那么我们利用这段代码,证实第三个参数存储了环境变量:
  1. #include <stdio.h>#include <string.h>int main(int argc,char* argv[],char* env
  2. iron[]){    int i = 0;                                                                                                                                                                             for(i = 0; env
  3. iron[i]; i++)    {        printf("[%d]-->%s\n",i, env
  4. iron[i]);    }    return 0;}
复制代码



  • 大概利用另一种写法可以完成
  1. #include <stdio.h>#include <string.h>int main(int argc,char* argv[]){    extern char** env
  2. iron;    int i=0;                                                                                                                                                                             for( i=0;env
  3. iron[i];i++)    {        printf("[%d]-->%s\n",i,env
  4. iron[i]);    }    return 0;}
复制代码



  • 环境变量是具有全局属性的,也就意味着子进程只能继承父进程的具有全局属性的环境变量。称作本地变量。如何设置本地变量呢?我们只必要在bash上面按这个格式敲指令:
  • 变量中间不能有空格
  1. [变量名]=[内容]      
复制代码



  • 我们发现利用env
    来检察我们设置的变量,并不能体现出效果,证实了我们刚刚设置的变量是本地变量
  • 但是利用【echo】下令还可以检察到,由于echo是可以操作环境变量的,所用echo下令是可以操作所有的变量的,不管是本地变量还是环境变量。
  • 子进程并没有继承父进程的本地变量,那我们如何使本地变量变成环境变量呢?我们输入下面这个指令:
  1. export [变量名称]     
复制代码



  • 现在我们学会了如何设置本地变量和如何把本地变量转换成环境变量了。那么如何检察本地变量呢,大概说如何检察所有的变量呢?我们利用下面这条下令:
  1. set
复制代码



  • 取消变量可以利用下面这条下令
  1. unset
  2. [变量名]
复制代码

五、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规则进程列队调治。
调治过程如下:

  • 从0下标开始遍历queue[140]。
  • 找到第一个非空队列,该队列必定为优先级最高的队列。
  • 拿到选中队列的第一个进程,开始运行,调治完成。
  • 接着拿到选中队列的第二个进程进行调治,直到选中进程队列当中的所有进程都被调治。
  • 继承向后遍历queue[140],探求下一个非空队列。
bitmap[5]:queue数组当中一共有140个元素,即140个优先级,一共140个进程队列,为了提高查找非空队列的服从,就可以用5 × \times× 32个比特位体现队列是否为空,这样一来便可以大大提高查找服从。
总结: 在体系当中查找一个最合适调治的进程的时间复杂度是一个常数,不会随着进程增多而导致时间成本增加,我们称之为进程调治的O(1)算法。
过期队列



  • 过期队列和活动队列的结构雷同。
  • 过期队列上放置的进程都是时间片耗尽的进程。
  • 当活动队列上的进程被处理完毕之后,对过期队列的进程进行时间片重新盘算。
active指针和expired指针



  • active指针永世指向活动队列。
  • expired指针永世指向过期队列。
由于活动队列上时间片未到期的进程会越来越少,而过期队列上的进程数目会越来越多(新创建的进程都会被放到过期队列上),那么总会出现活动队列上的全部进程的时间片都到期的情况,这时将active指针和expired指针的内容交换,就相称于让过期队列变成活动队列,活动队列变成过期队列,就相称于又具有了一批新的活动进程,如此循环进行即可。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

王國慶

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表