#!/bin/bash trap ':' HUP trap 'kill $(jobs -p)' EXIT /usr/local/bin/inotifywait -q -m /tmp --format %f | while IFS= read -r filename; do echo "$filename" | sed 's/a/b/' > test.log done
这是实际脚本的简化版本.我上面的脚本也有一个Sys-V类型的init脚本,因为我希望保持LSB兼容,我的init脚本有force-reload(如果服务支持,则重新加载配置.否则,服务重新启动) .)将HUP信号发送到脚本的选项.现在在执行执行killproc -HUP test.sh的force-reload之前,pstree的输出如下:
# pstree -Ap 4424 test.sh(4424)-+-inotifywait(4425) `-test.sh(4426) #
执行strace killproc -HUP test.sh后,子shell终止:
# pstree -Ap 4424 test.sh(4424)---inotifywait(4425) #
根据strace,killproc将SIGHUP发送到进程4424和4426,但只有后者被终止.
在我的例子中,这个带有PID 4426的子shell有什么意义,即为什么它首先被创建?另外,有没有办法忽略HUP信号?
问题的第一部分是通过shell(在本例中为Bash)在管道中运行命令的机制来解释的.
管道是FIFO(先进先出)单向进程间通信(IPC)通道:它允许在一端(只写端)写入字节,从另一端读取字段(只读端) )无需读取或写入物理文件系统.
管道允许两个不同的命令通过匿名或未命名(即,在文件系统中没有条目)管道彼此通信.
当shell执行简单命令时,该命令在shell的子进程中运行.如果没有使用作业控制,则当子进程终止时,shell将重新获得对终端的控制.
当在管道中运行两个命令时,管道中的两个命令都作为两个单独的子进程执行,这些子进程同时运行.
在Unix系统中,使用pipe(2)系统调用创建管道,该管道创建一个新管道并返回一对文件描述符,其中一个引用读取端,另一个引用管道的写入端.
在GNU / Linux系统上使用Bash时,clone(2)系统调用用于创建子进程.这允许子进程与其父进程共享文件描述符表,以便两个子子进程都继承匿名管道的文件描述符,以便可以读取它并且另一个可以写入它.
在您的情况下,inotifywait命令获得4425的PID,并通过将其stdout连接到写端的文件描述符来写入管道的只写端.
同时,管道命令的右侧获取PID,4426并且其stdin文件描述符设置为管道的只读端的描述符.由于管道右侧的子shell不是外部命令,因此表示子进程的名称与其父进程test.sh的名称相同.
有关更多信息,请参阅man 7 pipe和以下链接:
> Anonymous pipe,Wikipedia article
> Unix Pipeline,Wikipedia article
信号处理
我花了很长时间(实际上是几个小时的研究)来弄清楚为什么SIGHUP信号的陷阱没有被忽略.
我的所有研究表明,由clone(2)系统调用创建的子进程也应该能够共享父进程的信号处理程序表.
Bash手册页也说明了这一点
@H_502_50@Command substitution,commands grouped with parentheses,and asynchronous commands are invoked in a subshell environment that is a duplicate of the shell environment,except that traps caught by the shell are reset to the values that the shell inherited from its parent at invocation.
它后来说明了这一点
@H_502_50@Signals ignored upon entry to the shell cannot be trapped or reset. Trapped signals that are not being ignored are reset to their original values in a subshell or subshell environment when one is created.
这表明子shell不继承未被忽略的信号处理程序.正如我所理解的那样,你的陷阱’:’HUP行意味着(有效)忽略了SIGHUP信号(因为:builtin除了返回成功之外什么都不做) – 而且应该被管道的子shell忽略.
但是,我最终遇到了Bash手册页中内置陷阱的描述,该手册定义了忽略Bash的含义:
@H_502_50@If arg is the null string the signal specified by each sigspec is ignored by the shell and by the commands it invokes.
简单地将陷阱命令更改为陷阱”HUP可确保忽略SIGHUP信号,脚本本身以及任何子壳.