쉘 스크립트 및 tty 누출 메모리

쉘 스크립트 및 tty 누출 메모리

Arduino와 통신하기 위해 라즈베리 파이에서 쉘 스크립트를 실행하고 있습니다. 내 메모리는 시간당 약 50MB를 소비합니다.

스크립트 자체는 단순히 ttyACM0(arduino의 USB 터미널)에 연결하고 여기에 문자를 보냅니다. 스크립트는 1분마다 실행되어 임시 값을 확인합니다. 첫 번째 매개변수의 값은 "a", "b" 또는 "T"일 수 있습니다. "a" 또는 "b"의 경우 릴레이를 켜거나 끕니다. "T"의 경우 Arduino는 내가 저장한 3개의 온도 값을 반환합니다.

스크립트는 잘 작동합니다(arduinos 직렬 포트를 다시 시작하지 않고도 릴레이를 제어하고 값을 받을 수 있습니다). 하지만 제한된 메모리로 인해 문제가 발생합니다.

내 장치에서 실행 중인 다른 프로세스가 없으며 새로운 Raspbian 설정을 시도했습니다.

누군가 이 스크립트에서 메모리 누수가 어디에 있는지, 그리고 이를 방지하는 방법을 말해 줄 수 있습니까?

편집: 시간이 지남에 따라 수백 개의 "cat" 명령을 실행하는 스크립트인 범인을 찾았습니다. 어떻게 제거합니까? 나는 killall cat을 시도했지만 이것은 내 라즈베리 파이 직렬 연결을 재설정합니다(arduino를 "다시 시작"하므로 비활성화하고 싶습니다!) @mikeserv는 정의된 라인을 읽는 헤드를 사용한다고 지적했습니다. 몇 초 후에 자동으로 종료됩니다. 불행히도 작동하지 않습니다. 파이프 없이는 출력 파일에 온도를 쓸 수 없는 것 같습니다.

head -n3 <&3 >>/home/pi/output

종료되지도 않고 출력도 나오지 않기 때문에 작동하지 않습니다.

메모리를 확보하기 위해 몇 분마다 모든 고양이를 죽일 수 있지만 그렇게 하면 내 tty도 arduino로 재설정됩니다(그래서 재부팅하면 릴레이 상태가 손실됩니다).

Edit2: 나는 이것을 작동시키지 못했습니다(미니콤, 스크린 등을 포함하여 여러 가지 가능성을 시도했습니다...). 하지만 수신 부분이 문제가 있습니다. 그러나 Arduino에 문자를 보내는 것은 훌륭하게 작동합니다!

#!/bin/bash

# READ / WRITE ARDUINO

exec 3<> /dev/ttyACM0

echo "connected, sleep for 1 sec..."
sleep 1

echo "send $1..."

echo "$1" >&3

if [ "$1" = "T" ]
then
        cat <&3 | cat >> /home/pi/output &2>1
else
        echo "nothing to save"
fi

echo "closing.."

exec 3>&-

exit 0

답변1

때를:

exec 3<> /dev/ttyACM0

...3의 USB 직렬 tty에 대한 읽기/쓰기 파일 설명자를 엽니다. 이는 하위 항목(예: 복제된 서브쉘)에 의해 자동으로 상속되므로 cat <&3나중에 백그라운드 파이프에서 읽을 수 있습니다.

그러나 문제는 파이프를 백그라운드에 배치하기 때문에 파이프와 관련된 모든 하위 쉘을 별도의 프로세스 그룹에 넣기 때문에 나중에 파이프 프로세스가 닫히지 않는다는 것입니다.

exec 3<&-

...현재 쉘 프로세스에 대한 스크립트 설명자를 닫습니다. 대신에...

cat </dev/ttyACM0 | cat >> file &

...백그라운드에 매달리고, 읽을 것이 없는 한 아무것도 읽지 않으며, 항상 해당 tty에 대한 열린 라인을 유지합니다. 이는 cat입력이 EOF에서만 종료되고, 이 경우 입력을 수신하지 않기 때문입니다 .

넌 할 수있어:

cat /dev/tty

... 프롬프트에 따라 동작을 대략적으로 설명합니다.

당신이 해야 할 일은 필요한 내용을 읽자마자 입력을 명시적으로 종료하거나, cat읽은 후에 입력을 명시적으로 종료하는 것입니다. 다음을 수행할 수 있습니다.

head -n"$GUARANTEED_NUM_AVAIL_INPUT LINES" <&3 >>file

...또는 또는 sed [num]q유사합니다. 이렇게 하면 완전히 수행하는 것을 피해야 하며 |pipe아마도 &배경을 완전히 생략할 수도 있습니다. 그렇지 않으면 죽이기 위해 다음을 cat수행할 수 있습니다.

cat <&3 >>file &
sleep 1 && kill "$!"

...하지만 입력을 종료하는 것이 훨씬 간단하므로 이것이 필요하지 않을 수도 있습니다.

그런데 스크립트가 이미 이식 가능한 구문으로 완전히 구성되어 있다는 점은 주목할 가치가 있으므로 해당 #!/bin/bash줄을 변경하고 더 가볍고 더 빠른 셸을 호출하는 것이 가치가 있을 수 있습니다. 나는 dash이런 것을 추천합니다. 구경하다여기bash및 를 포함한 다양한 쉘 간의 성능 비교에 대한 Q&A에 관심이 있는 경우 dash.

답변2

수정 사항을 작성할 수는 없지만 무슨 일이 일어나고 있는지 설명할 수는 있습니다.

  1. 항구를 개방하고 있습니다.
  2. 온도 센서의 값을 확인/전송합니다.
  3. 포트를 닫고 있습니다.

몇 가지 가능성:

  • Arduino의 USB 버퍼는 스크립트를 다시 실행할 때까지 계속 채워지지만 버퍼는 마지막 실행 이후에도 여전히 가득 차 있습니다. 이전 실행의 숫자가 버퍼에 쌓여 통신 포트에 버퍼 오버플로가 발생했습니다.
  • 다른 방향에서도 같은 방식으로 버퍼가 채워지므로 Raspberry Pi에서도 동일한 문제가 발생합니다.

문이 열리고, 3을 보내고, 닫히고, 열리고, 3을 보내고, 닫히도록 양쪽의 버퍼를 지우는 방법을 찾아야 합니다. Yeti가 제안한 것처럼 Subshelling은 하나의 접근 방식입니다. 버퍼를 닫았다가 다시 열었지만 CommPort를 종료하지 않는 등의 다른 방법이 있을 것이라고 확신합니다. 나는 통신 드라이버 전문가가 아니기 때문에 이론만 제시할 수 있습니다.

고쳐 쓰다

If [ "$1" = "T" ]
then
        cat <&3 | cat >> /home/pi/output &2>1
        cat <&3 | cat >> /dev/null
else
        echo "nothing to save"
        cat <&3 | cat >> /dev/null
fi

관련 정보