web-dev-qa-db-ja.com

より長い文字列を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:~

これをトラブルシューティングするためのアイデアが不足しています。
ぶら下がっているコマンドは、ランダムなcentosLinuxサーバーで正常に実行されます。コマンドは最近まで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
mattpr

カーネルバッファのリークに関する@Barmarのコメントに基づいて、現在の非OSkextを調べました。 BlockBlockの最近のインストールから比較的新しいものがあることに気づきました( https://objective-see.com/products/blockblock.html )。

BlockBlockをアンインストールし、再起動しましたが、問題は再発していません。そのため、この場合はBlockBlockが原因であり、私はこの問題を作成者に報告しました。

ただし、原因を特定するためにほとんど推測とチェックのアプローチを採用したため、これは特に満足のいくものではありません。正直なところ、根本的な原因(OSの観点から)がよくわかりません。つまり、私は誰でもありません。 -将来この種の問題をトラブルシューティングするための賢明な方法です。

誰かがこれに遭遇し、何が起こっているのかをより詳細に説明し、トラブルシューティングのアプローチを提供できる場合は、「BlockBlockをアンインストールする」よりもはるかに良い答えになります。

0
mattpr