2017-07-28 23:51:03 +0000   |     operating system process   |   Viewed times   |    

什么是进程?

首先,进程(process)是一个 正在顺序执行程序的实例。讲到实例,就不光有自身属性(程序本身逻辑流),而且还有一个当前状态(包括程序计数器,寄存器和变量等等)。

用大白话说,

有个程序叫《水浒》。我看《水浒》这个过程,就是一个进程,就是《水浒》个程序当前的一个实例。看到《王婆贪贿说风情郓哥不忿闹茶肆》这一章,我觉得潘金莲这个人物我很喜欢。所以很想去看一下《金瓶梅》好好了解一下潘金莲。但我(主机)只有一双眼睛,同时只能看一本书。所以我要先放下《水浒》进程,去打开《金瓶梅》进程,这就叫进程的切换。但是放下《水浒》之前,为了等会儿回来能接着读,需要夹一张书签记住我看到二十三回,这就是进程的当前状态(上下文)。

每个进程都有自己完整的一套逻辑地址空间。进程a访问的0xffff和进程b方位的0xffff不是同一个物理地址。

关于一个进程的地址空间布局,参考下面这张图, c-memory-layout

除了一个祖先进程,其他所有进程都是由其他某个(它的父进程)已经存在的进程执行了一个用于创建进程的系统调用而创建的。关于几个比较特殊的进程,下面再细说。

在UNIX系统中,只有一个系统调用可以用来创建进程:fork。在调用了fork后,父进程和子进程拥有相同的存储映像,相同的环境字符串,和相同的的打开文件。

进程的状态

  1. 运行态:该时刻进程实际占用CPU。
  2. 就绪态:可运行,但因为其他程序正在运行而暂时停止。
  3. 阻塞态:除非某种外部事件发生,否则进程不能运行。

三种状态间的转换示意图如下, process-three-status

这里面的内容很丰富,先记住这么个大概。

进程的终止

  1. 正常退出。自愿的。
  2. 错误退出。自愿的。比如:要编译的程序源文件不存在。
  3. 程序中的严重错误。非自愿。比如:引用的内存不存在,用零做除数等。
  4. 被其他程序杀死。当然非自愿。调用kill系统调用可以杀死某个进程。

进程表(process table)

系统维护一张记录进程元信息的表,叫 进程表(process table)。 这张表包含了随后再次启动进程需要的所有信息,包括程序计数器,堆栈指针,内存分配状况,所打开文件的状态,账号和调度信息。具体细节如下表, process-table

中断向量(interrupt vector)

与每一个I/O类关联的是一个称作 中断向量(interrupt vector) 的位置(靠近内存底部的固定区域)。它包含中断服务程序入口地址。

用大白话说,有下面几件重要的事情,

  1. 要挂起一个进程,需要一个中断信号触发。
  2. 整个中断的过程由硬件,汇编语言过程,以及C中断服务例程(它完成大部分工作)共同完成。
  3. 这个中断向量就是指向中断服务例程入口地址的表(汇编语言过程适用于所有类型的中断,作为所有C中断服务例程共同的引导程序,所以中断服务例程包含汇编和C两部分)。
  4. 为什么要这么一个表呢?因为中断服务有很多不同的类型。不同类型中断的处理过程有区别。具体见下面这张很丑的表。

interrupt-vector

中断发生后操作系统最底层的工作步骤如下,

  1. 硬件压入堆栈程序计数器(程序计数器就是记录下一个要执行的指令地址的寄存器,大白话讲就是当前程序执行到哪一步了,和看书的书签是一个道理)。
  2. 硬件从中断向量装入新的程序计数器(特定类型的中断服务例程的入口地址)。
  3. 汇编语言过程保存寄存器值(C语言做不了这个)。
  4. 汇编语言过程设置新的堆栈(这个C语言也做不了)。
  5. C中断服务例程运行。
  6. 调度程序决定下一个将运行的程序。
  7. C过程返回至汇编代码(不是刚才说的那段汇编过程)。
  8. 汇编语言过程开始运行新的当前进程(是用来启动进程的汇编过程)。

总结

  1. 进程有个属性表,叫进程表。有重新启动进程需要的所有信息。

  2. 进程上下文切换以后,前一个进程的当前状态,全纪录在进程表里。

  3. 进程上下文切换是由中断信号触发。

  4. 上下文切换的工作由硬件,汇编过程,C过程共同完成。

  5. 汇编过程及C过程的入口地址保存在中断向量里。

切换过程大白话说主要就是:

进程有一系列当前执行的状态信息,所以就有一个进程表。有完整的信息,进程随时可以恢复继续执行。所以程序切换之前就要保存这些信息。但保存信息的动作也不是自动完成。也是有一段程序来执行。怎么找到这段程序呢?就存在中断向量里。但要执行这段程序也要先加载程序,而且加载了之后会覆盖程序计数器里的下一条指令地址,这是当前程序执行到哪里的重要信息,这时候这些信息还没有保存,就会丢失,怎么办呢?所以就要有一个硬件过程做压程序计数器入栈,和加载中断程序这些动作。然后中断程序中想保存寄存器值这些动作C语言无法完成,所以需要由汇编语言过程来完成。