在Linux开发中,我们常需要调试串口通信程序,但有时缺少真实的串口硬件(如USB转串口模块、单片机)。此时,通过软件模拟虚拟串口是高效的解决方案------借助Linux自带的伪终端(Pseudoterminal,
PTY)机制,可创建一对"相互连接"的虚拟串口,让两个程序像操作真实串口一样进行数据交互。本文将详细讲解如何用
socat工具快速创建虚拟串口,并通过实际示例验证两个程序的通信效果。
一、核心原理:虚拟串口的本质是"伪终端对"
Linux中的虚拟串口并非真实的硬件接口,而是基于"伪终端对"实现的逻辑通道。一个伪终端对包含两个端点:主设备(master)和从设备(slave),数据会在两个端点间双向同步传输------从主设备发送的数据会被从设备接收,反之亦然。
我们将这两个端点模拟为"串口设备文件"(如 /dev/ttyS0、/dev/ttyS1),让两个程序分别绑定这两个设备文件,即可实现"程序A→虚拟串口→程序B"的通信链路,完全模拟真实串口的交互逻辑。
二、准备工具:安装socat
socat 是Linux下功能强大的"数据转发工具",支持多种协议和设备的连接,其中就包括伪终端对的创建,是模拟虚拟串口的首选工具。
安装命令:
-
Debian/Ubuntu :
sudo apt update && sudo apt install socat -y -
CentOS/Fedora :
sudo dnf install socat -y -
验证安装:
socat -V若输出版本信息(如 socat 1.7.4.4),说明安装成功。
三、步骤1:创建虚拟串口对
通过 socat 命令可直接创建一对相互绑定的虚拟串口,命令格式如下:
bash
socat -d -d pty,raw,echo=0,link=/dev/ttyV0 pty,raw,echo=0,link=/dev/ttyV1
命令参数解释:
-
-d -d:输出调试信息(两次 -d 表示详细程度,可省略,便于排查问题); -
pty:指定创建伪终端设备; -
raw:设置串口为"原始模式",禁用缓冲区和特殊字符处理(模拟真实串口的无缓冲传输); -
echo=0:关闭回显功能(避免发送的数据被自身回显,干扰通信); -
link=/dev/ttyV0:为伪终端设备创建软链接(简化设备名,原伪终端名较长,如 /dev/pts/10),ttyV0和ttyV1是自定义的虚拟串口名,可修改(建议用 ttyV 前缀,避免与真实串口 ttyS 冲突)。
执行命令后,终端会输出类似以下的调试信息,说明虚拟串口对创建成功:
text
2026/01/14 15:30:00 socat[12345] N PTY is /dev/pts/10
2026/01/14 15:30:00 socat[12345] N PTY is /dev/pts/11
2026/01/14 15:30:00 socat[12345] N starting data transfer loop with FDs [5,5] and [7,7]
此时,/dev/ttyV0 和 /dev/ttyV1 就是一对相互通信的虚拟串口,且软链接已自动创建(可通过 ls -l /dev/ttyV* 查看链接关系)。
⚠️ 注意:创建虚拟串口的终端窗口需保持打开状态(socat 进程运行中),若关闭窗口,虚拟串口会自动消失。
四、步骤2:验证虚拟串口通信(基础测试)
我们先用两个终端窗口模拟"两个程序",通过 cat 和 echo 命令测试虚拟串口的双向通信功能。
测试1:单向通信(终端1→终端2)
-
打开 终端1 (接收端):绑定 /dev/ttyV0,监听数据:
cat /dev/ttyV0 -
打开 终端2 (发送端):绑定 /dev/ttyV1,发送数据:
echo "Hello, Virtual Serial Port!" > /dev/ttyV1 -
观察结果:终端1会立即显示终端2发送的字符串,说明单向通信正常。
测试2:双向通信(终端1与终端2互发)
-
终端1(绑定 /dev/ttyV0):用
read命令接收数据,同时可发送数据:
while read -r line; do echo "终端1收到:$line"; done < /dev/ttyV0 -
终端2(绑定 /dev/ttyV1):同样用循环接收并发送数据:
while read -r line; do echo "终端2收到:$line"; done < /dev/ttyV1 -
测试互发:
- 在终端1输入
echo "Hi from V0" > /dev/ttyV0,终端2会显示"终端2收到:Hi from V0"; - 在终端2输入
echo "Hi from V1" > /dev/ttyV1,终端1会显示"终端1收到:Hi from V1"。
五、步骤3:两个程序通过虚拟串口通信(实战示例)
下面用 Python 编写两个简单的串口程序(发送端+接收端),模拟真实应用场景下的程序间通信。
前提:安装Python串口库
两个程序需用到 pyserial 库,安装命令:
pip install pyserial
程序1:虚拟串口发送端(send.py,绑定 /dev/ttyV1)
python
import serial
import time
# 配置虚拟串口参数(波特率、超时时间等,需与接收端一致)
ser = serial.Serial(
port='/dev/ttyV1', # 绑定虚拟串口V1
baudrate=9600, # 波特率(虚拟串口可任意设置,真实串口需匹配)
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1 # 读取超时时间(秒)
)
# 循环发送数据
try:
print("发送端启动,开始发送数据...")
count = 0
while True:
data = f"Test data {count}: Hello Virtual Serial!\n"
ser.write(data.encode('utf-8')) # 编码为字节流发送
print(f"已发送:{data.strip()}")
count += 1
time.sleep(2) # 每2秒发送一次
except KeyboardInterrupt:
print("\n发送端退出")
ser.close() # 关闭串口
程序2:虚拟串口接收端(recv.py,绑定 /dev/ttyV0)
python
import serial
# 配置虚拟串口参数(与发送端完全一致)
ser = serial.Serial(
port='/dev/ttyV0',
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1
)
# 循环接收数据
try:
print("接收端启动,等待数据...")
while True:
if ser.in_waiting > 0: # 检测是否有数据可读
data = ser.readline().decode('utf-8').strip() # 读取一行数据并解码
print(f"接收端收到:{data}")
except KeyboardInterrupt:
print("\n接收端退出")
ser.close()
运行程序并验证
-
确保之前创建虚拟串口的终端窗口仍打开(socat 进程运行中);
-
打开 终端3 ,运行接收端程序:
python recv.py -
打开 终端4 ,运行发送端程序:
python send.py -
观察结果:接收端会每隔2秒收到发送端发送的测试数据,类似以下输出:
接收端启动,等待数据...
接收端收到:Test data 0: Hello Virtual Serial!
接收端收到:Test data 1: Hello Virtual Serial!
接收端收到:Test data 2: Hello Virtual Serial!
六、常见问题与注意事项
1. 权限问题:Permission denied
现象:运行程序或访问 /dev/ttyV0 时提示"权限拒绝"。
解决:将当前用户加入 dialout 用户组(Linux串口设备默认属于该组),命令:
sudo usermod -aG dialout $USER
执行后注销当前用户并重新登录,权限即可生效。
2. 虚拟串口消失
原因:创建虚拟串口的 socat 进程被终止(如关闭终端窗口)。
解决:重新执行 socat 创建命令,确保该终端窗口持续打开。
3. 程序通信失败
排查方向:
- 两个程序的串口参数(波特率、数据位、停止位、校验位)是否完全一致;
- 程序绑定的虚拟串口是否正确(需分别绑定 ttyV0 和 ttyV1,不可重复);
- 虚拟串口是否正常创建(用
ls -l /dev/ttyV*检查软链接)。
4. 自定义虚拟串口名
若不想用 ttyV0/ttyV1,可修改 socat 命令中的 link 参数,如 link=/dev/ttyS10、link=/dev/mySerial0,但需避免与系统真实串口名冲突(真实串口通常为 ttyS0-ttyS3)。