더 긴 문자열을 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

이를 사용하면 dtrussread_nocancel tr호출이 중단되는 것 같습니다.

업데이트

진전을 보이고 있습니다. 행잉 및 파이프 버퍼 크기에 대한 의견을 찾았습니다. 여기에서 테스트 스크립트를 훔쳤습니다. 파이프 버퍼는 얼마나 큽니까?

문제가 발생하는 동안 실행하면 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의 의견을 바탕으로 현재 비 OS kext를 살펴보았습니다. 저는 최근 BlockBlock을 설치하면서 비교적 새로운 것이 있다는 것을 깨달았습니다(https://objective-see.com/products/blockblock.html).

BlockBlock을 제거하고 다시 시작해도 문제가 재발되지 않았습니다. 그래서 이 경우에는 BlockBlock이 범인이었고 작성자에게 이 문제를 보고했습니다.

그러나 원인을 파악하기 위해 주로 추측하고 확인하는 접근 방식을 취했기 때문에 이것은 특히 만족스럽지 않습니다. 솔직히 말해서 근본 원인(OS 측면에서)을 실제로 이해하지 못합니다. -앞으로 이런 종류의 문제를 해결하는 것이 현명합니다.

누군가 이 문제를 발견하고 무슨 일이 일어나고 있는지 더 자세히 설명하고 문제 해결 방법을 제공할 수 있다면 "BlockBlock 제거"보다 훨씬 더 나은 대답이 될 것입니다.

관련 정보