当前位置: 首页 > 面试题库 >

在bash中使用命名管道-数据丢失问题

姬衡
2023-03-14
问题内容

在网上进行了一些搜索,找到了使用命名管道的简单“教程”。但是,当我对后台作业执行任何操作时,我似乎会丢失很多数据。

[[编辑:找到了一个简单得多的解决方案,请参阅回复。因此,我提出的问题现在是学术性的,以防万一可能需要工作服务器]]

在Linux 2.6.32-25-generic#45-Ubuntu SMP上使用Ubuntu 10.04(星期六)10月16日19:52:42 UTC
2010 x86_64 GNU / Linux

GNU bash版本4.1.5(1)-发行版(x86_64-pc-linux-gnu)。

我的bash函数是:

function jqs
{
  pipe=/tmp/__job_control_manager__
  trap "rm -f $pipe; exit"  EXIT SIGKILL

  if [[ ! -p "$pipe" ]]; then
      mkfifo "$pipe"
  fi

  while true
  do
    if read txt <"$pipe"
    then
      echo "$(date +'%Y'): new text is [[$txt]]"

      if [[ "$txt" == 'quit' ]]
      then
    break
      fi
    fi
  done
}

我在后台运行:

> jqs&
[1] 5336

现在我喂它:

for i in 1 2 3 4 5 6 7 8
do
  (echo aaa$i > /tmp/__job_control_manager__ && echo success$i &)
done

输出不一致。我经常没有得到所有成功的回声。我得到的新文本回声与成功回声最多,有时更少。

如果我从“提要”中删除“&”,它似乎可以工作,但是在读取输出之前,我一直处于阻塞状态。因此,我想让子流程受阻,而不是主流程受阻。

目的是编写一个简单的作业控制脚本,这样我最多可以并行运行10个作业,并将其余的作业排队等待以后的处理,但可靠地知道它们确实可以运行。

完整的职位经理如下:

function jq_manage
{
  export __gn__="$1"

  pipe=/tmp/__job_control_manager_"$__gn__"__
  trap "rm -f $pipe"    EXIT
  trap "break"      SIGKILL

  if [[ ! -p "$pipe" ]]; then
      mkfifo "$pipe"
  fi

  while true
  do
    date
    jobs
    if (($(jobs | egrep "Running.*echo '%#_Group_#%_$__gn__'" | wc -l) < $__jN__))
    then
      echo "Waiting for new job"
      if read new_job <"$pipe"
      then
    echo "new job is [[$new_job]]"

    if [[ "$new_job" == 'quit' ]]
    then
      break
    fi

    echo "In group $__gn__, starting job $new_job"
    eval "(echo '%#_Group_#%_$__gn__' > /dev/null; $new_job) &"
      fi
    else
      sleep 3
    fi
  done
}

function jq
{
  # __gn__ = first parameter to this function, the job group name (the pool within which to allocate __jN__ jobs)
  # __jN__ = second parameter to this function, the maximum of job numbers to run concurrently

  export __gn__="$1"
  shift
  export __jN__="$1"
  shift

  export __jq__=$(jobs | egrep "Running.*echo '%#_GroupQueue_#%_$__gn__'" | wc -l)
  if (($__jq__ '<' 1))
  then
    eval "(echo '%#_GroupQueue_#%_$__gn__' > /dev/null; jq_manage $__gn__) &"
  fi

  pipe=/tmp/__job_control_manager_"$__gn__"__

  echo $@ >$pipe
}

呼唤

jq <name> <max processes> <command>
jq abc 2 sleep 20

将开始一个过程。那部分工作正常。开始第二个,很好。似乎一个接一个地工作正常。但是,像上面更简单的示例一样,以循环开头10似乎会使系统丢失。

我将为解决这种明显的IPC数据丢失问题提供任何建议。

问候,阿兰。


问题答案:

您的问题if如下:

while true
do
    if read txt <"$pipe"
    ....
done

发生的情况是您的作业队列服务器每次在循环中都在打开和关闭管道。这意味着某些客户端在尝试写入管道时会遇到“管道中断”错误-
也就是说,管道的读取器在编写器打开后消失了。

要解决此问题,请在服务器中更改循环,为整个循环打开一次管道:

while true
do
    if read txt
    ....
done < "$pipe"

这样,管道将打开一次并保持打开状态。

您将需要注意在循环中运行的内容,因为循环中的所有处理会将stdin附加到命名管道。您将要确保将循环中所有进程的stdin从其他位置重定向,否则它们可能会消耗管道中的数据。

编辑:现在的问题是,当最后一个客户端关闭管道时,读取时会出现EOF,您可以使用jilles方法复制文件描述符,或者可以确保自己也是客户端并保持写方打开的管道:

while true
do
    if read txt
    ....
done < "$pipe" 3> "$pipe"

这将使管道的写端在fd
3上保持打开状态。与stdin一样,此文件描述符也适用相同的警告。您将需要关闭它,以便任何子进程都不继承它。它的重要性可能不如stdin,但它会更干净。



 类似资料:
  • 问题内容: 有人可以在Linux的Bash中发布使用命名管道的简单示例吗? 问题答案: 实际使用命名管道的最好例子之一… 从http://en.wikipedia.org/wiki/Netcat: 另一个有用的行为是用作代理。端口和主机都可以重定向。看这个例子: 端口12345代表请求。 这将在端口12345上启动服务器,并且所有连接都将重定向到。如果网络浏览器向发送了请求,则请求将发送到goog

  • 问题内容: 我经历了各种不同的名为管道客户端/服务器实现的Linux,但其中大多数在读取/写入时使用阻止默认值。 因为我已经在使用poll()来检查其他标志,所以也可以通过poll()检查传入的FIFO数据是一个好主意… 经过所有研究,我认为以O_RDWR模式打开管道是防止在没有任何编写者打开管道的情况下无限期发生EOF事件的唯一方法。 这样,管道的两端都关闭了,其他客户端也可以打开可写端。作为回

  • 主要内容:使用命名管道双向通信管道是用于相关过程之间的通信。 我们是否可以使用管道进行不相关的进程通信,比方说,我们要从一个终端执行客户端程序,从另一个终端执行服务器程序? 答案是否定的。那么怎样才能实现不相关的进程通信,简单的答案就是使用 命名管道。 即使这适用于相关的进程,但是使用命名管道进行相关的进程通信没有任何意义。 我们使用一个管道进行单向通信,两个管道进行双向通信。 命名管道是否适用相同的条件。 答案是否定的,我们

  • 问题内容: 在groovy脚本中(用于jenkins管道):如何运行命令而不是命令? 我尝试了以下方法: 在通话中致电“ ” : 将呼叫替换为呼叫: 附加信息: 我的命令比命令更复杂。 问题答案: 您提供的Groovy脚本正在将结果脚本中的第一行格式化为空白行。shebang告诉脚本使用/ bin / bash而不是/ bin / sh运行,它需要位于文件的第一行,否则它将被忽略。 因此,您应该这

  • 我的问题是DataInputStream在流仍然打开但没有数据时抛出。我希望这会简单地阻止(尽管文档在这个问题上非常模糊)。 DataOutputStream: DataInPutStream: 另一边我用这个: 它给出: 奇怪的是,没有阻塞,它只是愉快地继续运行,没有阻塞,但也没有填充数组中的任何值(将数组初始化为{9,9,9,9}后仍然是9,9,9,9)。 这就是疯狂的感觉吗?

  • 我正试图通过命名管道使用VB.NET从windows应用程序连接到SQL Server2008。 但这根本不连接到服务器。这是使用命名管道连接到sql sererver的正确方法吗?