UNIX 中的进程是如何编号的?

UNIX 中的进程是如何编号的?

当我查看进程表 ( ) 中的 PID 编号时,我找不到任何模式ps -a,因为 PID 不是连续的数字,有时这些数字之间存在很大的“间隙”。是不是因为可能有一些进程运行时间很短,他们保留了一些PID?是否有一定范围,之后进程编号会重置?

我使用的是 Mac OS X,但我想答案通常应该适用于 UNIX。

答案1

这两方面都是的。

许多过程都是短暂的。他们获得一个 PID,运行,完成,然后 PID 从进程表中消失。

进程有时只存在几分之一秒!

通常,当程序启动时,它们会运行许多命令作为检查系统和初始化环境的一部分。

最大 PID 编号取决于系统,有时是可配置的。基本上,如果您知道将拥有大量进程,那么您可能需要增加数量,但在新操作系统上,我相信最大数量通常足以满足大多数工作负载。

PID 是进程表中的条目,进程表越多,占用的内存就越多。

看看这个相关问题:https://serverfault.com/questions/279178/what-is-the-range-of-a-pid-on-linux-and-solaris

另请注意,与此相关的是“每个用户的最大进程数”,这是一种防止恶意用户故意创建许多进程来占用整个进程表的措施。

答案2

还可以配置一些现代内核,将随机 PID 分配给新进程,以提高安全性。至少 Linux 和 FreeBSD 可以通过 sysctl 设置来做到这一点,而 OpenBSD 总是这样做。在这些情况下,您可以少说一些关于编号模式的事情。

答案3

UNIX 中的进程非常轻量、廉价并且诞生于分叉(!)

分叉这方面经常让用户、系统管理员和程序员感到困惑,尤其是那些来自所谓的“spawn”导向系统(如 Windows)的人。

Windows 无法分叉,或者更确切地说,它只能分叉线程(从技术上讲,它可以分叉,但这个 API 是 WINAPI 程序无法访问的)。

即在 UNIX 上,fork 与线程生成比 Windows 进程生成更相似。它将初始进程拆分为自身,其子进程正在运行其父进程的副本,并且两者共享相同的数据和历史记录,直到分叉点为止(然后它们各自的历史记录开始缓慢但稳定地分歧)。

正如其他答案中已经说过的,这一切(便宜、快速、轻量)意味着 PID 可以非常非常快地出现和消失。

请理解,传统的 UNIX 编写互联网服务器的方式是使用一个连接模型一个分支 - 这本质上意味着为每个连接生成一个新的 PID。

考虑一下非常繁忙的图像服务 http 服务器的上下文:每次图像显示到远程客户端时,这意味着新的 PID 被分叉图像数据被读取和发送,然后 PID 被消耗,所有这些都在分数期间非常快速地连续发生一秒钟的时间。直到今天,传统的 ftp 以及许多轻量级的 http 服务器仍然以这种方式编写。

UNIX 的另一个最初奇怪的方面是,即已不复存在。死进程不要发布他们的 PID!当给定的 PID 进程结束时,它无法以任何方式影响仍在保留的 PID。然而这个 PID 仍然被分配并占用/霸占系统进程表。

这是为什么?因为在那个进程表PID下保存着该进程的程序退出码值和各种记账计数器!

这样 CPU 和内存统计程序就可以读取其子进程的信息,即使在它们结束之后也是如此。

我们将这样的失效会计记录称为僵尸

这意味着,在处理特定的僵尸进程之前,其 PID 不能被新分叉进程重用(即,在具有非常简单的线性 PID 分配方案的 UNIX 系统中,等待处理的多个僵尸进程可能会导致“漏洞”)。

进程的父进程有责任“收割”僵尸,即消耗其记账信息,从而将僵尸的 PID 返回到“空闲 PID”池中,以供其他分叉进程重用。

考虑到所有这些事实,即使 PID 生成是一种倾向于重用第一个“未占用”PID 的简单线性算法,我们也会看到各种系统操作(如双亲僵尸收割)将导致 PID 永远不会严格线性增长。

在所有现代系统上,完全随机的 PID 分配算法都可以被激活,但 OpenBSD 是唯一一个默认情况下强制执行的系统。

因此,我们应该始终将 PID 视为完全不透明的标识符。

相关内容