好吧,这很长,请做好准备! :)
最近,我尝试在启动过程中启动用bash编写的看门狗脚本。因此,我在 rc.local 中添加了以下内容:
su someuser -c "/home/someuser/watchdog.sh &"
watchdog.sh看起来像这样:
#!/bin/bash
until /home/someuser/eventMonitoring.py
do
sleep 1
done
一切都很好,一切都很好,脚本也已开始。但是,新流程将出现在流程列表中,并一直保留在该列表中:
UID PID PPID C SZ RSS PSR STIME TTY TIME CMD
root 3048 1 0 1024 620 1 20:04 ? 00:00:00 startpar -f -- rc.local
现在,我的脚本(watchdog.sh)已启动并成功分离,因为它的PPID也为1。我当时的任务是找出该过程是什么。 Startpar是 sysvinit 引导系统(http://savannah.nongnu.org/projects/sysvinit)的一部分。我目前在使用该系统的Debian Wheezy 7.4.0上。现在
man startpar
说:startpar is used to run multiple run-level scripts in parallel.
通过反复试验的方法,我基本上想出了如何在引导过程中正确启动脚本,而不使 startpar 挂起。进程的所有文件描述符都需要重定向到文件或/dev/null 或一起关闭。当您考虑时应该做的是理性的事情。我终于做到了:
su someuser -c "some_script.sh >/dev/null 2>&1 &"
那解决了问题。但是仍然让我怀疑为什么会这样。为什么 startpar 的行为类似于它。它是错误还是功能。
因此,我深入研究了代码(http://svn.savannah.nongnu.org/viewvc/startpar/trunk/startpar.c?root=sysvinit&view=markup),然后从头开始:
首先,我找到 startpar -f的位置-进行rc.local 调用:
第741行:
execlp(myname, myname, "-f", "--", p->name, NULL);
好的,这样实际上将启动一个新的 startpar 进程,它将替换当前正在运行的实例。基本上,这是对自身的递归调用。让我们看看 -f 参数的作用:
第866行:
case 'f':
forw = 1;
break;
好的,让我们看看将 forw 变量设置为 1 是什么...
第900行:
if (forw)
do_forward();
最后,让我们看看该函数的功能:
第615行:
void do_forward(void)
{
char buf[4096], *b;
ssize_t r, rr;
setsid();
while ((r = read(0, buf, sizeof(buf))))
{
if (r < 0)
{
if (errno == EINTR)
continue;
#if defined(DEBUG) && (DEBUG > 0)
perror("\n\rstartpar: forward read");
#endif
break;
}
b = buf;
while (r > 0)
{
rr = write(1, b, r);
if (rr < 0)
{
if (errno == EINTR)
continue;
perror("\n\rstartpar: forward write");
rr = r;
}
r -= rr;
b += rr;
}
}
_exit(0);
}
据我了解。这会将来自文件描述符0的所有内容重定向到文件描述符1。现在,让我们看一下与这些文件描述符真正相关的内容:
root@server:~# ls -al /proc/3048/fd
total 0
dr-x------ 2 root root 0 Apr 2 21:13 .
dr-xr-xr-x 8 root root 0 Apr 2 21:13 ..
lrwx------ 1 root root 64 Apr 2 21:13 0 -> /dev/ptmx
lrwx------ 1 root root 64 Apr 2 21:13 1 -> /dev/console
lrwx------ 1 root root 64 Apr 2 21:13 2 -> /dev/console
嗯,很有趣...所以ptmx是根据人说的:
The file /dev/ptmx is a character file with major number 5
and minor number 2, usually of mode 0666 and owner.group of root.root.
It is used to create a pseudoterminal master and slave pair.
和控制台:
The current console is also addressed by
/dev/console or /dev/tty0, the character device with major number 4
and minor number 0.
那时我来到了stackoverflow。现在,有人可以告诉我这是怎么回事吗?我是否正确,startpar处于不断将 ptmx 所涉及的任何内容重定向到控制台的阶段?为什么这样做呢?为什么是 ptmx ?这是一个错误吗?
最佳答案
TL; DR
绝对不是 startpar
的错误,它正在执行promises to in the first place的功能。
代码详细信息
在 run()
的startpar.c
函数中,
/dev/ptmx
)p->fd = getpt();
else if ((m = ptsname(p->fd)) == 0 || grantpt(p->fd) || unlockpt(p->fd))
if ((p->pid = fork()) == (pid_t)-1)
stdout
TEMP_FAILURE_RETRY(close(1));
1
,即子代的stdout
现在重定向到从属伪终端(并由主伪终端节点接收)。if (open(m, O_RDWR) != 1)
stderr
。TEMP_FAILURE_RETRY(dup2(1, 2));
execlp(p->name, p->arg0, (char *)0);
/dev/console
)来捕获该新启动进程的所有输出/错误日志。如何防止系统上悬挂
startpar -f ...
进程?方法1:定义要以交互方式启动的可执行文件。
显式标记可执行的交互式可执行文件会告诉
startpar
跳过psedoterminal主/从欺骗来缓冲终端I/O,因为启动的交互式可执行文件的任何输出都需要立即显示在屏幕上而不是进行缓冲。这样可以在几个地方修改执行流程。主要在第1171行,其中
startpar
不会为交互式可执行文件调用run()
函数。已经测试并描述了here。
方法2:丢弃要启动的可执行文件的
stdout
和stderr
。使用结构
">/dev/null 2>&1 &"
丢弃要启动的可执行文件的stdout
/stderr
。如果它们都显式设置为NULL,即startpar不会像通常那样无限期地缓冲它们。方法3:为
startpar
设置一个明确的超时在
timo
中配置 startpar.c
或
gtimo
中的 startpar.c