检查 named pipe/FIFO 是否打开写入

Check whether named pipe/FIFO is open for writing

我已经为其他一些进程创建了一个命名管道来写入并想检查其他进程是否正确启动,但不知道它的 PID。上下文是 running a command in screen,确保命令正确启动。我希望这会起作用:

mkfifo /tmp/foo
echo hello > /tmp/foo &
lsof /tmp/foo

遗憾的是,lsof 没有报告 echoinotifywait 可能是另一种选择,但并不总是安装,我真的只想轮询一次,而不是阻塞直到发生某些事件。

有什么方法可以检查命名管道是否打开写入?连一般都开?


更新:

两端连接后 lsof 似乎可以工作。这实际上解决了我的问题,但为了这个问题,我很想知道是否有可能在没有 reader.

的情况下检测到命名管道的初始重定向
> mkfifo /tmp/foo
> yes > /tmp/foo &
> lsof /tmp/foo
> cat /tmp/foo > /dev/null &
> lsof /tmp/foo
COMMAND   PID     USER   FD   TYPE DEVICE SIZE/OFF     NODE     NAME
yes     16915     user    1w  FIFO   8,18      0t0 16660270 /tmp/foo
cat     16950     user    3r  FIFO   8,18      0t0 16660270 /tmp/foo

更新 2:玩过 inotify-tools 后,似乎没有办法获得已打开命名管道进行写入和正在阻塞。这可能就是为什么 lsof 在有一个 reader 和一个 writer 之前不显示管道的原因。

更新:在研究了命名管道之后,我不相信有任何方法可以单独使用命名管道。 推理:

  • 没有办法限制写入命名管道的数量(不求助于锁定)
  • 如果没有reader
  • ,所有作者都会阻止
  • 如果存在 reader(大概只要内核缓冲区未满)就没有编写器阻塞)

您可以尝试在短时间内不向管道写入任何内容。如果超时,则写入阻塞,表明有人已经打开管道进行写入。

注意:正如评论中指出的那样,如果 reader 存在并且可能足够快,我们的测试写入将不会阻塞并且测试基本上失败。注释掉下面的 cat 行以对此进行测试。

#!/bin/bash

is_named_pipe_already_opened_for_writing() {
    local named_pipe=""
    # Make sure it's a named pipe
    if ! [ -p "$named_pipe" ]; then
        return 1
    fi
    # Try to write zero bytes in the background
    echo -n > "$named_pipe" &
    pid=$!
    # Wait a short amount of time
    sleep 0.1
    # Kill the background process. If kill succeeds, then
    # the write was blocked indicating that someone
    # else is already writing to the named pipe.
    kill $pid 2>/dev/null
}

PIPE=/tmp/foo

# Ignore any bash messages from killing below
trap : TERM

mkfifo $PIPE
# a writer
yes > $PIPE &
# a reader
cat $PIPE >/dev/null &

if is_named_pipe_already_opened_for_writing "$PIPE"; then
    echo "$PIPE is already being written to by another process"
else
    echo "$PIPE is NOT being written to by another process"
fi

jobs -pr | kill 2>/dev/null
rm -f $PIPE

您需要两根管道,每个方向一根: 一个用于等待准备好新数据信号,另一个仅用于数据: 在我的例子中,逐行处理文件:

mkfifo r w;

cat file1 | while read l; do echo "$l" >w; read <r; done &
cat file2 | while read ln; do if read l <w; then echo "$ln"; echo "$l"; fi; echo 1>r;  done