我有一个串行设备设置为环回(这意味着它将简单地回显它接收到的任何字符),并且我想测量有效的吞吐量速度。为此,我希望可以使用 time
,就像
time bash -c '...'
“...
”是我可以运行的一些命令一样。
现在,第一个问题是我想以 2000000 bps 的速度使用设备,所以我不能使用 ttylog 或 screen (它们似乎都达到 115200 bps仅有的)。但是,将 /dev/ttyUSB0
作为文件使用(使用文件重定向和 cat
)似乎工作正常:
# initialize serial port
stty 2000000 -ixon icanon </dev/ttyUSB0
# check settings
stty -a -F /dev/ttyUSB0
# in one terminal - read from serial port
while (true) do cat -A /dev/ttyUSB0 ; done
# in other terminal - write to serial port
echo "1234567890" > /dev/ttyUSB0
# back to first terminal, I now have:
# $ while (true) do cat -A /dev/ttyUSB0 ; done
# 1234567890$
# ...
现在,我想做类似的事情 - 我'我想将文件cat
传输到串行端口,并让串行端口读回 - 但从单个终端命令(这样我可以将它用作时间
的参数) 。
我认为我可以使用 Bash 进程替换,让“写入”和“读取”部分以“并行”方式进行 - 如果我用命名管道尝试它,它会起作用:
# mkfifo my.pipe # same as below:
$ mknod my.pipe p
$ comm <(echo -e "test\ntest\ntest\n" > my.pipe) <(cat my.pipe)
test
test
test
comm: file 2 is not in sorted order
在那里,我不是将 comm
用于任何其他目的,而不是(某种程度上)将两个进程合并为一个命令(我想,我也可以使用 echo
来代替)。
不幸的是,这个技巧似乎不适用于串行端口,因为当我尝试它时,我有时会得到:
$ comm <(echo "1234567890" > /dev/ttyUSB0) <(while (true) do cat -A /dev/ttyUSB0 ; done)
cat: /dev/ttyUSB0: Invalid argument
...,但是,通常我只是没有得到任何输出。这告诉我:要么无法控制哪个进程首先启动,所以 cat
可能会在端口准备好之前开始读取(但是,这在第一个示例中似乎不是问题多于);或者在 Linux/Bash 中,您无法同时读取和写入串行端口,因此当读取和写入似乎都发生在串行端口时,会出现“Invalid argument
”。同一时间。
所以我的问题是:
- 有没有办法只在 Bash 中执行类似的操作(
cat
将文件发送到配置为环回的串行端口;读回它并查看需要多长时间),而无需求助于编写专用的 C 程序?
- 如果我需要一个专用的 C 程序,网上有我可以使用的源代码示例吗?
非常感谢您的回复,
干杯!
编辑:我知道上面写的 while
循环不会退出;该命令行用于初步测试,我使用 Ctrl-C 中断它。 (原则上我可以用 timeout -9 0.1 bash -c 'while (true) do echo AA ; done' 之类的东西来中断它,但这会破坏 time 的目的,然后:) )
while
存在的原因是,暂时通过 cat
从设备读取数据会立即退出;有时,我已经设置了该设备,以便当发出cat
时,它实际上会阻塞并等待传入数据;但我到目前为止还无法弄清楚发生了什么(这就是为什么我正在寻找一种从命令行进行测试的方法的原因)。
如果我没有使用 while
,我想为了计时,我会使用类似的东西:
time bash -c 'comm <(echo "1234567890" > /dev/ ttyUSB0) <(cat -A /dev/ttyUSB0)'
...然而,为了使其正常工作,假设 cat -A /dev/ttyUSB0
首先启动并且块;然后 echo 写入串行端口(并退出);然后 cat -A
输出从串行端口读取的任何内容 - 然后退出。 (我不确定串行端口是否可以以这种方式运行,也不确定 cat
是否可以像这样任意阻止和退出)。
确切的方法并不重要;重要的是。如果可能的话,我只是想避免编写自己的 C 程序来进行此类测试 - 这就是为什么我的主要兴趣是是否可以使用基本的 Bash/ 运行这样的“全双工测试” Linux(即coreutils
); (如果没有,如果有现成的代码我可以用于类似的事情)。
EDIT2:也可能相关:
I have a serial device set up as loopback (meaning it will simply echo back any character it receives), and I'd like to measure effective throughput speed. For this, I hoped I could use time
, as in
time bash -c '...'
where '...
' would be some command I could run.
Now, the first problem is that I want to use the device at 2000000 bps, so I cannot use ttylog or screen (they both seem to go up to 115200 bps only). However, working with /dev/ttyUSB0
as a file (using file redirection and cat
) seems to work fine:
# initialize serial port
stty 2000000 -ixon icanon </dev/ttyUSB0
# check settings
stty -a -F /dev/ttyUSB0
# in one terminal - read from serial port
while (true) do cat -A /dev/ttyUSB0 ; done
# in other terminal - write to serial port
echo "1234567890" > /dev/ttyUSB0
# back to first terminal, I now have:
# $ while (true) do cat -A /dev/ttyUSB0 ; done
# 1234567890$
# ...
Now, I'd like to do something similar - I'd like to cat
a file to a serial port, and have the serial port read back - but from a single terminal command (so I could use it as argument to time
).
I thought that I could use a Bash process substitution, to have the "writing" and "reading" part go, sort of, in "parallel" - if I try it with named pipes, it works:
# mkfifo my.pipe # same as below:
$ mknod my.pipe p
$ comm <(echo -e "test\ntest\ntest\n" > my.pipe) <(cat my.pipe)
test
test
test
comm: file 2 is not in sorted order
Up there, I'm not using comm
for any other purpose, than to (sort of) merge the two processes into a single command (I guess, I could have just as well used echo
instead).
Unfortunately, that trick does not seem to work with a serial port, because when I try it, I sometimes get:
$ comm <(echo "1234567890" > /dev/ttyUSB0) <(while (true) do cat -A /dev/ttyUSB0 ; done)
cat: /dev/ttyUSB0: Invalid argument
..., however, usually I just get no output whatsoever. This tells me that: either there is no control of which process starts first, and so cat
may start reading before the port is ready (however, that doesn't seem to be a problem in the first example above); or in Linux/Bash, you cannot both read and write to a serial port at the same time, and so the "Invalid argument
" would occur in those moments when both read and write seem to happen at the same time.
So my questions are:
- Is there a way to do something like this (
cat
a file to a serial port configured as loopback; read it back and see how long it takes) only in Bash, without resorting to writing a dedicated C program?
- If I need a dedicated C program, any source examples out there on the net I could use?
Thanks a lot for any responses,
Cheers!
EDIT: I am aware that the while
loop written above does not exit; that command line was for preliminary testing, and I interrupt it using Ctrl-C. ( I could in principle interrupt it with something like timeout -9 0.1 bash -c 'while (true) do echo AA ; done'
, but that would defeat the purpose of time
, then :) )
The reason that while
is there, is that for the time being, reading via cat
from the device exits immediately; at times, I have set up the device, so that when cat
is issued, it in fact blocks and waits for incoming data; but I cannot as of yet figure what's going on (and partially that is why I'm looking for a way to test from the command line).
In case I didn't use the while
, I imagine for timing, I'd use something like:
time bash -c 'comm <(echo "1234567890" > /dev/ttyUSB0) <(cat -A /dev/ttyUSB0)'
... however for this to be working, sort of, assumes that cat -A /dev/ttyUSB0
starts first and blocks; then the echo
writes to the serial port (and exits); and then cat -A
outputs whatever it read from the serial port - and then exits. (And I'm not really sure neither if a serial port can behave this way at all, nor if cat
can be made to block and exit arbitrarily like that).
The exact method really doesn't matter; if at all possible, I'd just like to avoid coding my own C program to do this kind of testing - which is why my primary interest is if it is somehow possible to run such a "full-duplex test" using basic Bash/Linux (i.e. coreutils
); (and if not, if there is a ready-made code I can use for something like this).
EDIT2: Also possibly relevant:
发布评论
评论(2)
好吧,我设法使用
pthread
将writeread.c
放入线程版本中(代码在下面 - 我不认为serial.h 改变了很多;无论如何,它在线程版本中没有使用太多
)。我还将速度降低到 115200,现在我可以在下面的示例命令行会话中使用设备确认这些测量值:
嗯,测量值现在报告高达预期波特率的 99%,所以我猜这意味着该程序的分析方面应该有效。注意:
write
在单个块中执行(因为 PC 应该能够在必要时处理数据包的排序),>读取
以较小的块进行(可能表明设备不会等待整个块到达 - 相反,一旦接收到足够的块,它就会开始发送回较小的块)嗯,我想这就是我最初需要的;我还猜想可能无法通过进程替换来安排
cat
和echo
在此执行,我们称之为“线程”,方式:) (现在,我确实在以 2000000 波特率执行相同操作时遇到问题,但这表明设备编程存在问题)。干杯!
writeread.c - 线程版本
Well, I managed to put
writeread.c
in a threaded version usingpthread
(code is below - I don't thinkserial.h
changed much; it's not used that much in the threaded version anyways). I have also lowered the speed to 115200, and now I can confirm these measurements with the device, in the sample command line session below:Well, measurements now report up to 99% of the expected baud rate, so I guess that means that the profiling aspect of this program should work. Notice:
write
is executed in a single chunk (as the PC should be able to handle the sequencing to packets, if necessary),read
goes on in smaller chunks (probably indicating that the device doesn't wait for the entire chunk to arrive - instead it starts sending back smaller chunks as soon as it has received enough)Well, I guess this is what I needed originally; I also guess it is probably not possible to arrange
cat
andecho
via process substitution to execute in this, let's call it "threaded", manner :) (Now, I do have a problem with doing the same at 2000000 baud, but that indicates a problem with the programming of the device).Cheers!
writeread.c - threaded version
好吧,这就像是部分答案 - 尽管有关 bash 使用的问题仍然悬而未决。我尝试查看一些 C 代码解决方案 - 看起来,这也不是微不足道的! :)
首先,让我们看看什么可能对这种情况不起作用 - 下面是来自“写入和读取之间:串行端口。 - C":
上述代码的问题是它没有显式初始化串行端口以进行字符(“原始”)操作;因此,根据之前端口的设置方式,会话可能如下所示:
...换句话说,没有输入数据的回显。但是,如果串行端口设置正确,我们可以获得如下会话:
...(但即便如此,此
sertest
代码在输入超过 3 个字符的单词时会失败。)最后,通过一些在线挖掘,我设法找到了“(已解决)串行编程,读写问题”,其中提供了一个
writeread.cpp
示例。然而,对于这种逐字节“双工”情况,甚至这还不够 - 即“串行编程 HOWTO" 注释:“规范输入处理...是终端的正常处理模式...这意味着读取只会返回完整的数据输入行默认以 NL (ASCII LF) 结尾..." ;因此,我们必须通过ICANON
在代码中显式将串行端口设置为“非规范”(或“原始”)模式(换句话说,仅通过open
设置O_NONBLOCK
是不够足够的) - “3.2 如何从终端读取单个字符? - Unix 编程常见问题 - 3. 终端 I/O ”。完成后,调用 writeread 也将“正确”设置serport
示例(上面)的串行端口。因此,我将其中一些
writeread
代码更改回 C,添加了所需的初始化内容,以及时间测量、发送字符串或文件的可能性以及附加输出流(用于“管道”将串行数据读取到单独的文件)。代码如下:writeread.c
和serial.h
,使用它们,我可以执行类似以下 Bash 会话中的操作:嗯:
/dev/null
!此时,我认为速度减慢是因为在 writeread.c 中每个写入字节之后,我们等待读取中断清除标志,然后再继续读取串行缓冲区。可能,如果读取和写入是单独的线程,则读取和写入都可以尝试在单个
read
或write
调用中使用更大的字节块,因此带宽将是用过比较好?! (或者,也许中断处理程序在某种意义上确实像并行运行的“线程”一样 - 所以也许可以通过将所有与读取相关的函数移至中断处理程序来实现类似的效果?!)嗯,在这一点上,我非常愿意接受现有代码的建议/链接,例如 writeread.c,但是是多线程的:)当然,对于任何其他可能的 Linux 工具,或者可能的 Bash 方法(虽然看起来 Bash 无法发挥这种控制作用……)
干杯!
writeread.c:
serial.h:
Well, here is something like a partial answer - although the question about the use of bash is still open. I tried to look a little bit in some C code solutions - and that, it seems, isn't trivial either! :)
First, let's see what possibly doesn't work for this case - below is an example from "between write and read:serial port. - C":
The problem with the above code is that it doesn't explicitly initialize the serial port for character ("raw") operation; so depending on how the port was set previously, a session may look like this:
... in other words, there is no echoing of the input data. However, if the serial port is set up properly, we can get a session like:
... (but even then, this
sertest
code fails on input words greater than 3 characters.)Finally, through some online digging, I managed to find "(SOLVED) Serial Programming, Write-Read Issue", which offers a
writeread.cpp
example. However, for this byte-by-byte "duplex" case, not even that was enough - namely, "Serial Programming HOWTO" notes: "Canonical Input Processing ... is the normal processing mode for terminals ... which means that a read will only return a full line of input. A line is by default terminated by a NL (ASCII LF) ..." ; and thus we have to explicitly set the serial port to "non-canonical" (or "raw") mode in our code viaICANON
(in other words, just settingO_NONBLOCK
viaopen
is not enough) - an example for that is given at "3.2 How can I read single characters from the terminal? - Unix Programming Frequently Asked Questions - 3. Terminal I/O". Once that is done, callingwriteread
will "correctly" set the serial port for theserport
example (above), as well.So I changed some of that
writeread
code back to C, added the needed initialization stuff, as well as time measurement, possibility to send strings or files, and additional output stream (for 'piping' the read serial data to a separate file). The code is below aswriteread.c
andserial.h
, and with it, I can do something like in the following Bash session:Well:
/dev/null
!At this point, I'm thinking that the slowdown is because after each written byte in
writeread.c
, we wait for a flag to be cleared by the read interrupt, before we proceed to read the serial buffer. Possibly, if the reading and writing were separate threads, then both reading and writing could try to use bigger blocks of bytes in singleread
orwrite
calls, and so bandwidth would be used better ?! (Or, maybe the interrupt handler does act, in some sense, like a "thread" running in parallel - so maybe something similar could be achieved by moving all read related functions to the interrupt handler ?!)Ah well - at this point, I am very open to suggestions / links for existing code like
writeread.c
, but multithreaded :) And, of course, for any other possible Linux tools, or possibly Bash methods (although it seems Bash will not be able to exert this kind of control...)Cheers!
writeread.c:
serial.h: