bash 没有 FIFO 的进程间通信

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10862970/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 02:25:51  来源:igfitidea点击:

Inter-process communication without FIFOs

bashfile-descriptorfifo

提问by davide

Inside a BASH script we can have multiple processes running in background which intercommunicate using named pipes, FIFOs registered on the filesystem. An example of this could be:

在 BASH 脚本中,我们可以在后台运行多个进程,这些进程使用命名管道、在文件系统上注册的 FIFO 进行相互通信。这方面的一个例子可能是:

#!/bin/bash
mkfifo FIFO

# BG process 1
while :; do echo x; done & >FIFO

# BG process 2
while :; do read; done & <FIFO

exit

I wonder if it's possible to do the same intercommunication between background processes of a script without using a FIFO on filesystem, maybe with some kind of file-descriptor redirection.

我想知道是否可以在不使用文件系统上的 FIFO 的情况下在脚本的后台进程之间进行相同的相互通信,也许使用某种文件描述符重定向。

回答by Christian Vogel

Here's an example that runs two subprocesses implemented as functions of the same shell-script... One subprocess generates numbers 1...5 (sleeps in between prints), the second one reads from a fixed filedescriptor (5, to which STDOUT of the first FD is redirected to), multiplies by 2 and prints again. The main process redirects STDOUT of that second process to another fixed filedescriptor (6) and later on reads from that one in the loop.

这是一个运行两个子进程的示例,这些子进程实现为同一个 shell 脚本的函数......一个子进程生成数字 1...5(在打印之间休眠),第二个从固定的文件描述符(5,其中的 STDOUT 中读取)第一个 FD 被重定向到),乘以 2 并再次打印。主进程将第二个进程的 STDOUT 重定向到另一个固定的文件描述符 (6),然后在循环中从那个文件描述符中读取。

It works basically the same as you'd do in C-code with fd pairs created by the pipe(2) system call. To understand what's happening run the script under strace -f!

它的工作原理与您在 C 代码中使用由 pipe(2) 系统调用创建的 fd 对所做的工作基本相同。要了解发生了什么,请在 strace -f 下运行脚本!

Bash Version is 4.2.24(1) running on Ubuntu/x86.

Bash 版本是 4.2.24(1),在 Ubuntu/x86 上运行。

[ubuntu /home/chris]
$ bash --version
GNU bash, version 4.2.24(1)-release (i686-pc-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Output of script:

脚本输出:

[ubuntu /home/chris]
$ ./read_from_fd.sh
Got number 2.
Got number 4.
Got number 6.
Got number 8.
Got number 10.

Source code:

源代码:

#!/bin/bash

# Generate data, output to STDOUT.
generate_five_numbers() {
        for n in `seq 5` ; do
                echo $n
                sleep 2
        done
}

# Read data from FD#5, multiply by two, output to STDOUT.
multiply_number_from_fd5_by_two() {
        while read n <&5 ; do
                echo "$(( $n * 2 ))"
        done
}

# choose your FD number wisely ;-)

# run generator with its output dup'ed to FD #5
exec 5< <( generate_five_numbers )

# run multiplyier (reading from fd 5) with output dup'ed to FD #6
exec 6< <( multiply_number_from_fd5_by_two )

# read numbers from fd 6
while read n <&6 ; do
        echo "Got number $n."
done

Process tree while running:

运行时的进程树:

──read_from_fd.sh(8118)─┬─read_from_fd.sh(8119)───sleep(8123)
                        └─read_from_fd.sh(8120)

回答by Paused until further notice.

Bash 4 has coprocesses.

Bash 4 有进程。

You can also use anonymous named pipes, aka process substitutionin Bash 2, 3 or 4.

您还可以使用匿名命名管道,即Bash 2、3 或 4 中的进程替换

回答by Micha? Kosmulski

You could use nc(aka netcat) which allows connecting a script's standard streams to a network socket. Of course it also works on localhost, so you can use it for IPC between scripts. The bonus is the possibility to have scripts running on different hosts, which is not possible with FIFOs (OK, maybe on NFS it is, but that would be rather cumbersome to set up unless you already have the NFS in place).

您可以使用nc(又名netcat),它允许将脚本的标准流连接到网络套接字。当然它也适用于本地主机,因此您可以将它用于脚本之间的 IPC。好处是可以在不同的主机上运行脚本,这在 FIFO 中是不可能的(好吧,也许在 NFS 上是这样,但除非您已经有了 NFS,否则设置起来会相当麻烦)。

回答by Camilo Martin

I just want to point out that ugly hacks didn't wish to be born that way.

我只想指出丑陋的黑客不希望以这种方式出生。

Part which receives data:

接收数据的部分:

node -e "require('net').createServer(function(s){s.pipe(process.stdout)}).listen(1337)"

Part which sends data:

发送数据的部分:

echo "write clean code they said" > /dev/tcp/localhost/1337
echo "it will pay off they said" > /dev/tcp/localhost/1337


Works even in MSysGit's Bash for Windows, to my surprise.

令我惊讶的是,它甚至可以在 MSysGit 的 Bash for Windows 中工作。

回答by Fabien Bouleau

Have you considered the use of signals? If the only thing you need is to trigger an event (without passing arguments), using kill and trap works perfectly (be careful of the semantics though, use SIGUSR1 for instance).

有没有考虑过信号的使用?如果您唯一需要的是触发事件(不传递参数),则使用 kill 和 trap 非常有效(但请注意语义,例如使用 SIGUSR1)。

You might need to rework the logic though, as in the example below:

不过,您可能需要重新设计逻辑,如下例所示:

subprocess_finished()
{
    np=$( jobs -p | wc -l )
}

start_processing()
{
    myfile=""
    # DO SOMETHING HERE!!
    kill -SIGUSR1 
}

CPUS=$( lscpu | grep "^CPU(s):" | rev | cut -f 1 -d ' ' | rev )
POLLPERIOD=5  # 5s between each poll
np=0
trap subprocess_finished SIGUSR1

for myfile in *
do 
        start_processing "$myfile" $$ &
        np=$( jobs -p | wc -l )
        echo "$( date +'%Y-%m-%d %H:%M:%S' ) [$!] Starting #$np on $CPUS: $myfile"

        if [ $np -eq $CPUS ] 
        then
            # Wait for one CPU to be free
            trap subprocess_finished SIGUSR1
            while [ $np -eq $CPUS ]
            do
                sleep $POLLPERIOD
            done
        fi
    done
done

# wait for the last subprocesses
while [ ! -z "$( jobs -rp )" ]
do
    sleep $POLLPERIOD
done