13

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

#!/bin/bash
mkfifo FIFO

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

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

exit

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

4

5 回答 5

17

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

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

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

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

脚本输出:

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

源代码:

#!/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

运行时的进程树:

──read_from_fd.sh(8118)─┬─read_from_fd.sh(8119)───sleep(8123)
                        └─read_from_fd.sh(8120)
于 2012-12-07T07:21:24.730 回答
5

Bash 4 有协同进程。

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

于 2012-06-02T14:33:45.260 回答
3

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

于 2012-06-04T13:21:17.170 回答
2

我只想指出,丑陋的黑客不希望这样出生。

接收数据的部分:

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

发送数据的部分:

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

令我惊讶的是,它甚至可以在 MSysGit 的 Windows 版 Bash 中使用。

于 2016-02-03T07:20:58.040 回答
0

你考虑过信号的使用吗?如果您唯一需要的是触发事件(不传递参数),那么使用 kill 和 trap 可以完美地工作(但要注意语义,例如使用 SIGUSR1)。

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

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

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

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
于 2014-03-10T11:16:59.320 回答