找出startpar.c(sysvinit)在做什么

收藏

提问

好吧,这很长,请做好准备! ?

最近,我尝试在启动过程中启动用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上.现在,男子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的错误,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()函数中,

> 422行:获取主伪终端的句柄(在这种情况下为/ dev / ptmx)

p-> fd = getpt();
>第429行:获取相应从属伪终端的路径

否则if((m = ptsname(p-> fd))== 0 || grantpt(p-> fd)|| unlockpt(p-> fd))
>第438行:分叉子进程

如果((p-> pid = fork())==(pid_t)-1)
>第475行:使默认标准输出无效

TEMP_FAILURE_RETRY(close(1));
> 476行:获取从属伪终端的句柄.现在为1,即子代的stdout现在重定向到从属伪终端(并由主伪终端节点接收).

如果(open(m,O_RDWR)!= 1)
> 481行:还可以通过将stderr与从属伪终端fd复制来捕获它.

TEMP_FAILURE_RETRY(dup2(1,2));
> 561行:在完成一些簿记工作之后,启动感兴趣的可执行文件(作为子进程)

execlp(p->名称,p-> arg0,(字符*)0);
>然后,父进程可以通过读取缓冲的主伪终端并将其记录到实际的stdout(在本例中为/ 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设置一个明确的超时

在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.

或gtimo在startpar.c中

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.

回复