进程管理这点破事

2019-07-14 09:45发布

 进程是操作系统资源分配和独立运行的基本单位,操作系统,我们可以看做是一个工厂,那进程就是工厂中的车间,线程就如车间中的工人,一个车间有很多工人,那么,一个进程由很多线程组成。进程控制块PCB被称为是进程的唯一标识,这就好比是一个车间的车间主任,他所代表的是进程的描述信息,控制信息以及进程的资源信息;所谓创建进程就是创建一个PCB,撤销一个进程就是撤销一个PCB。正由于PCB的特殊性(经常被系统访问),因此需要常驻内存。
     进程的特征有:结构特征(程序+数据+PCB);动态性(产生→执行→消亡);并发性(多个进程同时执行);独立性(PCB决定了该特征);异步性(进程往往按各自独立的,不可预知的速度执行,导致结果的不可再现性)。
     例:进程A:N+1
         进程B:print(N);N:=0;
         N的初值为1;
         有三种情况:N+1在print(N)和N:=0之前,N的值是:2,2,0                      
N+1在print(N)和N:=0之间,N的值是:1,2,0;
                     
N+1在print(N)和N:=0之后,N的值是:1,0,1;
从上面的结果可以看出,由于共享了N变量,导致了结果的不同,即不可再现性。解决办法见下述

     进程管理中最基本的功能是进程控制,而进程控制一般是由OS内核中的原语来完成;所谓原语就是指由若干条指令组成,完成一个特定功能的过程,我的理解他就是一个特殊的进程,类似于工厂的老板,由他对进程(或者说是PCB)进行控制和通信。原语的特点就是要么全做,要么全不做,执行过程中不允许被打断。
    进程之间的关系要么是竞争(互斥),要么就是合作(同步)。
    竞争关系(进程-资源-进程),系统有一个共享资源,比如说是打印机,A和B都要用。A先向系统提出请求并得到该资源使用,B此时只能将自己阻塞,等待A释放后,B由阻塞态转换为就绪态,得到打印机资源使用;这种关系不加控制的话,会导致B陷入“死等”状态、“忙等”状态
    合作关系(进程-buffer-进程-资源),比如说B要把A的结果,打印出来,那么该过程表示为:A向缓冲区buffer放一个数据,B从中取出打印。buffer为空时,B阻塞等待A将之唤醒,buffer不为空时,A阻塞等待B将之唤醒。
    临界资源指一段时间内,只允许一个进程访问的资源。临界区CS是指访问临界资源的那段代码(我的理解:如果说临界资源是一个房间的话,那临界区就是房牌)。上面例子中的不可再现性解决办法就是把N当做临界资源来访问。即定义N为锁变量
     while N==1;//空循环,1表示等待
     N=1;//相当于把房牌放在房间外,告诉他人等我用完,你才可以使用
     //这是进入区
     CS;
     //这是对临界资源进行访问
     N==0;//0表示空闲
     //这是退出区
  从上可以总结出同步机制的四条准则:空闲让进(N=0时,进程进入CS);忙则等待(N=1时,表示临界资源正被访问);有限等待(进程进入CS的这个请求在有限时间内满足,以免“死等”);
  上述策略没有从根本上解决互斥访问的问题,没有执行让权等待,也不是原语操作,因此执行过程中可能中断。
  让权等待(进程不能进入CS时,马上释放CPU资源,以免“忙等”)
  为了解决上面的问题,出现了信号量(Semaphores),它与变量的区别是:只能有赋值、P操作、V操作这三种,不能用表达式(如:S=S+1)。
  整形信号量定义了一个表示可用资源数目的整型量S(这个整型量只有原语才能访问),他解决互斥问题的操作为:
   wait(S)://也叫P操作或者减1操作
             while S<=0 do no-op ;//重复检测,直到S>0,有了可用的资源
             S:=S-1; 
             //这是进入区
   signal(S)://也叫V操作或者加1操作
             S:=S+1;
            //这是退出区 
   这种信号量也没有解决“忙等”的问题,只是把普通的操作变成了原语操作,保证了执行过程中不可中断。
   记录型信号量彻底解决了忙等的问题。类似于C++中的结构体类型,他定义了一个记录型的变量。具体实现操作如下:
    type semaphore =record //定义一个记录型变量 semaphore
                    value:integer;//当前可用的资源数目
                    L:list of process;//阻塞进程队列
    end
    procedure wait(S)
              var S: semaphore;//S是semaphore
              begin
              S.value = S.value-1;
              if S.value < 0 then block (S.L);
              //先减1后判断,注意,小于0才阻塞,等于0时,剩下最后一个可用的资源,由此(block操作)可见,执行了让权等待。
              CS
              end 
    procedure signal (S)
              var S :semaphore;
              begin
              S.value = S.value + 1;
              if S.value < = 0 then wakeup (S.L)
              //先加1后判断,若小于等于0,表示阻塞队列中有进程等待刚释放资源的进程来唤醒他进入CS区
              CS
              end
   这种信号量针对的是进程只共享一种临界资源的情况,假如,现在有两个进程X、Y,他俩都要求必须得到A、B这两个资源才执行;那么就出现了下面的情况:X的得到的A资源,但B资源被Y抢占了,X说:Y你把B释放,让我先用;Y就说:凭什么我释放B资源,你把A释放,我先用,用完你再用。这就产生了一个新词:“死锁”。解决办法就是引进---AND型信号量。这种信号量的基本思想是:系统将进程要用的资源一次性全分配给他,使用完之后一起释放,简单的说,就是要么全给你,要么一个也不给你。
   综上,信号量可以实现进程间的竞争关系或者叫互斥。实现办法就是定义一个互斥信号量mutex,并设其初值为1。
    var mutex:semaphore:=1;
        begin 
    wait(mutex);//减1操作,P操作
    CS
    signal(mutex);//加1操作,V操作
从上可以看出,wait(mutex)和signal(mutex)必须是成对出现的,少了P操作,就不能实现互斥访问,少了V操作,临界资源就得不到释放,等待该资源的阻塞进程就得不到唤醒。这种信号量叫互斥信号量
    利用信号量实现进程间的合作关系或者叫同步。比如
A向缓冲区buffer放一个数据,B从中取出打印。buffer为空时,B阻塞等待A将之唤醒,buffer不为空时,A阻塞等待B将之唤醒。这个过程我们可以设置两个信号量empty和full;empty的初值设为1,full的初值设为0
   A:wait(empty);
      buffer
      signal(full);
   B:wait (full);
      buffer
      signal (empty); 
从上可以看出,wait()、signal()分属于不同的进程,这种信号量叫私有信号量。
     这种信号量,由于分布在各个进程当中,给系统的管理带来了麻烦,导致系统死锁。为了解决该问题,引进了---管程。
     管程供进程调用,可以看做是工厂的公用会议室,供车间使用。
附例:下图的实现可以表示成
图片  有7条有向边,故定义7个信号量a,b,c,d,e,f,g,并设其初值都为0。
     var a,b,c,d,e,f,g:semaphore: =0,0,0,0,0,0,0;
     begin
          begin S1;signal (a);signal (b);end;
          begin wait (a);S2;signal (c);signal (d);end;
          begin wait (b);S3;signal (e);end;
          begin wait (c);S4;
signal (f);end;
          
begin wait (d);S5;signal (g);end;
          begin wait (e);wait (f);wait (g);S6;end
     end