提问人:JeffV 提问时间:9/9/2008 最后编辑:KaraJeffV 更新时间:9/8/2022 访问量:257958
适用于 Linux 的虚拟串行端口
Virtual Serial Port for Linux
问:
我需要在 Linux 上测试一个串口应用程序,但是,我的测试机器只有一个串口。
有没有办法将虚拟串行端口添加到 Linux 并通过 shell 或脚本模拟设备来测试我的应用程序?
注意:我无法重新映射端口,它在 ttys2 上硬编码,我需要在编写时测试应用程序。
答:
您可以使用 USB->RS232 适配器吗?我有几个,他们只是使用 FTDI 驱动程序。然后,您应该能够将 /dev/ttyUSB0(或创建的任何内容)重命名为 /dev/ttyS2 。
我能想到三个选择:
实施 RFC 2217
RFC 2217 涵盖了 TCP/IP 标准的 com 端口,该标准允许一个系统上的客户端模拟本地程序的串行端口,同时透明地向另一个系统上的服务器发送和接收数据和控制信号,该系统实际上具有串行端口。下面是一个高级概述。
你要做的是找到或实现一个客户端 com 端口驱动程序,该驱动程序将在您的 PC 上实现系统的客户端 - 看起来是一个真正的串行端口,但实际上将所有内容都发送到服务器。您也许可以从 Digi、Lantronix 等免费获得此驱动程序,以支持其真正的独立串行端口服务器。
然后,您将在另一个程序中本地实现连接的服务器端 - 允许客户端连接并根据需要发出数据和控制命令。
这可能不是一件小事,但RFC就在那里,你也许能够找到一个实现连接的一端或两端的开源项目。
修改linux串口驱动
或者,Linux 的串行端口驱动程序源随时可用。拿着它,去掉硬件控制件,让一个驱动程序运行两个 /dev/ttySx 端口,作为一个简单的环回。然后将您的真实程序连接到 ttyS2,将您的模拟器连接到另一个 ttySx。
在环回中使用两根 USB<-->串行电缆
但现在最简单的事情是什么?花 40 美元购买两个串行端口 USB 设备,将它们连接在一起(零调制解调器),实际上有两个真正的串行端口——一个用于您正在测试的程序,一个用于您的模拟器。
-亚当
评论
为此,您可以使用 pty(“伪电传打字机”,其中串行端口是“真正的电传打字机”)。从一端打开,然后将程序附加到 ; 将像串行端口一样工作,但将通过 /dev/ptyp5 发送/接收它所做的一切。/dev/ptyp5
/dev/ttyp5
ttyp5
如果你真的需要它与一个名为 的文件通信,那么只需将旧的文件移开,并创建一个符号链接。/dev/ttys2
/dev/ttys2
ptyp5
ttys2
当然,您可以使用 以外的一些数字。也许选择一个数字较高的终端以避免重复,因为您的所有登录终端也将使用 ptys。ptyp5
维基百科有更多关于ptys的信息:http://en.wikipedia.org/wiki/Pseudo_terminal
评论
pts
手册页。
你可能想看看 Tibbo VSPDL,它使用内核驱动程序创建一个 linux 虚拟串行端口——它看起来很新,现在可以下载(测试版)。目前不确定许可证,或者他们是否希望仅在将来将其商业化。
还有其他商业替代方案,例如 http://www.ttyredirector.com/。
在开源中,Remserial (GPL) 也可以使用 Unix PTY 做你想做的事。它以“原始形式”将串行数据传输到网络套接字;在创建端口时,必须对终端参数进行类似 STTY 的设置,似乎不支持像 RFC 2217 中所述的稍后更改它们。您应该能够运行两个 remserial 实例来创建像 com0com 这样的虚拟零调制解调器,但您需要提前设置端口速度等。
Socat(也是 GPL)就像是 Remserial 的扩展变体,具有更多选项,包括用于将 PTY 重定向到其他东西的“PTY”方法,这可能是 Socat 的另一个实例。对于 Unit tets,socat 可能比 remserial 更好,因为您可以直接将 cat 文件放入 PTY。请参阅手册页上的 PTY 示例。“contrib”下存在一个补丁,用于为协商串行线路设置提供RFC2217支持。
使用前面答案中发布的链接,我使用虚拟串行端口在C++中编写了一个小示例。我把代码推送到了 GitHub: https://github.com/cymait/virtual-serial-port-example .
代码是不言自明的。首先,通过运行 ./main master 创建主进程,它将打印到设备正在使用的 stderr。之后,调用 ./main 从属设备,其中 device 是第一个命令中打印的设备。
就是这样。两个进程之间有一个双向链接。
使用此示例,您可以通过发送各种数据来测试应用程序,并查看它是否正常工作。
此外,您始终可以对设备进行符号链接,因此无需重新编译正在测试的应用程序。
评论
<unistd.h>
为此,请使用 socat:
例如:
socat PTY,link=/dev/ttyS10 PTY,link=/dev/ttyS11
评论
还有 tty0tty http://sourceforge.net/projects/tty0tty/ 它是一个真正的 linux 零调制解调器模拟器。
它是一个简单的内核模块 - 一个小的源文件。我不知道为什么它只在 sourceforge 上得到了大拇指,但它对我来说效果很好。最好的一点是,它还模拟了硬件引脚(RTC/CTS、DSR/DTR)。它甚至实现了 TIOCMGET/TIOCMSET 和 TIOCMIWAIT iotcl 命令!
在最近的内核上,您可能会遇到编译错误。这很容易解决。只需在 module/tty0tty.c 源代码的顶部插入几行(在包含之后):
#ifndef init_MUTEX
#define init_MUTEX(x) sema_init((x),1)
#endif
加载模块时,它会创建 4 对串行端口。设备是 /dev/tnt0 到 /dev/tnt7,其中 tnt0 连接到 tnt1,tnt2 连接到 tnt3,依此类推。 您可能需要修复文件权限才能使用设备。
编辑:
我想我的热情有点快。虽然驱动程序看起来很有前途,但它似乎不稳定。我不确定,但我认为它使我在家工作的办公室的一台机器崩溃了。直到周一回到办公室,我才能检查。
第二件事是TIOCMIWAIT不起作用。该代码似乎是从一些“微小的 tty”示例代码中复制而来的。TIOCMIWAIT 的处理似乎已经到位,但它永远不会唤醒,因为缺少对 wake_up_interruptible() 的相应调用。
编辑:
办公室的车祸确实是司机的错。缺少初始化,完全未经测试的 TIOCMIWAIT 代码导致机器崩溃。
我昨天和今天花了时间重写驱动程序。有很多问题,但现在它对我来说效果很好。驱动程序管理的硬件流控制仍然缺少代码,但我不需要它,因为我将使用用户模式代码中的 TIOCMGET/TIOCMSET/TIOCMIWAIT 自行管理引脚。
如果有人对我的代码版本感兴趣,请给我发消息,我会把它发给你。
评论
补充@slonik的答案。
您可以测试 socat 以创建虚拟串行端口,执行以下过程(在 Ubuntu 12.04 上测试):
打开一个终端(我们称之为终端 0)并执行它:
socat -d -d pty,raw,echo=0 pty,raw,echo=0
上面的代码返回:
2013/11/01 13:47:27 socat[2506] N PTY is /dev/pts/2
2013/11/01 13:47:27 socat[2506] N PTY is /dev/pts/3
2013/11/01 13:47:27 socat[2506] N starting data transfer loop with FDs [3,3] and [5,5]
打开另一个终端并写入(终端 1):
cat < /dev/pts/2
此命令的端口名称可以根据 PC 进行更改。这取决于之前的输出。
2013/11/01 13:47:27 socat[2506] N PTY is /dev/pts/**2**
2013/11/01 13:47:27 socat[2506] N PTY is /dev/pts/**3**
2013/11/01 13:47:27 socat[2506] N starting data transfer loop with FDs
您应该使用突出显示区域的可用数字。
打开另一个终端并写入(终端 2):
echo "Test" > /dev/pts/3
现在回到 1 号航站楼,你会看到字符串“Test”。
评论
link=/path/to/link
socat -d -d pty,raw,echo=0 /dev/ttyUSB5,raw,echo=0
/dev/ttyS0
/dev/pts/1
$ socat -d -d pty,link=/tmp/vserial1,raw,echo=0 pty,link=/tmp/vserial2,raw,echo=0
将生成/tmp/vserial1
/tmp/vserial2
/dev/pts/*
结合所有其他非常有用的答案,我发现以下命令对于在不同类型的 Linux 发行版上进行测试非常有用,在这些发行版中,不能保证您每次都会获得相同的 /dev/pts/# 和/或您需要一次测试多个 psuedo 串行设备和连接。
parallel 'i="{1}"; socat -d -d pty,raw,echo=0,link=$HOME/pty{1} pty,raw,echo=0,link=$HOME/pty$(($i+1))' ::: $(seq 0 2 3;)
分解一下:
parallel
对提供给它的每个参数运行相同的命令。
因此,例如,如果我们使用它给我们的标志运行它:--dryrun
i="0"; socat -d -d pty,raw,echo=0,link=$HOME/pty0 pty,raw,echo=0,link=$HOME/pty$(($i+1))
i="2"; socat -d -d pty,raw,echo=0,link=$HOME/pty2 pty,raw,echo=0,link=$HOME/pty$(($i+1))
这是由于在最后,其中
x = 开始 #,y = 递增,z = 结束 #(或您需要生成的设备 #)$(seq x y z;)
parallel 'i="{1}"; echo "make psuedo_devices {1} $(($i+1))"' ::: $(seq 0 2 3;)
输出:
make psuedo_devices 0 1
make psuedo_devices 2 3
将所有这些收集在一起,上面的最后一个命令将适当的 psuedo 设备符号链接到一起,无论 /dev/pts/ 中的内容如何,都可以通过标志提供给 socat 的任何目录。link
pstree -c -a $PROC_ID
给:
perl /usr/bin/parallel i="{1}"; socat -d -d pty,raw,echo=0,link=$HOME/pty{1} pty,raw,echo=0,link=$HOME/pty$(($i+1)) ::: 0 2
├─bash -c i="0"; socat -d -d pty,raw,echo=0,link=$HOME/pty0 pty,raw,echo=0,link=$HOME/pty$(($i+1))
│ └─socat -d -d pty,raw,echo=0,link=/home/user/pty0 pty,raw,echo=0,link=/home/user/pty1
└─bash -c i="2"; socat -d -d pty,raw,echo=0,link=$HOME/pty2 pty,raw,echo=0,link=$HOME/pty$(($i+1))
└─socat -d -d pty,raw,echo=0,link=/home/user/pty2 pty,raw,echo=0,link=/home/user/pty3
ls -l $HOME/pty* 产量:
lrwxrwxrwx 1 user user 10 Sep 7 11:46 /home/user/pty0 -> /dev/pts/4
lrwxrwxrwx 1 user user 10 Sep 7 11:46 /home/user/pty1 -> /dev/pts/6
lrwxrwxrwx 1 user user 10 Sep 7 11:46 /home/user/pty2 -> /dev/pts/7
lrwxrwxrwx 1 user user 10 Sep 7 11:46 /home/user/pty3 -> /dev/pts/8
这都是因为我尝试在一个平台上运行测试,在这个平台上,我需要生成大量的马赫串行连接,并通过容器化(Docker)测试它们的输入/输出。希望有人觉得它有用。
评论