如何在Linux中为特定程序设置进程ID

28

我在想是否有办法在运行应用程序之前,强制Linux使用某个特定的进程ID给它。我需要提前知道进程ID。


4
闻起来像一个 XY 问题。你为什么需要提前知道 pid? - user529758
根据我对Linux和Unix的了解,如果你能做到那个,我会感到惊讶,但是一旦进程运行起来,有很多方法可以找出给定进程的PID。也许你应该解释一下你想要做什么,并听取替代方案的建议。 - deStrangis
2
简短的回答是不。 - devnull
你真的需要提前设置PID吗?还是仅通过与另一个进程通信(例如在父进程中从fork的返回值或在子进程中在forkexec *之间,甚至在exec *之后)来传递不透明生成的PID就足够了? - David Foerster
http://unix.stackexchange.com/questions/131514/run-a-process-to-particular-dedicated-pid-only - Ciro Santilli OurBigBook.com
7个回答

34

实际上,有一种方法可以实现这个需求。自内核3.3以来,在CONFIG_CHECKPOINT_RESTORE设置中(大多数发行版都已设置),存在/proc/sys/kernel/ns_last_pid文件,其中包含内核生成的最后一个pid。因此,如果要为派生程序设置PID,则需要执行以下操作:

  1. 打开/proc/sys/kernel/ns_last_pid并获取fd
  2. 使用LOCK_EX锁定它
  3. 写入PID-1
  4. 派生

完成!子进程将拥有您想要的PID。另外,请不要忘记解锁(使用LOCK_UN进行加锁)并关闭ns_last_pid。

您可以在我的博客这里查看C代码。


C代码是CRIU项目的一部分。并且文件/proc/sys/kernel/ns_last_pid在所有系统中都不存在。因此,该代码在Ubuntu上无法工作。有没有办法让它在Ubuntu上运行? - feeling_lonely
@hebbo 代码应该能在相对较新的 Ubuntu 上运行。已测试通过 14.04 和 14.10 版本。 - Ruslan Kuprieiev
2
我喜欢这个想法,但在大多数实际情况下,这是一个可怕的黑客。 :-D - David Foerster

9

正如许多人已经建议的那样,您不能直接设置PID,但通常shell有知道最后一个分叉进程ID的工具。

例如,在bash中,您可以在后台运行可执行文件(附加&),并在变量$!中找到其PID。 示例:

$ lsof >/dev/null &
[1] 15458
$ echo $!
15458

7
在CentOS7.2上,您可以简单地执行以下操作:
假设您想要使用PID 1894来执行sleep命令。
sudo echo 1893 > /proc/sys/kernel/ns_last_pid; sleep 1000

(但是请记住,如果在echo和sleep命令之间的极短时间内出现另一个进程执行,您可能会得到一个PID为1895+的PID。我已测试了数百次,从未发生过这种情况。如果您想保证PID,您需要在写入文件后锁定该文件,执行sleep,然后按照Ruslan上面的建议解锁文件。)


1
请注意,对于非根用户,sudo echo 1893 > /proc/sys/kernel/ns_last_pid 不起作用,因为写入文件的是Bash而不是 echo。使用 echo 1893 | sudo tee /proc/sys/kernel/ns_last_pid 或以 root 用户身份运行 Bash 命令都可以。 - TheTechRobo the Nerd

3

无法强制使用特定的PID来运行进程。正如维基百科所说:

进程ID通常按顺序分配,从0开始递增到系统到达的最大值。一旦达到此限制,分配将重新开始,并在300处增加。在Mac OS X和HP-UX中,分配将在100处重新开始。但是,对于这个和后续的传递,仍然分配给进程的任何PID都将被跳过。


感谢提供有用的答案。我尝试根据进程ID生成一些ICMP数据包回复,为此我需要基于原始进程的PID计算一些校验和。因此,我真的需要提前知道这个PID以便生成正确的校验和。这意味着,我应该根据实时请求构造数据包。但是如果我知道PID,那么在之前就可以更容易地准备好数据包。我认为唯一的选择是在某些特定情况下对Linux源代码进行黑客/修改以给出特定的PID。 - Borja Tarraso
您可以在启动时提前启动一些进程,并在适当的程序后使用execve(在某些已知位置注册其pid)。我绝对会避免为此黑客攻击内核。 - Basile Starynkevitch
谢谢,最终我将根据输入数据包实时创建数据包响应。因此,我从原始数据包中提取进程ID并计算出校验和,然后为客户端应用程序提供一些延迟时间以便构建数据包并回复。现在这对我来说完美地运作了,所以最终不需要任何黑客技巧。 - Borja Tarraso

2
你可以重复调用 fork() 来创建新的子进程,直到你得到所需的 PID 的子进程。记得经常调用 wait(),否则你很快就会达到每个用户的进程限制。
这种方法假设操作系统按顺序分配新的 PID,例如在 Linux 3.3 上看起来是这样的。
ns_last_pid 方法相比,它的优点是不需要 root 权限。

1
在Linux系统上,每个进程都是通过fork()生成的,因此没有办法强制指定特定的PID。

除了内核自动启动的几个进程:/sbin/init 和可能的 /sbin/modprobe 等等...(更不用说 vfork 作为 fork 的一种变体) - Basile Starynkevitch

1

从Linux 5.5开始,您可以通过将PID数组传递给clone3系统调用,并分配给新进程,最多为每个嵌套的PID命名空间分配一个。从内部向外部。这需要在PID命名空间上具有CAP_SYS_ADMIN或(自Linux 5.9以来)CAP_CHECKPOINT_RESTORE

如果您不关心PID命名空间,请使用大小为一的数组。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接