进程是计算机科学中最深刻、最成功的概念之一。
在计算机系统上运行一个程序时,会得到一个假象,即好像程序是系统中当前运行的唯一程序一样,独占地使用处理器和内存。处理器好像是无间断地一条接一条地执行程序中的命令,而代码和数据好像是系统内存中唯一的对象。这些假象都是通过进程的概念提供的。
系统中的每个程序都运行在某个进程的上下文(context)中。上下文由程序正确运行所需的状态组成的。这个状态包括存在内存中的程序的代码和数据,以及它的栈、通用目的寄存器的内容、程序计数器、环境变量和打开文件描述符的集合。
每次用户通过向shell输入一个可执行目标文件的名字,运行程序时,shell就会创建一个新的进程,然后在这个新进程的上下文中运行这个可执行目标文件。应用程序也能够创建新进程,并且在这个新进程的上下文中运行它们自己的代码或其他应用程序。
这里不对操作系统实现进程的细节进行讨论,而是关注进程提供给应用程序的关键抽象:
- 一个独立的逻辑控制流,它提供一个程序独占地使用处理器的假象;
- 一个私有的地址空间,它提供一个程序独占地使用内存系统的假象。
1、逻辑控制流
即使在系统中通常有许多其他程序在运行,进程也可以向每个程序提供一个独占地使用处理器的假象。如果想用调试器单步执行程序,就会看到一系列的程序计数器(Program Counter,PC)的值,这些值唯一地对应于包含在程序的可执行目标文件中的指令,或是包含在运行时动态链接到程序的共享对象中的指令。这个PC值的序列叫做逻辑控制流。
程序计数器(Program Counter,PC)是计算机处理器中的寄存器,它包含当前正在执行的指令的地址(位置)。当每个指令被获取,程序计数器的存储地址加一,指向顺序中的下一个指令,如此循环执行每一条指令。当计算机重启或复位时,程序计数器通常恢复到零。
比如一个运行着三个进程的系统,如下图所示,处理器的一个物理控制流被分成了三个逻辑流,每个进程一个。图中每个竖直的条表示一个进程的逻辑流的一部分。三个逻辑流的执行是交错的。进程A运行一会,然后是进程B开始运行到完成,然后进程C运行一会,接着进程A接着运行到完成,最后进程C继续运行到结束。
上图的关键点在于进程是轮流使用处理器的。每个进程执行它的流的一部分,然后被暂时挂起,接着轮到其他进程。对于一个运行在这些进程之一的上下文中的程序,看上去就像是在独占地使用处理器。
一个逻辑流的执行在时间上与另一个流重叠,称为并发流(concurrent flow),这两个流称为并发地运行。
参考阅读:并发和并行的区别
2、私有地址空间
进程也为每个程序提供一个它独占地使用系统地址空间的假象。在一台n位地址的机器上,地址空间是2^n个可能地址的集合。进程为每个程序提供它自己的私有地址空间,一般和这个空间中某个地址相关联的那个内存字节是不能被其他进程读或写的,从这个意义上来说,这个地址空间是私有的。
尽管和每个私有地址空间相关联的内存的内容一般是不同的,但是每个这样的空间都有相同的通用结构。下图是一个 x86-64 Linux进程的地址空间的组织结构。
地址空间底部是保留给用户程序的,包括通常的代码、数据、堆和栈段。代码段总是从地址0x400000开始。地址空间顶部保留给内核(操作系统常驻内存的部分)。地址空间的这个部分包含内核在代表进程执行指令时使用的代码、数据、堆和栈。