將較長的字串傳輸到 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 隨機字串顯示否則。 ulimits 與另一台沒有相同問題的 Mac 上的相同。

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」更好的答案。

相關內容