将较长的字符串传输到 tr 会导致挂起和 CPU 峰值

将较长的字符串传输到 tr 会导致挂起和 CPU 峰值

MacOS 优胜美地 (10.10.5)。我知道这是 Unix/Linux 部分...但我想这个问题可能比在 MacOS 领域更适合。

我的终端在启动时开始挂起,然后显示提示......并且 CPU 使用率同时飙升。我可以按 CTRL-C 然后得到提示(大概是退出一些挂起/运行的 .bashrc/.profile/etc)。

我很快发现 .bashrc 中的某些行导致了挂起。这是新的(即我没有更改 .bashrc 中的任何内容,并且所有内容都工作正常),因此系统上发生了一些变化。

似乎管道某些较长的字符串会导致挂起/CPU 峰值。

我可以通过管道连接字符串tr -d '\n'并查看它是否挂起来重现这一点。

macattack:~ $ openssl rand -base64 93  | tr -d '\n'
eDsz4JqFX/HAVjplNI6WDWwPRp9l9snp6UKp/pLn+GbBvJx0+ZMvSJFS/SuCwjMRRXVXfUvBdkaH1R0UgCr2UOf283MvHVTRusLFEVPcGCIz1t3sFMU/3foRzNWVmattp@macattack:~ $ openssl rand -base64 94 | tr -d '\n'
^C
mattp@macattack:~ $ openssl rand -base64 94 | tr -du '\n'
^C

看起来 93 个字符是 tr 开始挂起的神奇数字。 openssl 没有挂起(即,如果我移除tr所有出口的管道)。然而我原来的问题线恰好是不同的长度。

mattp@macattack:~ $ echo 'echo -e "$TS\t${TERM_SESSION_ID}\t$(pwd)\t$(history 1 | cut -c 8-)\n" >> $HOME/.history/history-$(date "+%Y-%m-%d")-${USER}.log;' | tr -d '\n'
^C-bash: echo: write error: Interrupted system call

mattp@macattack:~ $ echo 'echo -e "$TS\t${TERM_SESSION_ID}\t$(pwd)\t$(history 1 | cut -c 8-)\n" >> $HOME/.history/history-$(date "+%Y-%m-%d")-${USER}.log' | tr -d '\n'

mattp@macattack:~ $ echo 'echo -e "$TS\t${TERM_SESSION_ID}\t$(pwd)\t$(history 1 | cut -c 8-)\n" >> $HOME/.history/history-$(date "+%Y-%m-%d")-${USER}.log' | wc -c
     128
mattp@macattack:~ $

这可能是管道问题而不是tr问题。我可以重现同样的问题sed(命令没有意义......只是说明挂起)。

mattp@macattack:~ $ echo 'echo -e "$TS\t${TERM_SESSION_ID}\t$(pwd)\t$(history 1 | cut -c 8-)\n" >> $HOME/.history/history-$(date "+%Y-%m-%d")-${USER}.log;'  | sed 's/\n/ /g'
^C-bash: echo: write error: Interrupted system call

mattp@macattack:~ $ echo 'echo -e "$TS\t${TERM_SESSION_ID}\t$(pwd)\t$(history 1 | cut -c 8-)\n" >> $HOME/.history/history-$(date "+%Y-%m-%d")-${USER}.log'  | sed 's/\n/ /g'
echo -e "$TS\t${TERM_SESSION_ID}\t$(pwd)\t$(history 1 | cut -c 8-)\n" >> $HOME/.history/history-$(date "+%Y-%m-%d")-${USER}.log
mattp@macattack:~

我已经没有办法解决这个问题了。
挂起的命令在随机的 centos Linux 服务器上运行良好。直到最近,这些命令在 macOS 上都运行良好。我以前从未遇到过管道悬挂的情况。我想也许输入中的奇怪字符导致了问题......但 openssl 随机字符串显示否则。 ulimit 与另一台没有相同问题的 Mac 上的 ulimit 相同。

mattp@macattack:~ $ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
file size               (blocks, -f) unlimited
max locked memory       (kbytes, -l) unlimited
max memory size         (kbytes, -m) unlimited
open files                      (-n) 7168
pipe size            (512 bytes, -p) 1
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 709
virtual memory          (kbytes, -v) unlimited

使用dtrusstr似乎挂在 read_nocancel 调用上。

更新

取得进展。找到有关悬挂和管道缓冲区大小的评论。从这里窃取了一个测试脚本: 管道缓冲区有多大?

问题发生时运行显示管道缓冲区为 128 字节。重新启动(问题暂时消失)并且管道缓冲区为 65536 字节。请参阅下面的测试输出。

所以现在的问题是,为什么/如何“某些东西”减少系统上的管道缓冲区大小。

有问题

$ /bin/bash -c 'for p in {0..18}; do pipe-buffer-test.sh $((2 ** $p)) 0.5; done'
write size:          1; bytes successfully before error: 128
write size:          2; bytes successfully before error: 128
write size:          4; bytes successfully before error: 128
write size:          8; bytes successfully before error: 128
write size:         16; bytes successfully before error: 128
write size:         32; bytes successfully before error: 128
write size:         64; bytes successfully before error: 128
write size:        128; bytes successfully before error: 128
write size:        256; bytes successfully before error: 0
write size:        512; bytes successfully before error: 0
write size:       1024; bytes successfully before error: 0
write size:       2048; bytes successfully before error: 0
write size:       4096; bytes successfully before error: 0
write size:       8192; bytes successfully before error: 0
write size:      16384; bytes successfully before error: 0
write size:      32768; bytes successfully before error: 0
write size:      65536; bytes successfully before error: 0
write size:     131072; bytes successfully before error: 0
write size:     262144; bytes successfully before error: 0

重启后(问题暂时消失)

$ /bin/bash -c 'for p in {0..18}; do pipe-buffer-test.sh $((2 ** $p)) 0.5; done'
write size:          1; bytes successfully before error: 65536
write size:          2; bytes successfully before error: 65536
write size:          4; bytes successfully before error: 65536
write size:          8; bytes successfully before error: 65536
write size:         16; bytes successfully before error: 65536
write size:         32; bytes successfully before error: 65536
write size:         64; bytes successfully before error: 65536
write size:        128; bytes successfully before error: 65536
write size:        256; bytes successfully before error: 65536
write size:        512; bytes successfully before error: 65536
write size:       1024; bytes successfully before error: 65536
write size:       2048; bytes successfully before error: 65536
write size:       4096; bytes successfully before error: 65536
write size:       8192; bytes successfully before error: 65536
write size:      16384; bytes successfully before error: 65536
write size:      32768; bytes successfully before error: 65536
write size:      65536; bytes successfully before error: 65536
write size:     131072; bytes successfully before error: 0
write size:     262144; bytes successfully before error: 0

答案1

根据 @Barmar 关于泄漏内核缓冲区的评论,我查看了当前的非操作系统 kext。我意识到最近安装的 BlockBlock 中有一个相对较新的(https://objective-see.com/products/blockblock.html)。

卸载了BlockBlock,重新启动,问题没有再出现。所以BlockBlock是这个案例的罪魁祸首,我向作者报告了这个问题。

然而,这并不是特别令人满意,因为我主要采用猜测和检查的方法来找出原因,而且说实话,我并不真正理解根本原因(就操作系统而言),这意味着我不是- 将来更明智地解决此类问题。

如果有人遇到此问题并且可以更详细地解释发生的情况并提供故障排除方法,那么这将是比“卸载 BlockBlock”更好的答案。

相关内容