2009-02-07

CPU的进程管理

  作为基础知识,最近几天会稍微看看操作系统原理,所以会记录一些关于操作系统的东西。虽然我手头的这一本是Solaris原理,但是,之后回学校还有一本很厚的Windows原理。。。


  这里把CPU成为处理机,进程是处理机的基本概念,进程就是一个程序在一个数据集合上的一次动态执行的过程,所以,所谓进程,立竿见影的说法就是进程是程序的执行,是一种状态变化的过程。

  操作系统用来记录进程相关信息的一个数据接口就是进程控制块(process control block, PCB)这个东西包括进程描述信息、进程控制信息、资源占用信息和处理机现场保护结构:

  • 进程描述信息包括:进程标识符(process ID)、进程名、用户标识符(user ID)和进程组关系(process group)
  • 进程控制信息包括:当前状态、优先级、代码执行入口地址、运行统计信息(执行时间和页面调度)、进程阻塞原因等

  • 资源占用信息就是进程占用的系统资源列表

  • 处理机现场保护结构保存寄存器值(通用寄存器、程序计数器(PC)、状态字(PSW)、栈指针等)


进程的状态转换


  进程从创建到终止的过程是不断变化的,一般操作系统会把进程分成若干种状态,定义各种状态间转换的条件。我们先看一个单挂起进程模型:


  • New即进程创建
  • Admit即提交:进程创建后被提交,新的进程就进入了Ready的状态
  • Dispathc即调度运行:系统从就绪进程表中选择一个进程,进入运行状态
  • Release:进程执行完成或异常中断就会被release就进入Exit状态,异常中断被release时是Abort


  • Timeout:当本进程的时间片用完了或者这个进程被高优先级的进程打断了,就Timeout,进入Ready状态


  • Event Wait:进程要求的事件还没有出现,这个时候进程就被Blocked(阻塞)了,因而暂停运行了
  • Event Occur:进程等待的事件发生了,这个时候阻塞的进程就进入就绪队列
  • Suspend即挂起:从内存转到外存,在单挂起进程模型里,进程从阻塞到阻塞挂起,是由于没有进程处于就绪状态或者有就绪的进程要求更多的内存资源的时候,就会进行这种转换,以求能够提交新进程或运行就绪进程
  • Activate即激活:把一个进程从外存转到内存,在单挂起进程模型里,进程从阻塞挂起到就绪是由于被阻塞挂起的进程是目前优先级最高的进程。所以被系统开始执行。

  单挂起进程模型基本就是如此,而在双挂起进程模型中,情况复杂的多,但是原理也是一样,区别就是所有挂起的进程都必须要从就绪挂起的状态恢复,而进程初始化后也会检查是否有比自己优先级更高的进程在执行,有的话就先把自己就绪挂起了。只看图,不需多说明:




进程控制:

  操作系统对进程的控制是依据用户命令和系统状态来决定的,进程的控制就是进程状态的转换。

  操作系统完成初始化后系统就可以创建进程,在进程创建过程中,操作系统进行PCB的维护,每一个进程都可以利用系统调用功能来创建新的进程,创建者就是父进程,被创建的就是子进程,按照子进程是否覆盖父进程和是否加载新程序,子进程被分为三类:

  • fork:复制现有进程的上下文(之后会不同),产生新的进程

  • spawn:产生新进程,加载新程序

  • exec:加载新程序,覆盖自身,不产生新进程

  进程退出时,操作系统要负责删除系统维护的相关数据结构,并且回收资源。Solaris支持fork和exec这两类进程创建的系统调用,同时使用vfork来改善创建并加载新程序的性能。

  除了创建和推出,进程还有阻塞和唤醒的控制。当出现等待io或其他事件时进程会被阻塞。

  在Unix中,与进入阻塞的相关系统调用有:

  • sleep:暂停一段时间,在指定的时间内,本进程将被阻塞,程序上的返回值是实际的阻塞时间
  • pause:暂停并等待信号,本进程将等待信号,若收到信后后就恢复执行或终止进程(收到终止信号),程序上不返回

  • wait:等待子线程暂停或终止,本进程将被阻塞,等待子进程结束,当福进程创建多个子进程,并且有子进程退出了,父进程的wait函数将在第一个子进程结束的时候返回子进程的ID

  • kill:发送信号到某个进程或一组进程,Solaris中信号的定义在“/usr/src/bin/include/sys/signal.h"这个信号和kill命令发给进程的信号是一样的。


在操作系统中,进程的引入提高了系统资源的利用率,但是要进一步提高进程的并发性时,切换开销过大,使得进程间通信的效率收到很大限制,因此,就引入了线程,来简化进程间通信,提高进程内的并发程度。


  在引入线程的操作系统中,进程被作为资源分配的的那位,而线程才是处理机调度的对象。一个进程内部允许有多个并发执行的线程。一个线程是进程中的一个控制点,同一个进程内的各个线程可以访问这个进程的所有资源,且这些线程的上下文空间很多地方相同,所以能够大幅度简化内部通信。


  同进程相比,线程的优点在于,创建和终止快速,同进程内线程切换比进程切换快速,可以不通过内核进行通信。为了区分清楚线程和进程,我们必须注意他们的不同之处:


  • 地址空间资源方面:进程的地址空间是相互独立的,而同一进程内的各线程共享这个进程的地址空间

  • 通信关系方面:进程间通信需要使用操作系统提供的进程间通信机制,而同一进程内的各线程可以直接读写进程数据段来通信。

  • 调度切换:统一进程中的线程上下文切换比进程快得多

在操作系统中,对线程的支持也有不同的方式,其中一种是在操作系统的内核和用户程序两个层次提供线程的控制机制:


  • 内核线程(kernel-level thread):由内核创建和销毁,内核维护进程和线程的上下文信息,线程的切换有内核完成,如果一个内核线程由于io被阻塞,不会影响其他线程。此时处理机的时间片分配对象也是线程,多线程的进程能够获得更多的CPU时间。内核线程需要操作系统支持。

  • 用户线程(user-level thread):由应用进程利用线程库提供的函数来控制的线程。操作系统不必知道用户线程的存在。用户线程的切换不需要内核特权,线程调度算法可以为应用特别优化。但是由于系统不知道用户线程的存在,如果一个线程在进入系统调用后阻塞了,整个进程都必须等待。此外,处理机分配给进程的时间片是一定的情况下,线程多了,每个线程的CPU时间就少了。用户线程是不需要操作系统提供支持的,因此可以在不支持内核线程的系统中使用。

  • 轻量级进程(Light Weight Process):由内核支持的用户线程,一个进程可以有多个轻量级进程,每个轻量级进程由一个单独的内核线程来支持。由于同时提供内核线程控制和用户线程库,这种实现有着两者的有点。

  Solaris10是一个多线程系统,操作系统的任务以内核线程的形式执行。多线程进程中,用户线程和LWP一起创建,LWP是内核对象,他使用户线程能够独立于统一进程中的其他线程执行,并且能够进入内核,在Solaris中,内核线程是调度和执行的单位,因此进程中的用户线程必须链接到内核线程才能执行。在Solaris10中,线程模型使用的是1:1的模型,所有的线程都是LWP


  由于Solaris的这种机制,所以同上面介绍的一样,在Solaris中,进程被定义为线程的状态容器,因此,系统是按照进程来分配资源的,所以也就有了能够控制进程可使用资源的方法。在Solaris中,进程对象的名字是proc_t,用户可以使用进程文件系统的proc的命令获得进程的信息和控制进程。常用的系统命令 ps、prstat和proc都是在为文件系统/proc之上构建的。


进程的终止:

  1. 程序调用了exit函数,这个时候整个进程的所有线程都会被终止。线程调用了thr_exit,这个时候只有线程终止。

  2. 进程执行完毕

  3. 收到信号,被终止。

  当进程终止的时候,会执行内核退出函数,释放分配给进程的所有资源,进程进入SZOME(zombie)状态,等待父进程执行wait系统调用来收集退出状态,当wait执行完后,这个进程彻底消失。这个也是init进程继承sched进程的目的。

没有评论: