壳牌:为什么在使用管道和三通时存在“重复过程”?

壳牌:为什么在使用管道和三通时存在“重复过程”?

我对 bash 进程不是很熟悉,并且很惊讶地看到在使用管道和tee.你能帮我理解这一点吗?

在下面,脚本的路径被替换为$FOLDER.

  • $FOLDER/DBB/myparent.ksh:

    echo "BEGIN $$ this is the parent process"
    $FOLDER/DBB/myChild.ksh
    echo "END $$ this is the parent process"
    
  • $FOLDER/DBB/mychild.ksh:

    function toto {
      echo " this is $$ child process "
      sleep 10
    }
    {
      echo " $$ go1 "
      toto 
      ptree $$
      echo " $$ go2 "
    } | tee myLog$$.log
    
  • 当我启动时myParent.ksh,我有这个进程树:

    28417 /usr/lib/ssh/sshd -R
      28531 -ksh
        41387 /bin/bash myParent.ksh
          41390 /bin/bash $FOLDER/DBB/myChild.ksh
            41391 /bin/bash $FOLDER/DBB/myChild.ksh
              41393 sleep 10
            41392 tee myLog41390.log
    
  • 控制台输出:

    BEGIN 52665 this is the parent process
    in myChild.ksh
     52680 go1
     this is 52680 child process
    20192 zsched
      21104 /usr/lib/ssh/sshd
        27882 /usr/lib/ssh/sshd -R
          28417 /usr/lib/ssh/sshd -R
            28531 -ksh
              52665 /bin/bash myParent.ksh
                52680 /bin/bash $FOLDER/DBB/myChild.ksh
                  52688 /bin/bash $FOLDER/DBB/myChild.ksh
                    61896 ptree 52680
                  52692 tee myLog52680.log
     52680 go2
    END 52665 this is the parent process
    

为什么有2个$FOLDER/DBB/myChild.ksh

答案1

shell 脚本的后缀是.ksh,但进程树表明它们实际上是由bashshell 执行的。

在我的 Debian 11 man ksh(实际上是mkshMirBSD Korn shell)上说:

管道的所有命令都在单独的子 shell 中执行;这是 POSIX 允许的,但与 AT&T UNIX ksh 的两个变体不同,其中除了最后一个命令之外的所有命令都在子 shell 中执行;请参阅 read 内置函数的描述以了解含义和解决方法。

man bash说:

管道中的每个命令都作为单独的进程(即在子shell 中)执行。 [...] 如果lastpipe使用内置函数启用该选项shopt(请参阅下面的描述shopt),则管道的最后一个元素可能由 shell 进程运行。

因此,最初进程#41390 是开始执行的进程myChild.ksh。当它看到管道时:

{
  echo " $$ go1 "
  toto 
  ptree $$
  echo " $$ go2 "
} | tee myLog$$.log

它将扩展$$管道中的 ,然后为{...中的复合命令派生一个子 shell 进程 #41391 }

进程 #41392 可以是一个子 shell,它只需要运行一个非内部命令,并将exec()其作为优化,也可以是由父进程 #41390tee直接fork()+ 'd 的命令。exec()在这两种情况下,它都相当于同一件事:#41392 最终会exec()执行某些操作,并exec()导致可见的命令行ptree发生变化。

另一方面,子 shell #41391 有多个命令要运行,因此进程 #41391 将保持其作为子 shell 的身份。由于它需要执行的最后一个命令是echoshell 内部命令,因此它将结束其作为子 shell 进程的生命。并且由于其父级 #41390$$在准备执行管道时已经扩展了 s,因此子 shell #41391 的 PID 将永远不会在输出中可见。

相关内容