如何在 Linux 上使用管道或 netcat 等串行端口?

如何在 Linux 上使用管道或 netcat 等串行端口?

我有两台计算机,有两个串行端口:ttyS0 和 ttyUSB0。 (实际上他们在同一台计算机上,但这只是为了测试)。这些端口通过零调制解调器电缆连接。我希望能够简单地将字节发送到一端并从另一端发出,反之亦然。为什么以下不起作用?:

# set both serial ports to 9600 8n1
# `-parenb` means no parity,
# `-cstopb` means 1 stop bit
# cs8 means 8 bits at a time 
stty -F /dev/ttyUSB0 cs8 -cstopb -parenb 9600
stty -F /dev/ttyS0 cs8 -cstopb -parenb 9600

# in one terminal:
echo "asdf" > /dev/ttyUSB0

# in another terminal, this hangs and does nothing
cat < /dev/ttyS0

我可以很容易地用 and 管道做类似的事情netcat(如下),所以我觉得像上面这样的事情也应该是可能的。

mkfifo mypipe

# in one terminal
cat < mypipe

# in another. works as expected
echo "asdf" > mypipe

答案1

为什么下面的不起作用?

# in one terminal:
echo "asdf" > /dev/ttyUSB0

# in another terminal, this hangs and does nothing
cat < /dev/ttyS0

因为,一般来说,串行端口不缓冲数据。如果没有客户端应用程序接收到达串行端口的字节,它们将被简单地丢弃。

作为实验,尝试在接收计算机上启动minicom或或另一个串行终端程序,然后在传输计算机上再次运行该命令。假设波特率和帧一致,您应该看到“asdf”出现在目的地。cuecho

答案2

我希望能够简单地将字节发送到一端并从另一端发出,反之亦然。为什么下面的不起作用?

我认为这是因为你只需要切换顺序:启动监听器第一的然后发送数据(就像您在自己的管道示例中所做的那样 - 您开始监听第一的):

# Absolutely first, configure each port using `stty`, as you already
# do:

# set both serial ports to 9600 8n1
# `-parenb` means no parity,
# `-cstopb` means 1 stop bit
# cs8 means 8 bits at a time 
stty -F /dev/ttyUSB0 cs8 -cstopb -parenb 9600
stty -F /dev/ttyS0 cs8 -cstopb -parenb 9600

# THEN, do in this order

# first, in the receiving terminal, start listening
cat < /dev/ttyS0

# then, in a separate terminal for sending, send the data
echo "asdf" > /dev/ttyUSB0

我非常有信心我的答案是程序上正确,意思是:如果你盲目地遵循它,它就会起作用。

但是,问题仍然存在:为什么?为什么你必须先开始听? Linux 内核没有为你缓冲吗?如果是这样,数据难道不应该放在缓冲区中以供读取吗?显然它不是,否则先写后读也可以。但是,事实并非如此。

我在这里猜测,但我认为这里的答案是,当驱动程序接收数据时,它首先检查接收伪文件是否处于/dev/ttyUSB0打开状态,由另一个进程保持打开状态。如果不是,驱动程序丢弃数据,因为没有人接收它。如果文件open,它允许打开文件的进程读取缓冲的数据。

通过串行端口发送和通过进程间通信 (IPC) 管道发送之间的主要区别

这与您使用 制作管道的管道示例不同mkfifo。我刚刚检查过,在管道示例中,您可以先在一个终端中开始监听或者首先发送到另一个终端的管道。没关系。它在两种情况下都有效。

如果您先写入,则写入会阻塞并等待,直到进程从管道中读取数据。如果您先读取,则读取会阻塞并等待,直到进程写入管道。 Linux 的 C 函数文档证实了这一点mkfifo(),bashmkfifo几乎肯定是基于该文档的。man 3 mkfifo(强调):

一旦以这种方式创建了 FIFO 特殊文件,任何进程都可以打开它进行读取或写入,就像普通文件一样。 但是,它必须两端开放 同时地在您可以继续进行任何输入或输出之前 对其进行操作。 打开 FIFO 进行读取通常会阻塞 直到其他进程打开同一个 FIFO 进行写入,反之亦然。

然而,这与串行端口的行为不同,可能是因为每一端的串行端口都应该由不同的机器,与 FIFO 管道不同,其读写端是需要控制的来自同一台机器。在后一种情况下,内核可以轻松跟踪一个进程尝试读取而另一个进程尝试写入的时间,因为它控制管道的两端……因此,它允许任一进程阻塞。

然而,在串行端口的情况下,内核不知道另一个设备是否正在侦听或要发送,因此它不会阻塞发送。需要明确的是:写入管道时,写入会被阻止,直到出现侦听器为止。但是,当写入串行端口时,计算机无法检查侦听器是否存在, 所以永远不会阻塞!它只是发送。如果没有侦听器存在,则不会阻塞,而是在无人侦听时通过串行发送的数据会丢失。同样,这与管道不同,我的实验和上面的文档都证实了尝试通过管道发送数据的进程被阻塞,直到存在侦听器来读取该数据。

参考文献和“另见”:

  1. 当我写这篇文章时,我正在积极学习,记录我的各种发现eRCAGuy_dotfiles如果您有兴趣,请在此处回购:串行终端_README.md
  2. 我的演示代码:eRCaGuy_hello_world/bash/ipc_pipe_fifo.sh
  3. man 3 mkfifo:https://man7.org/linux/man-pages/man3/mkfifo.3.html

相关内容