适用于 Linux 的虚拟串行端口

Virtual Serial Port for Linux

提问人:JeffV 提问时间:9/9/2008 最后编辑:KaraJeffV 更新时间:9/8/2022 访问量:257958

问:

我需要在 Linux 上测试一个串口应用程序,但是,我的测试机器只有一个串口。

有没有办法将虚拟串行端口添加到 Linux 并通过 shell 或脚本模拟设备来测试我的应用程序?

注意:我无法重新映射端口,它在 ttys2 上硬编码,我需要在编写时测试应用程序。

Linux 虚拟串口端口

评论

1赞 JonathanPeel 11/11/2022
嗨,@JeffV。我知道这个问题现在已经过时了,但我很好奇你最终使用了什么。
2赞 JeffV 11/11/2022
哇,对不起,已经很久了,我什至不记得我在做什么。

答:

5赞 Howler 9/9/2008 #1

您可以使用 USB->RS232 适配器吗?我有几个,他们只是使用 FTDI 驱动程序。然后,您应该能够将 /dev/ttyUSB0(或创建的任何内容)重命名为 /dev/ttyS2 。

5赞 Adam Davis 9/10/2008 #2

我能想到三个选择:

实施 RFC 2217

RFC 2217 涵盖了 TCP/IP 标准的 com 端口,该标准允许一个系统上的客户端模拟本地程序的串行端口,同时透明地向另一个系统上的服务器发送和接收数据和控制信号,该系统实际上具有串行端口。下面是一个高级概述

你要做的是找到或实现一个客户端 com 端口驱动程序,该驱动程序将在您的 PC 上实现系统的客户端 - 看起来是一个真正的串行端口,但实际上将所有内容都发送到服务器。您也许可以从 Digi、Lantronix 等免费获得此驱动程序,以支持其真正的独立串行端口服务器。

然后,您将在另一个程序中本地实现连接的服务器端 - 允许客户端连接并根据需要发出数据和控制命令。

这可能不是一件小事,但RFC就在那里,你也许能够找到一个实现连接的一端或两端的开源项目。

修改linux串口驱动

或者,Linux 的串行端口驱动程序源随时可用。拿着它,去掉硬件控制件,让一个驱动程序运行两个 /dev/ttySx 端口,作为一个简单的环回。然后将您的真实程序连接到 ttyS2,将您的模拟器连接到另一个 ttySx。

在环回中使用两根 USB<-->串行电缆

但现在最简单的事情是什么?花 40 美元购买两个串行端口 USB 设备,将它们连接在一起(零调制解调器),实际上有两个真正的串行端口——一个用于您正在测试的程序,一个用于您的模拟器。

-亚当

评论

1赞 Maxthon Chan 10/21/2014
实际上,零调制解调器USB UART电缆对我来说似乎是一个非常优雅的解决方案,因为它支持本地测试(如果端口不足,请获取USB集线器)和远程调试。
0赞 Daniel Santos 3/25/2020
我还没有审查它的质量,但 ttynvt 通过 Linux FUSE 实现了 RFC 2217
86赞 apenwarr 9/17/2008 #3

为此,您可以使用 pty(“伪电传打字机”,其中串行端口是“真正的电传打字机”)。从一端打开,然后将程序附加到 ; 将像串行端口一样工作,但将通过 /dev/ptyp5 发送/接收它所做的一切。/dev/ptyp5/dev/ttyp5ttyp5

如果你真的需要它与一个名为 的文件通信,那么只需将旧的文件移开,并创建一个符号链接。/dev/ttys2/dev/ttys2ptyp5ttys2

当然,您可以使用 以外的一些数字。也许选择一个数字较高的终端以避免重复,因为您的所有登录终端也将使用 ptys。ptyp5

维基百科有更多关于ptys的信息:http://en.wikipedia.org/wiki/Pseudo_terminal

评论

10赞 Matthew Smith 10/27/2008
在 linux 上,您可以使用 openpty / forkpty 系统调用。请参见手册页
12赞 linjunhalida 1/20/2010
如何使用命令行工具创建虚拟串口对?
12赞 Dima Tisnek 4/3/2012
请注意,许多串行端口参数(例如波特率、奇偶校验、硬件流量控制、字符大小 (?))未在 PTY 中实现,因此不可能在存在串行传输错误的情况下测试您的应用程序。
10赞 Craig McQueen 8/21/2012
这很有帮助,但它描述了“旧式”BSD 伪终端。“新样式”UNIX 98 伪终端的操作略有不同 — 有关详细信息,请参见 pts 手册页
3赞 Matthew Smith 11/20/2013
@LaszloPapp我道歉,我一直在撒谎
10赞 12/1/2008 #4

你可能想看看 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支持。

7赞 Mauro Ciancio 2/26/2012 #5

使用前面答案中发布的链接,我使用虚拟串行端口在C++中编写了一个小示例。我把代码推送到了 GitHub: https://github.com/cymait/virtual-serial-port-example .

代码是不言自明的。首先,通过运行 ./main master 创建主进程,它将打印到设备正在使用的 stderr。之后,调用 ./main 从属设备,其中 device 是第一个命令中打印的设备。

就是这样。两个进程之间有一个双向链接。

使用此示例,您可以通过发送各种数据来测试应用程序,并查看它是否正常工作。

此外,您始终可以对设备进行符号链接,因此无需重新编译正在测试的应用程序。

评论

1赞 Mattis Asp 1/4/2018
while (read(fd, &inputbyte, 1) == 1) { ... } read 在代码中未定义。写入未定义。关闭未定义。
0赞 Yves Lhuillier 11/10/2020
@MattisAsp有道理,你至少应该包括 .另外,还有一堆不应该忽视的警告,可修复的东西,但总体上是我喜欢的简单代码。<unistd.h>
63赞 slonik 5/12/2012 #6

为此,请使用 socat:

例如:

socat PTY,link=/dev/ttyS10 PTY,link=/dev/ttyS11

评论

0赞 gbmhunter 1/8/2014
这对我来说效果很好,用 minicom 测试过!似乎一个终端的输入会回显到两个终端(因此它也会重新出现在输入终端上)。
1赞 cptHammer 6/9/2016
我没有相同的回声行为......Minicom 具有“本地回声”功能......但是当它被禁用时,它的工作方式与真正的串行端口完全相同。谢谢你的提示。
1赞 Raleigh L. 12/11/2021
如果没有特殊权限,这似乎不起作用: ''' 2021/12/10 16:08:47 socat[90013] E unlink(“/dev/ttyS10”): 权限被拒绝 '''
21赞 Peter Remmers 7/7/2012 #7

还有 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 自行管理引脚。

如果有人对我的代码版本感兴趣,请给我发消息,我会把它发给你。

评论

3赞 Craig McQueen 3/20/2013
我很想看看你的代码。你能把它贡献给 tty0tty 项目吗?但是,我更希望看到人们改进 Linux 内核中的伪终端代码。例如,添加硬件握手支持和 TIOCMIWAIT。
3赞 Craig McQueen 7/1/2013
“如果有人对我的代码版本感兴趣,请给我发消息,我会把它发给你。是的,我很感兴趣!你能在某个地方指出它吗,例如在 GitHub 上?
10赞 Peter Remmers 11/14/2013
我将驱动程序上传到: github.com/pitti98/nullmodem 对不起,回复了这么长时间。我在 stackoverflow 上不是很活跃,忽略了你的评论!
0赞 Peter Remmers 11/17/2013
不,我写它是因为我需要它,一旦它足够好,可以做我想做的事就停止了。现在它是公开的,我希望它对其他人有用,也许有人会从我离开的地方捡起它。
230赞 cantoni 11/2/2013 #8

补充@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”。

评论

0赞 gbmhunter 1/8/2014
这对我来说比 slonik 的答案效果更好,因为它会自动分配给虚拟 COM 端口文件,并且不会回声。
14赞 Patrick B. 8/10/2017
如果要在每个设备声明之后(在 echo=0 之后)使用可重现的文件名。因此,它可以用于自动化测试。(正如 Slonik 在他们的回答中所做的那样)link=/path/to/link
3赞 Penghe Geng 8/31/2018
要创建链接到实际串行端口的 pty: .socat -d -d pty,raw,echo=0 /dev/ttyUSB5,raw,echo=0
4赞 mrid 4/29/2020
我可以创建一个名称类似于 而不是 的串行端口吗?/dev/ttyS0/dev/pts/1
1赞 trampster 1/24/2022
我试过了这个,但我只能打开它一次(使用 c# SerialPort),即使我在尝试连接新实例之前处理它总是在打开时崩溃
6赞 Sadeesh Kalhara 11/29/2021 #9
$ socat -d -d pty,link=/tmp/vserial1,raw,echo=0 pty,link=/tmp/vserial2,raw,echo=0

将生成/tmp/vserial1/tmp/vserial2/dev/pts/*

资源

2赞 Dan2theR 9/8/2022 #10

结合所有其他非常有用的答案,我发现以下命令对于在不同类型的 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)测试它们的输入/输出。希望有人觉得它有用。