这个问题可能看起来有点傻,但由于在基于 Unix 的系统中,替换可执行文件的映像只需一步即可完成,即替换当前正在运行的进程,在函数调用execve
(及其派生函数)中,问题是:
为什么在执行替换过程sudo
fork()
之前会默认执行?execve
通过 fork 之前,必须初始化额外的内核元素,尽管在某些 Unix 中 fork 已经相当优化,但仍然有一些不可避免的元素必须初始化。如果fork()
默认情况下不发生,PID 空间的增加会更慢。
如果你很好奇,可以通过发出以下命令来检查此默认行为,例如
sudo sleep 30
当前代码[1] 相当复杂,因为此后又添加了许多功能;但在 Apple 托管的版本中 [2] 它的作用非常清楚。
#ifndef PROFILING
if ((sudo_mode & MODE_BACKGROUND) && fork() > 0)
exit(0);
else
EXEC(safe_cmnd, NewArgv); /* run the command */
#else
/* Complicated code when profiling is enabled, but we don't care */
我目前正在运行 sudo 版本 1.8.11p2,无论是否使用开关,它都会产生睡眠-b
,因此当前代码似乎变得更加复杂。
我正在寻找一个答案,其中也涵盖为什么这是默认行为吗?它能给我们带来哪些好处。
答案1
使用简单的 sysctl即可将 Linux 上的 PID 空间提升到 2 22;这真的不是问题……
如今,大多数 Linux 发行版都使用 PAM,并且sudo
通常也使用 PAM 支持进行编译。除其他事项外,它pam_open_session()
在运行程序之前调用,因此它也必须pam_close_session()
从同一进程中调用,因为您的程序不知道它需要这样做(并且很可能不会知道)允许要做到这一点)。
答案2
分叉的原因在Process model
man 页面的部分中有记录。相关内容如下:
这个额外的进程使得暂停和恢复命令等操作成为可能。如果没有它,命令将处于 POSIX 所称的“孤立进程组”中,并且不会收到任何作业控制信号。特殊情况下,如果策略插件未定义 close 函数且不需要 pty,则 sudo 将直接执行命令,而不是先调用 fork(2)。sudoers 策略插件仅在启用 I/O 日志记录、需要 pty 或启用 pam_session 或 pam_setcred 选项时才会定义 close 函数。请注意,在使用 PAM 的系统上,pam_session 和 pam_setcred 默认启用。