4
本文原始地址:https://is.daryl.moe/2017/12/...

一个进程就是一个正在执行的程序实例,它包括程序计数器、寄存器以及变量的当前值。一个程序运行,它的逻辑计数器装入 CPU 的程序计数器中;一个程序暂停,CPU 的程序计数器被保存在内存的逻辑程序计数器中,在下次运行前不占用 CPU。

要特别注意的是,进程并不是在 CPU 中一直运行的,CPU 会在各进程之间来回切换,所以每个进程执行的速度是不确定的。所以,大多数进程并不受 CPU 多道程序设计或其它进程相对速度的影响。

进程的创建和终止

有四种情况会导致进程的创建,它们分别是系统初始化、正在运行的程序执行了创建进程的系统调用、用户请求创建一个新进程、一个批处理作业的初始化。拿 Linux 为例,Linux 启动时的第一个进程是 0 号进程,它是所有进程的祖先。其次是 1 号进程,它是所有用户进程的祖先。

我们都知道 Nginx。当我们启动 Nginx 后,它一直在默默地监听端口执行 WEB 服务,而我们没有感知。这一类进程,便是守护进程。

任何进程,都可以创建一个新的进程,这个进程便是子进程,执行的是 fork 系统调用。进程可以 fork 子进程,子进程共享父进程的数据,但是,子进程对数据做的任何修改,对于父进程,都是不可见的。子进程的内存地址空间,是父进程的副本,是不同的地址空间。

进程只能有一个父进程,但是一个进程可以有多个子进程。在 UNIX 中,进程不能剥夺其子进程的继承权。

可写的空间是不共享的,共享的空间是不可写的。子进程可以共享父进程的内存空间,但是要通过写时复制共享。即在修改部分内存,先要明确地复制,以确保发生在私有的内存区域。

进程终止通常由下面的情况引起,正常退出、出错退出、严重错误、被其他进程杀死。其中后面两种情况是非自愿的。Linux 中自愿终止进程可以通过调用 exit 系统调用完成。

进程的状态

进程由三种状态,运行、阻塞和就绪。

处在运行态的进程,在这个时刻实际占用 CPU。

处在就绪态的进程具备可以运行条件,但是因为其它进程正在运行,而被操作系统暂停运行。

处在阻塞态的进程,因该进程调用了本地阻塞的系统调用,导致暂停运行。处在阻塞态的进程,不具备可以运行的条件。除非外部某实践发生,例如本地阻塞的调用完成,方能够转换为就绪态,等待操作系统调度。

进程的实现

操作系统维护这一个进程表,每一个进程的详细信息都保存在这张进程表中。包括程序计数器、堆栈指针、内存分配情况、文件打开情况、中断向量等。

如前面所说,每个进程都不是一直在 CPU 中运行地。其中就绪态和阻塞态是非运行状态。当操作系统将正在运行的进程切换为就绪态,或者进程因为调用了本地阻塞的系统调用而进入阻塞态时,其运行信息被保存在进程表中。当操作系统重新切换进程为运行状态时,将进程表中的信息恢复,就像进程没有中断一样。

以 IO 为例。当一个进程 A 调用了网络 IO 的系统调用时,由于该系统调用时阻塞的,于是操作系统将其切换为阻塞态,并且将它的现场都保存在进程表中,并将此地址与中断向量映射保存。然后,切换 B 进程。一段时间过去,此时可能是 C 进程在 CPU 中运行,A 进程调用的 IO 完成了,则该硬件发生了一个中断。此时,操作系统将中断正在运行的 C 进程,同时通过中断向量找到进程表中的 A 进程的现场并恢复到 A 进程没有中断时的状态,继续运行。

一个进程,在其生命周期中,会被中断数千次。每次中断后恢复运行,都会恢复到未中断时的状态。

关于进程变慢的误区

这里只说说自己的理解。在工作的时候,偶然听到同事这样说,意思大致是这样的:一个进程在网络请求的时候,会影响其它进程的速度,导致其它进程变慢。这是不对的。一个进程,在遇到本地阻塞调用时,会中断。这时,它的现场被保存在了进程表中,CPU 运行其它的进程。不存在一个进程会导致其它进程运行变慢的可能,只会存在调度因为进程数量变多而变慢的情况,它们是不一样的。

本文是作者阅读《现代操作系统》的一些总结与理解,谨此记录下来已被日后翻阅。同时,也分享给各位希望了解这些知识的同道者们。由于作者水平有限,如有错误之处,望不吝赐教,谨表感谢。

daryl
4.7k 声望185 粉丝

2018 年上半年要做的事情: