在回答这个问题时,我无法完全解释信号如何通过管道传播。
考虑以下示例。
timeout作为管道的第一个元素这会导致gpg救助已捕获的SIGTERM交付给cat,由timeout,留下损坏的文件。
$ timeout 1 cat /dev/urandom | gpg -er attie@attie.co.uk > ./myfile.gpg
gpg: Terminated caught ... exiting
Terminated
$ gpg -d < ./myfile.gpg > /dev/null
You need a passphrase to unlock the secret key for
user: "Attie Grande <attie@attie.co.uk>"
4096-bit RSA key, ID C9AEA6AE, created 2016-12-13 (main key ID 7826F053)
gpg: encrypted with 4096-bit RSA key, ID C9AEA6AE, created 2016-12-13
"Attie Grande <attie@attie.co.uk>"
gpg: block_filter 0x145e790: read error (size=14775,a->size=14775)
gpg: block_filter 0x145f110: read error (size=10710,a->size=10710)
gpg: WARNING: encrypted message has been manipulated!
gpg: block_filter: pending bytes!
gpg: block_filter: pending bytes!
Run Code Online (Sandbox Code Playgroud)
timeout在管道中间使用这按预期工作 -gpg干净地退出。
$ cat /dev/urandom | timeout 1 cat | gpg -er attie@attie.co.uk > ./myfile.gpg
$ gpg -qd < ./myfile.gpg > /dev/null
You need a passphrase to unlock the secret key for
user: "Attie Grande <attie@attie.co.uk>"
4096-bit RSA key, ID C9AEA6AE, created 2016-12-13 (main key ID 7826F053)
Run Code Online (Sandbox Code Playgroud)
SIGUSR1代替SIGTERM同样,这按预期工作 -gpg干净地退出。我期望因为cat退出SIGUSR1,而gpg忽略它。
$ timeout -sUSR1 1 cat /dev/urandom | gpg -er attie@attie.co.uk > ./myfile.gpg
$ gpg -qd < ./myfile.gpg > /dev/null
You need a passphrase to unlock the secret key for
user: "Attie Grande <attie@attie.co.uk>"
4096-bit RSA key, ID C9AEA6AE, created 2016-12-13 (main key ID 7826F053)
Run Code Online (Sandbox Code Playgroud)
再次,这有效 - 尽管我没想到它会如此。
$ gpg -er attie@attie.co.uk > ./myfile.gpg < <( timeout 1 cat /dev/urandom )
$ gpg -qd < ./myfile.gpg > /dev/null
You need a passphrase to unlock the secret key for
user: "Attie Grande <attie@attie.co.uk>"
4096-bit RSA key, ID C9AEA6AE, created 2016-12-13 (main key ID 7826F053)
Run Code Online (Sandbox Code Playgroud)
我只能假设管道中第一个元素的信号传播到管道中的其余元素(甚至将它们与timeout cat | cat | gpg失败分开)。
我查看了文档,并尝试了使用set -e,set -o pipefail但它们并没有像我预期的那样行事。
我只能假设管道中第一个元素的信号传播到管道中的其余元素。
据我所知,没有这种传播。我主要回答你的第一个问题:
究竟发生了什么?
(这可能有点简化。)
bash将每个进程放在一个进程组中,PGID(进程组 ID)等于PID第一个命令的(进程 ID)。timeout将自己更改为自己PGID的PID。如果timeout是管道中的第一个命令,则不会改变任何内容。timeout不仅将信号发送到底层命令,而且还发送到其整个进程组。如果timeout是管道中的第一个命令,则其进程组仍将包含gpg,因此gpg将获得信号。下面对该现象进行研究和阐述。
bash行为当运行一个管,交互式bash每一个处理组与过程的地方PGID等于所述PID第一命令的。您可以进行自己的测试(请参阅是否可以从中获取进程组 ID/proc?)。我还没有研究过更复杂的可能性(例如,如果第一个“命令”是一个子外壳呢?),在你的情况下,它们并不重要。重要的是gpg在这些命令中
timeout 1 cat /dev/urandom | gpg -er attie@attie.co.uk > ./myfile.gpg
cat /dev/urandom | timeout 1 cat | gpg -er attie@attie.co.uk > ./myfile.gpg
timeout -sUSR1 1 cat /dev/urandom | gpg -er attie@attie.co.uk > ./myfile.gpg
gpg -er attie@attie.co.uk > ./myfile.gpg < <( timeout 1 cat /dev/urandom )
Run Code Online (Sandbox Code Playgroud)
获得PGID等于PID的
timeoutcattimeoutgpg (即本身)分别。
timeout改变自己PGID(或不改变)运行strace timeout 1 cat,您将看到以下内容:
setpgid(0, 0)
Run Code Online (Sandbox Code Playgroud)
摘录自man 2 setpgid:
Run Code Online (Sandbox Code Playgroud)int setpgid(pid_t pid, pid_t pgid);
setpgid()将PGID指定的进程的设置pid为pgid。如果pid为零,则使用调用进程的进程 ID。如果pgid为零,PGID则由 指定的pid进程的进程 ID 与其进程 ID 相同。
这意味着timeout设置它PGID等于它的PID。有两种可能:
timeout是第一个命令,其PGID是之前和之后相同setpgid,所以gpg仍然具有相同的PGID作为timeout;timeout不是第一个命令,它会PGID被更改,即使gpg最初相同PGID,现在timeout两个PGIDs 也不同。timeout发出比你预期更多的信号同样strace timeout 1 cat揭示了以下几行:
kill(19401, SIGTERM)
…
kill(0, SIGTERM)
Run Code Online (Sandbox Code Playgroud)
在这个例子中19401是PID的cat。如果你使用,-s USR1那么将会有SIGUSR1而不是SIGTERM等。这第二个kill负责你认为是通过管道的信号传播。见man 2 kill(摘录):
Run Code Online (Sandbox Code Playgroud)int kill(pid_t pid, int sig);如果
pid等于0,则sig发送到调用进程的进程组中的每个进程。
调用过程是timeout。它向整个进程组发送信号。我承认我不知道这背后的目的是什么,但它仍然如此。
因此,如果timeout是管道中的第一个命令,则所选信号将被发送到它的每个部分(好吧,几乎;考虑timeout同一管道中的另一个)。这包括gpg. 然后取决于gpg它对信号的反应。
我们对此有任何控制吗?有没有比从管道前端移动信号生成过程更好的方法?
我的快速搜索没有找到设置/更改的通用工具PGID。我认为您可以编写自己的程序来调用setpgid(2)左右;但是现在,当我们知道发生了什么时,timeout从管道前端移动似乎是一种非常明智的方法。
另请注意,这是因为timeout行为方式。其他信号生成过程可能不需要这种变通方法。
| 归档时间: |
|
| 查看次数: |
1183 次 |
| 最近记录: |