【问题标题】:Error while running a shell script : "pipe call failed"运行 shell 脚本时出错:“管道调用失败”
【发布时间】:2014-04-16 14:57:51
【问题描述】:

我正在无限循环中执行一个 shell 脚本。脚本旨在获取连接的设备。但是运行一段时间后,会在显示错误信息pipe call failed后停止执行。

这是我收到此错误的行

arp -n -i eth1 | grep "?" | awk '{print $4}' > out.txt

在将此输出重定向到文件时我做错了什么吗?有没有办法在 shell 脚本中处理文件描述符?

编辑:这是我的脚本

HOME_NETWORK_INTERFACE=eth1
echo "Generating list of MoCA device MACs..."
if [ $# -lt 1 ]; then
        echo "Error! Insufficient arguments. Format is $0 <output file path>"
        exit 1
fi
arp -n -i $HOME_NETWORK_INTERFACE | grep "?" | awk '{print $4}' > $1
echo "Done"

编辑:

我发现这个问题是由于在 cpp 应用程序中使用了“system”命令造成的。如果使用“系统”执行此脚本,例如 1000 次,我将收到此错误。有人对此有任何想法吗?提前致谢

【问题讨论】:

  • 我不认为 grep 会为此引发语法错误,但您可能想要修复 [grep(partmaps.org/era/unix/award.html#grep) 的无用使用; arp | awk '/\?/{ print $4 }'
  • pipe call failed 听起来更像是内部系统错误,而不是与您的脚本直接相关的错误。不过,您可以发布更多上下文脚本吗?您发布的行看起来不错。
  • 通过调用system(),您正在分叉一个子进程。每当一个子进程被分叉时,系统都会尝试将父内存复制到子内存。如果你尝试 fork 一个子进程 1000 次,内存可能还没有释放到系统,因此系统会认为父进程有更大的内存,它实际上正在使用,你正在耗尽内存根据记忆会计师。您可以设置限制并将内存过度使用设置为 2。但是过度使用并不是一个好主意。
  • system() 执行命令解释器,即 shell,它比直接 fork/exec 慢。也许您可以看看是否可以避免使用 system() 并尝试使用 fork/exec 以获得更多控制权(例如,清理 fork/exec 调用之间的内存。
  • 当您说“cpp 应用程序”时,您是指用 C++ 编写的应用程序吗?你的意思是你的脚本是从你的应用程序中运行的吗?我估计“管道调用失败”的最可能原因是您打开了太多文件描述符;您不会在可能的情况下关闭某些文件(或管道或套接字)。错误是来自 shell 脚本本身,还是来自通过system() 运行它的 C++ 程序?当system() 调用返回时,孩子已经死了;它的所有文件描述符都已关闭。应用程序应该没有问题。您是否正确设置了 FD_CLOEXEC?

标签: c++ c linux shell sh


【解决方案1】:

错误消息“管道调用失败”意味着没有更多的空闲内存可以分配。 按照 Dale 的建议,使用 ulimit -a 检查系统的限制并验证调用 system 命令的方式:顺序或并行。

从内核 2.6.11 开始,管道的大小为 64KB。此外,system() 命令通过调用/bin/sh -c command 来执行命令,并在命令完成后返回。调用 system 命令 1000 次效率不是很高,所以我的建议是在脚本中添加一个 while 循环以避免性能下降(您可以提供迭代次数作为第二个论点)。

最后,如果要作为可执行文件运行,最好将 shebang (#!/bin/sh) 添加为脚本的第一行。这不是绝对必要的,因为如果 shebang 缺失,则使用默认 shell(例如 /bin/sh)。

#!/bin/sh

HOME_NETWORK_INTERFACE=eth0
echo "Generating list of MoCA device MACs..."
if [ $# -lt 1 ]; then
        echo "Error! Insufficient arguments. Format is $0 <output file path>"
        exit 1
fi

X=0
while [ $X -lt $2 ]
do
    arp -n -i $HOME_NETWORK_INTERFACE | grep "?" | awk '{print $4}' > $1
    X=`expr $X + 1`
done
echo "Done"

【讨论】:

    【解决方案2】:

    我终于找到了问题所在。我在我的应用程序中使用套接字通信。一些套接字没有正确关闭。所以一段时间后文件描述符将耗尽,这导致“管道调用失败”的问题,因为管道也使用描述符。

    【讨论】:

      【解决方案3】:

      “alvits”已经准确指出了使用系统命令的问题。通常我们尝试实现等效的“C”代码而不是使用系统命令以避免内存峰值。

      您似乎在 C++ 代码中使用了一个 while 循环并调用了这个脚本。试一下,如果你可以在 while 循环中运行脚本(导致只有一个系统调用),将 o/p 重定向到一个文件并连续读取文件以获得所需的结果。

      【讨论】:

        【解决方案4】:

        我没有发现您的脚本有任何明显的问题;错误消息确实“听起来像”系统资源问题(这些值在系统之间可能会有很大差异。)一些建议:

        • 当涉及特殊字符并且您需要否定可能的 shell 变量修改时,请考虑使用“fgrep”(不要认为这是一个因素,但以防万一......)

        • 使用 ulimit -a

        • 检查您正在运行的用户/进程的“限制”
        • 您可能“消耗”的打开文件(猜测)多于允许的数量,或者可能是“系统”出现问题(而不是“用户进程”); “真正的”问题可能是同时运行的其他进程正在消耗资源。

        • 如果您的系统/用户“限制”没有问题,并且由于您声明这是一个“无限循环”,那么我猜您正在生成多个进程而不是只运行一个。一旦您达到 pipefile-related 资源的“硬限制”,脚本就会失败并出现“系统错误”。

          $ ulimit -a | awk '{printf "%3d | %s\n", NR, $0}'

        1 |核心文件大小(块,-c)0
        2 |数据段大小 (kbytes, -d) 无限制
        3 |调度优先级 (-e) 0
        4 |文件大小(块,-f)无限制
        5 |待处理信号 (-i) 46232
        6 |最大锁定内存 (kbytes, -l) 64
        7 |最大内存大小 (kbytes, -m) 无限制
        8 |打开文件 (-n) 1024
        9 |管道大小(512 字节,-p)8
        10 | POSIX 消息队列(字节,-q)819200
        11 |实时优先级 (-r) 0
        12 |堆栈大小(千字节,-s)10240
        13 | cpu 时间(秒,-t)无限制
        14 |最大用户进程 (-u) 1024
        15 |虚拟内存 (kbytes, -v) 无限制
        16 |文件锁 (-x) 无限制

        【讨论】:

          猜你喜欢
          • 2020-05-08
          • 1970-01-01
          • 2022-01-12
          • 1970-01-01
          • 2022-12-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多