gpt4 book ai didi

c - 弄清楚 startpar.c (sysvinit) 在做什么

转载 作者:太空狗 更新时间:2023-10-29 11:04:34 30 4
gpt4 key购买 nike

好吧,这是一个很长的,打起精神来! :)

最近我尝试在启动期间启动一个用 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 的任何内容重定向到 console 的阶段?为什么要这样做?为什么选择 ptmx?这是错误吗?

最佳答案

长话短说

这绝对不是 startpar 的错误,它正在做的正是它 promises to in the first place .

The output of each script is buffered and written when the script exits, so output lines of different scripts won't mix. You can modify this behaviour by setting a timeout.


代码详情

startpar.c 中的 run() 函数中,

  1. 第 422 行:获取主伪终端的句柄(在本例中为 /dev/ptmx)

    p->fd = getpt();

  2. 第429行:获取对应slave伪终端的路径

    else if ((m = ptsname(p->fd)) == 0 || grantpt(p->fd) || unlockpt(p->fd))

  3. 第 438 行:fork 一个子进程

    if ((p->pid = fork()) == (pid_t)-1)

  4. 第 475 行:使默认 stdout

    无效

    TEMP_FAILURE_RETRY(关闭(1));

  5. 第 476 行:获取从属伪终端的句柄。现在,这是 1,即子节点的 stdout 现在重定向到从属伪终端(并由主伪终端节点接收)。

    if (open(m, O_RDWR) != 1)

  6. 第 481 行:还通过使用 salve 伪终端 fd 复制它来捕获 stderr

    TEMP_FAILURE_RETRY(dup2(1, 2));

  7. 第 561 行:在一些簿记之后,启动感兴趣的可执行文件(作为子进程)

    execlp(p->name, p->arg0, (char *)0);

  8. 父进程稍后可以通过读取缓冲的主伪终端捕获这个新启动进程的所有输出/错误日志,并将其记录到实际的标准输出(即 /dev/console 在这种情况下)。


如何防止系统上出现悬空的 startpar -f ... 进程?

方法一:将要启动的可执行文件定义为交互式。

显式标记可执行交互告诉 startpar 跳过 psedoterminal 主/从欺骗以缓冲终端 I/O,因为启动的交互式可执行文件的任何输出需要立即显示在屏幕上而不是缓冲.

这在几个地方修改了执行流程。主要在 1171 行,其中 startpar 不会为交互式可执行文件调用 run() 函数。

这已经过测试和描述here .

方法2:丢弃要启动的可执行文件的stdoutstderr

使用构造 ">/dev/null 2>&1 &" 丢弃要启动的可执行文件的 stdout/stderr。如果它们都显式设置为 NULL,即 startpar 不会像通常那样无限期地缓冲它们。

方法三:为startpar设置显式超时

要么在startpar.c

中配置 timo

The timeout set with the -t option is used as buffer timeout. If the output buffer of a script is not empty and the last output was timeout seconds ago, startpar will flush the buffer.

startpar.c

中的 gtimo

The -T option timeout works more globally. If no output is printed for more than global_timeout seconds, startpar will flush the buffer of the script with the oldest output. Afterwards it will only print output of this script until it is finished.

关于c - 弄清楚 startpar.c (sysvinit) 在做什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22840360/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com