在bash中使用管道从串行设备缓冲数据

3

我有一个类似这样的脚本:

while true; do
    read -t10 -d$'\n' input_from_serial_device </dev/ttyS0
    # do some costly processing on the string
done

问题在于它会错过串行设备的下一个输入,因为它正在燃烧CPU周期进行昂贵的字符串处理。我认为我可以通过使用管道来解决这个问题,因为bash将在两个进程之间缓冲输入。
( while true; do
     read -d$'\n' input_from_serial_device </dev/ttyS0
     echo $input_from_serial_device
  done ) | ( while true; do
     read -t10 input_from_first_process
     # costly string processing
  done )

我首先想确认一下我是否正确理解了管道,并且这确实会像我打算的那样缓存两个进程之间的输入。这个想法正确吗?
其次,如果我在第二个进程中得到了我想要的输入,有没有一种方法可以立即终止两个进程,而不是从第二个进程退出,并等待下一个输入再退出第一个进程?
最后,我意识到bash不是最好的方式来做这件事,我目前正在开发一个C程序,但我很想将其作为一种中间解决方案使其工作。
谢谢!

1
我认为你不需要在子shell中运行协作处理程序(我通常在循环中读写而不需要额外的( )括号)。这是一个有趣的问题,但超出了我的经验范围,也很难测试(现在谁还有串口!?;-)。祝好运。 - shellter
管道将缓冲一些数据,但我不知道有多少;这肯定是系统相关的。例如,最近版本的Linux为命名管道缓冲64K,因此我认为对于临时“未命名”管道也会有类似的情况。 - chepner
1
我曾经在Sun4上测试过管道能容纳多少数据(很久以前的事情了),结果取决于/tmp中有多少可用磁盘空间。(看一下vi的一个打开会话在哪里创建它的工作文件,某些机器上可能是/var/tmp)。大约在同一时间,我的一个同事试图将一个100MB的文件塞入Windows管道,而我从同一项目的测试中知道,Windows NT肯定有限制(大约64K)。因此,不同的操作系统和版本(以及权限和配置)可能会影响这个问题。最好的方法是设置一些测试并观察发生了什么。祝你好运。 - shellter
1
无论对于给定的系统,PIPE_BUF是多少。如果未声明PIPE_BUF,则为_POSIX_PIPE_BUF(cygwin显示512)。我的SUSE Linux框显示64K。 - jim mcnamara
谢谢大家。@rici的解决方案正中要害,但是我从你们的评论中学到了很多关于bash和管道的知识。谢谢! - Froskoy
1个回答

6
问题不在于管道,而在于串行设备。
当你写下以下命令时:
while true; do
  read -t10 -d$'\n' input_from_serial_device </dev/ttyS0
  # use a lot of time
done

结果是串行设备被打开,从中读取一行,然后关闭。在执行完#使用了很长时间之前,它不会再次打开。当串行设备未打开时,传入的串行输入将被丢弃。
如果输入确实比处理速度快,那么仅仅缓冲是不够的。你必须丢弃输入。另一方面,如果输入以平均速度流入,可以保持串行设备打开来实现您想要的效果。
while true; do
  read -t10 -r input_from_serial_device
  # process input_from_serial_device
done < /dev/ttyS0

注意:我在你的read调用中添加了-r(这几乎肯定是必需的),并删除了-d$'\n',因为那是默认值。

谢谢!这正是问题所在,这个简单的解决方案解决了一切!非常感谢! - Froskoy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接