进程的经典定义就是一个执行中程序的实例。系统中的每个程序都运行在某个进程的上下文 (context) 中。上下文是由程序正确运行所需的状态组成的。这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合。

进程为应用程序提供了关键的抽象:

  • 一个独立的逻辑控制流,它提供一个假象,好像我们的程序独占地使用处理器。
  • 一个私有的地址空间,它提供一个假象,好像我们的程序独占地使用内存系统。

逻辑控制流

如果用调试器单步执行程序,我们会看到一系列的程序计数器 (PC) 的值,这些值唯一地对应于包含在程序的可执行目标文件中的指令,或是包含在运行时动态链接到程序的共享对象中的指令。这个PC值的序列叫做逻辑控制流,或者简称逻辑流。

进程是轮流使用处理器的,每个进程执行它的一部分,然后被抢占(preemepted)(暂时挂起),然后轮到其他进程。对于一个运行在这些进程之一的上下文中的程序,它看起来就像是在独占地使用处理器。唯一的反面例证是,如果我们精确地测量每条指令使用的时间,会发现在程序中的一些指令的执行之间,CPU好像会周期性地停顿。然而,每次处理器停顿,它随后会继续执行我们的程序,并不改变程序内存位置或寄存器的内容。

并发流

一个逻辑流的执行在时间上与另一个流重叠,成为并发流(concurrent flow),这两个流被称为并发地运行。

多个流并发地执行的一般现象被称为并发(concurrency)。一个进程和其他进程轮流运行的概念称为多任务(multitasking)。一个进程执行它的控制流的一部分的每一时间段叫做时间片(time slice)。因此,多任务也叫做时间分片(time slicing)。

并发流的思想与流运行的计算机核数或计算机数无关。并行流是并发流的一个真子集。如果两个流并发地运行在不同的处理器核和或计算机上,那么我们称它们为并行流(parallel flow),它们并行地运行(running in parallel),且并行地执行(parallel execution)。

私有地址空间

进程为每个程序提供它自己的私有地址空间。一般而言,和这个空间中某个地址相关联的那个内存字节是不能被其他进程读写的。

进程地址空间

地址空间底部是保留给用户程序的,包括通常的代码、数据、堆和栈段。代码段总是从地址0x400000开始。地址空间顶部保留给内核(操作系统常驻内存的部分)。地址空间的这个部分包含内核在代表进程执行指令时(比如当应用程序执行系统调用时使用的代码、数据和栈)。

用户模式和内核模式

处理器提供了一种机制,限制一个应用可以执行的指令以及它可以访问的地址空间范围。

处理器通常是用某个控制寄存器中的一个模式位(mode bit)来提供这种功能的,该寄存器描述了进程当前享有的特权,当设置了模式位时,进程就运行在内核模式中(有时叫做超级用户模式),一个运行在内核模式的进程可以执行指令集中的任何指令,并且可以访问系统中的任何内存位置。

没有设置模式时,进程就运行在用户模式中,用户模式中的进程不允许执行特权指令,比如停止处理器、改变模式位或发起一个IO操作。也不允许用户模式中的进程直接引用地址空间中内核区以内的代码和数据,任何这样的尝试都会导致致命的保护故障。用户程序必须通过系统调用接口,间接地访问内核代码和数据。

运行应用程序代码初始时是在用户模式中的。进程从用户模式变为内核模式的唯一办法是通过诸如中断、故障或陷入系统调用这样的异常,当异常发生时,控制传递到异常处理程序,处理器将模式从用户模式变为内核模式,处理程序运行在内核模式中,当它返回到应用程序代码时,处理器把模式从内核模式改回到用户模式。

Linux提供了一种聪明的机制,叫做/proc文件系统,它允许用户模式进程访问内核数据结构的内容。/proc文件系统将许多内核数据结构的内容输出为一个用户程序可以读的文本文件的层次结构。比如,你可以使用/proc文件系统找出一般的系统属性,或者某个特殊的进程使用的内存段。

上下文切换

操作系统内核使用一种称为上下文切换(context switch)的较高层形式的异常控制流来实现多任务,上下文切换机制是建立在那些较低层异常机制之上的。

内核为每一个进程维持一个上下文。上下文就是内核重新启动一个被抢占的进程所需的状态,它由一些对象的值组成,这些对象包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构,比如描述地址空间的页表,包含有关当前进程信息的进程表,以及包含进程已打开文件的信息的文件表。

在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个进程。这种决策就叫做调度,是由内核中被称为调度器的代码处理的。当内核选择一个新的进程运行时,我们说内核调度了这个进程,在内核调度了一个新的进程运行后,他就抢占当前进程,并使用一种称为上下文切换的机制,来将控制转移到新的进程。上下文切换,1保存当前进程,2上下文恢复某个进程的上下文,3将控制传递给这个新恢复的进程。

当内核代表用户执行系统调用时,可能会发生上下文切换,如果系统调用因为等待某个事件发生而阻塞,那么内核可以让当前进程休眠,切换到另一个进程。中断也可能引发上下文切换,所有系统都有某种周期性定时器中断的机制,通常为每一毫秒或者每10毫秒,每次定时器发生中断时,内核就能判定当前进程已经运行了足够长的时间,并切换到一个新的进程。