PTY多路复用器

6
我正在尝试在Linux上对串口进行多路复用。我正在使用嵌入式系统,该系统仅具有一个串口,因此最好有多个进程与之通信。
常见的用例是:
- 运行测试的主程序(发送命令并接收输出); - 另一个记录所有串口活动的程序; - 用户终端打开以发送其他命令和/或在测试期间发生错误后执行事后分析。
首先,我编写了一个简单的Python脚本来打开n个伪终端对(加上串口),并使用poll语句将输入/输出定向到正确的位置:
# Removed boiler plate and error checking for clarity

##### Serial port setup
ttyS = serial.Serial(device, baudrate, width, parity, stopbits, 1, xon, rtc)
ttyS.setTimeout(0) # Non-blocking

##### PTYs setup
pts = []
for n in range(number_of_slave_terminals):
    master, slave = os.openpty()
    # Print slave names so others know where to connect
    print >>sys.stderr, 'MUX > fd: %d pty: %s' % (slave, os.ttyname(slave))
    pts.append(master)

##### Poller setup
poller = select.poll()
poller.register(ttyS.fd, select.POLLIN | select.POLLPRI)
for pt in pts:
    poller.register(pt, select.POLLIN | select.POLLPRI)

##### MAIN
while True:

events = poller.poll(500)

for fd, flag in events:

    # fd has input
    if flag & (select.POLLIN | select.POLLPRI):
        # Data on serial
        if fd == ttyS.fd:
            data = ttyS.read(80)
            for pt in pts:
                os.write(pt, data)

        # Data on other pty
        else:
            ttyS.write(os.read(fd, 80))

如果每个pty都已连接,这种方法非常有效。如果有一些未连接的pty,则最终其缓冲区会填满,并在写入时被阻塞。似乎我需要知道哪些从设备已连接或某种按需打开pty的方法。

我在这个问题上发现了一个巧妙的技巧,在该问题中,该人仅需要读取串行端口部分,因此我调整了我的脚本:

##### Serial port setup
ttyS = serial.Serial(device, baudrate, width, parity, stopbits, 1, xon, rtc)
ttyS.setTimeout(0) # Non-blocking

##### PTYs setup
pts = []
for n in range(number_of_slave_terminals):
    master, slave = os.openpty()

    # slaves
    print >>sys.stderr, 'MUX > fd: %d pty: %s' % (slave, os.ttyname(slave))
    os.close(slave) # POLLHUP trick

    # masters
    pts.append(master)

##### Poller setup
reader = select.poll()
writer = select.poll()

reader.register(ttyS, select.POLLIN | select.POLLPRI)
for pt in pts:
    reader.register(pt, select.POLLIN | select.POLLPRI)
    writer.register(pt, select.POLLIN | select.POLLPRI | select.POLLOUT)

def write_to_ptys(data):
    events = writer.poll(500)

    for fd, flag in events:

        # There is someone on the other side...
        if not (flag & select.POLLHUP):
            os.write(fd, data)

##### MAIN
while True:
    events = reader.poll(500)

    for fd, flag in events:

    if flag & (select.POLLIN | select.POLLPRI):
        # Data on serial
        if fd == ttyS.fd:
            write_to_tty(ttyS.read(80))
        # Data on other pty
        else:
            ttyS.write(os.read(fd, 80))

这个方法可以实现,但是会使用100%的CPU,因为读取器轮询被POLLHUP事件淹没了。

我想如果我使用TCP套接字而不是伪终端,我就可以得到我想要的结果。缺点是我必须修改所有已经使用终端的其他脚本来使用套接字(我知道我可以使用socat,但我只想要更简单的东西)。此外,还有所有的网络开销...

那么,有什么好的想法吗?

我不介意使用其他工具,只要设置简单即可。我也不介意使用其他语言,我只是最喜欢Python。


1
将串口访问多路复用的想法似乎不是一个好方法。我在嵌入式平台上做过类似的事情。与其让多个进程使用串口,不如编写一个仅读写端口并通过套接字为其他用户提供数据接口的单个进程。在所有客户端中处理套接字接口要容易得多,并消除了尝试多路复用访问端口的所有疯狂行为。 - TJD
可能更好的做法是只使用单个pty,但使用screendtach - twalberg
@TJD:这是一般的想法。只是我认为直接使用伪终端并绕过网络层会更有意义。此外,我们已经有很多测试脚本指向 '/dev/ttyS0' 或类似的东西。我认为让它们简单地指向 '/dev/pts/xyz' 会更容易些。 - Marcelo MD
@twalberg:有什么提示吗?昨天我花了几个小时,但无法找到让多个窗口指向我的串口的方法。 - Marcelo MD
在我写完之后,我意识到你的嵌入式系统可能没有“屏幕”,但如果有的话,你可以通过一个终端连接(如minicom或其他工具)在嵌入式系统上运行它,并且它允许你运行多个虚拟终端,你可以使用按键命令在它们之间切换(还可以创建/销毁虚拟屏幕和许多其他命令)。 - twalberg
我明白了...不幸的是,我的系统无法运行屏幕。我要么在其自己的用户界面上运行命令(无法访问shell),要么在启动过程中寻找错误。非常感谢 =) - Marcelo MD
1个回答

6
最终我写了一个简单的TCP服务器,就像我之前说过我不想要的那样...但它运行得非常好。它使用与问题中的代码相同的总体架构,但使用TCP套接字而不是伪终端。
如果有人想要使用它,我在这里发布了它。
调用它:
md:mux_serial> ./mux_server.py --device /dev/ttyS0 --baud 115200 --port 23200
MUX > Serial port: /dev/ttyS0 @ 115200
MUX > Server: localhost:23200

我正在使用另一个终端上的 socat 直接访问该端口...
md:~> socat -,raw,echo=0,escape=0x0f TCP4:localhost:23200

或者创建一个伪终端,以在需要这些功能的脚本内使用:

md:~> socat -d -d pty,raw,echo=0 TCP4:localhost:23200
2012/10/01 13:08:21 socat[3798] N PTY is /dev/pts/4
2012/10/01 13:08:21 socat[3798] N opening connection to AF=2 127.0.0.1:23200
2012/10/01 13:08:21 socat[3798] N successfully connected from local address AF=2 127.0.0.1:35138
2012/10/01 13:08:21 socat[3798] N starting data transfer loop with FDs [3,3] and [5,5]

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