shell脚本和tty泄漏内存

shell脚本和tty泄漏内存

我正在树莓派上运行 shell 脚本来与 arduino 进行通信。我的内存每小时就会消耗大约 50 MB 的空间。

脚本本身只是连接到 ttyACM0(arduino 的 USB 终端),然后向其发送一个字符。该脚本每分钟运行一次以检查临时值。第一个参数的值可以是:“a”、“b”或“T”。在“a”或“b”的情况下,它打开/关闭继电器。在“T”的情况下,arduino 返回 3 个温度值,我将其存储。

该脚本运行良好(我可以控制继电器并接收值,而无需重新启动arduinos串口)。但由于我的记忆力有限,我遇到了问题。

我的设备上没有运行任何其他进程,并且已经尝试了新的 raspbian 设置。

有人可以告诉我这个脚本中哪里存在内存泄漏以及是否存在内存泄漏以及如何防止它们吗?

编辑:我找到了罪魁祸首:脚本随着时间的推移运行数百个“cat”命令,我该如何摆脱它们?我已经尝试过killall cat,但这会重置我的树莓派串行连接(我想禁止它,因为它会“重新启动”arduino!)@mikeserv 指出我使用 head,它在读取定义的行数后自动退出,不幸的是也不行。如果没有管道,我似乎无法将温度写入输出文件中

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

不起作用,因为它也没有退出,而且我没有得到任何输出

我可以每隔几分钟杀死所有的猫来释放内存,但这也会将我的tty重置为arduino(因此它会重新启动并丢失继电器的状态)

Edit2:我没有设法让这个工作(我尝试了几种可能性,包括 minicom、屏幕等...),但接收部分是我遇到麻烦的地方。不过,向 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 的读/写文件描述符,该描述符会自动由子级继承(例如克隆的子 shell),这就是为什么您可以cat <&3稍后在后台管道中读取它。

但问题是,因为您将管道置于后台 - 将其和所有关联的子 shell 放在一个单独的进程组中 - 当您稍后...时,管道进程不会关闭。

exec 3<&-

...并关闭脚本当前 shell 进程的描述符。反而...

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行并调用一个更轻量级且可能更快的 shell。我推荐dash这样的东西。看一看这里如果对各种 shell 之间的性能比较的问答感兴趣 - 包括bashdash

答案2

我无法给你写修复程序,但我可以解释发生了什么。

  1. 你正在打开港口。
  2. 您检查/发送温度传感器的值。
  3. 你正在关闭端口。

一些可能性:

  • Arduino 的 USB 缓冲区一直在填充,直到您重新运行脚本,但缓冲区在上次运行时仍然已满。由于先前运行的数量在缓冲区中堆积,您无意中导致了通信端口中的缓冲区溢出。
  • Raspberry Pi 中也出现同样的问题,因为缓冲区从另一个方向以相同的方式填充。

您需要找到一种方法来清除两侧的缓冲区,以便门打开,发送 3,然后关闭,打开,发送 3,然后关闭等。正如 Yeti 所建议的,Subshel​​ling 是一种方法。我确信还有其他方法,例如关闭并重新打开缓冲区但不杀死 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

相关内容