pty多路复用器

Mar*_* MD 6 python linux serial-port pty

我正在尝试多路访问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))
Run Code Online (Sandbox Code Playgroud)

如果连接每个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))
Run Code Online (Sandbox Code Playgroud)

哪个有效,但使用100%的CPU,因为读者轮询充满了POLLHUP事件.

我想如果我使用TCP套接字而不是伪终端,我可以得到我想要的东西.缺点是,我将不得不修改已经与终端都能使用套接字(我知道我可以使用socat,我只是想简单的东西)所有其它脚本.此外,还有网络开销......

那么,有什么想法吗?

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

Mar*_* MD 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
Run Code Online (Sandbox Code Playgroud)

我正在socat另一个终端上使用直接访问端口...

md:~> socat -,raw,echo=0,escape=0x0f TCP4:localhost:23200
Run Code Online (Sandbox Code Playgroud)

...或者创建一个伪终端以在需要这些脚本的脚本中使用:

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]
Run Code Online (Sandbox Code Playgroud)