使用 socat 模拟串行设备时如何删除缓冲区

使用 socat 模拟串行设备时如何删除缓冲区

我正在创建一个像这样的假串行设备:

socat -d -d pty,raw,echo=0 pty,raw,echo=0

这每次都会创建具有新编号的设备。让我们打电话/dev/pty/6给他们/dev/pty/7

为了模拟我的测试机器上没有的硬件设备,我以/dev/pty/6每秒二十行的速度写入。

与此类似(在我的实际项目中,我正在使用 python 编写,但这显示了相同的问题)

while true
do
    date > /dev/pts/6
    sleep 0.05
done

我注意到,如果我让它cat /dev/pty/7运行几秒钟,它就会从我上次停下的地方开始,而不是从我写的下一行开始。

所以如果我timeout 5 cat -n /dev/pty/7一遍又一遍地看,中间没有休息,我总是会得到大约 100 行。但是,如果我让“生产者”脚本运行五分钟,然后运行timeout 5 cat -n /dev/pty/7,我会得到数千行 - 我猜是自上次阅读以来编写的所有行。

我试图模拟的实际硬件的行为并非如此。

timeout 5 cat -n /dev/ttyUSB0总是给我大约一百行。它只是扔掉了没有人读的行。

如何更好地模拟硬件设备的这方面?制作者脚本是否需要检查是否有人正在读取且仅写入?或者我必须给 socat 命令提供一个选项吗?

我认为当我使用 python 执行此操作时,它正在写入的缓冲区已满,并且生产者程序陷入等待写入空间的状态,但我无法在本例中证明这一点。

答案1

使用此方法的问题socat是 pty 有很大的缓冲区。由于您使用的是 Python,因此您可以尝试自己模拟一些东西。例如,

#!/usr/bin/python3
# https://unix.stackexchange.com/a/735081/119298
# some termios code from:
#  https://github.com/pyserial/pyserial/blob/master/serial/serialposix.py
import os, time, struct, fcntl, termios

class pseudotty:
    TIOCM_zero_str = struct.pack('I', 0)

    def __init__(self, name, mode):
        master, slave = os.openpty()
        sname = os.readlink(f"/proc/self/fd/{slave}")   # "/dev/pts/6"
        os.unlink(name)
        os.symlink(sname, name)
        self.mfd = master
        self.sfd = slave
        self.file = os.fdopen(master, mode)

    def waiting(self):
        result = fcntl.ioctl(self.sfd, termios.FIONREAD, self.TIOCM_zero_str)
        return struct.unpack('I', result)[0]

datain = pseudotty("input", "r")
dataout = pseudotty("output", "w")

while True:
    d = datain.file.readline()
    wait = dataout.waiting()
    if wait<200:
        dataout.file.write(d)

这将创建并打开两个 pty,在当前工作目录中具有指向每个从站的符号链接,input并且output.因此,您可以引导循环使用date >input, 和catto cat output

该程序永远循环:它从输入 pty 读取一行,测试输出 pty 中当前等待的字符数,如果少于 200 个字符,则将该行写入输出。如果超过 200 个字符,则输入将被丢弃。

这可以让您对两个 pty 之间缓冲的数据量进行一些控制。当然,您可以将 替换readline()read(1),并将 200 限制替换为 15;这可以模拟许多具有有限 fifo 的串行设备。

相关内容