伪终端主控读取刚写入的数据

10 投票
2 回答
4334 浏览
提问于 2025-04-16 17:58

我正在做一个项目,这个项目需要把“虚拟设备”(用Python进程表示)和真实设备(也用串口连接)连接起来。我使用伪终端来连接多个(超过两个)串口通信进程(模拟串行设备),但是遇到了一些问题。

我有一个Python进程,它生成伪终端,并把伪终端的从端(slave)链接到一个文件(这样进程就可以通过这个文件创建一个pyserial对象),而主端(master)则由我的伪终端生成进程来管理和读取。当一个主端收到数据时,这些数据会被记录下来,然后写入到其他的主端。如果监听的进程一直在,这种方法是有效的。

问题在于,当虚拟设备崩溃或者根本没有启动时(这在这个项目中是一个合理的使用场景)。在我的系统上,如果数据写入到伪终端的主端,而从端没有任何东西在监听,那么在主端调用读取(read)时,会返回刚刚写入的数据!这意味着设备会收到重复的数据——这可不好!

举个例子:

>>master, slave = pty.openpty()
>>os.write(master,"Hello!")
6
>>os.read(master,6)
'Hello!'

我希望调用read()时能够阻塞,直到从端发送数据。实际上,这正是从设备的行为——它可以写入,然后os.read(slave,1)会阻塞,直到主端写入数据。

我的“虚拟设备”需要能够传递一个文件名来打开串口对象;我尝试链接主端,但这导致我的虚拟设备打开了/dev/ptmx,这样会创建一对新的伪终端,而不是链接到已经存在的从端!

有没有办法改变主端的行为?或者仅仅是获取一个与从设备对应的主端文件名(而不是/dev/ptmx)?

提前谢谢你!

2 个回答

5

如果有人看到这个问题,而jszakmeister的回答没有解决你的问题,这里有我成功的做法。

openpty这个函数似乎会在一种叫做“规范模式”的状态下创建伪终端,并且会开启回显功能。这可能和你预想的有点不一样。你可以通过使用tty.setraw这个函数来改变模式,下面是一个简单的使用openpty的回显服务器的例子:

master, slave = os.openpty()
tty.setraw(master, termios.TCSANOW)
print("Connect to:", os.ttyname(slave))

while True:
    try:
        data = os.read(master, 10000)
    except OSError:
        break
    if not data:
        break
    os.write(master, data)
6

我很确定这是因为默认情况下是开启回显的。借用一下Python termios 文档中的内容,你可以这样做:

master, slave = os.openpty()    # It's preferred to use os.openpty()
old_settings = termios.tcgetattr(master)
new_settings = termios.tcgetattr(master)   # Does this to avoid modifying a reference that also modifies old_settings
new_settings[3] = new_settings[3] & ~termios.ECHO
termios.tcsetattr(master, termios.TCSADRAIN, new_settings)

你可以使用以下代码来恢复之前的设置:

termios.tcsetattr(master, termios.TCSADRAIN, old_settings)

撰写回答