串行端口环回/双工测试,在 Bash 或 C 中?(过程替换) [英] Serial port loopback/duplex test, in Bash or C? (process substitution)
问题描述
我有一个串行设备设置为环回(这意味着它会简单地回显它接收到的任何字符),我想测量有效的吞吐速度.为此,我希望我可以使用 time
,如
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.
现在,第一个问题是我想以 2000000 bps 使用设备,所以我不能使用 ttylog 或 screen (它们似乎都上升了仅到 115200 bps).但是,使用 /dev/ttyUSB0
作为文件(使用文件重定向和 cat
)似乎可以正常工作:
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$
# ...
现在,我想做一些类似的事情 - 我想 cat
一个文件到串行端口,并让串行端口读回 - 但是从单个终端命令(所以我可以将它用作 time
的参数).
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
).
我认为我可以使用 Bash 进程替换,让写入"和读取"部分以某种方式并行"运行 - 如果我尝试使用命名管道,它可以工作:
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
test
test
" > my.pipe) <(cat my.pipe)
test
test
test
comm: file 2 is not in sorted order
在那里,我没有将 comm
用于任何其他目的,而不是将两个进程(某种程度上)合并为一个命令(我想,我也可以使用 echo
代替).
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
...但是,通常我只是没有得到任何输出.这告诉我:要么无法控制首先启动哪个进程,因此 cat
可能会在端口准备好之前开始读取(但是,在第一个示例中这似乎不是问题多于);或者在 Linux/Bash 中,您不能同时读取和写入串行端口,因此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.
所以我的问题是:
- 有没有办法只在 Bash 中做这样的事情(
cat
一个文件到配置为环回的串行端口;读回来看看需要多长时间),而无需编写专用C程序? - 如果我需要一个专用的 C 程序,我可以使用网上的任何源示例吗?
- 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,
干杯!
我知道上面写的 while
循环不会退出;该命令行用于初步测试,我使用 Ctrl-C 中断它.(原则上我可以用 timeout -9 0.1 bash -c 'while (true) do echo AA ; done'
来打断它,但这会破坏 time 的目的
,然后 :) )
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 :) )
while
之所以存在,是因为暂时通过cat
从设备读取立即退出;有时,我已经设置了设备,这样当 cat
发出时,它实际上会阻塞并等待传入数据;但我目前还无法弄清楚发生了什么(部分原因是我正在寻找一种从命令行进行测试的方法).
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).
如果我没有使用 while
,我想对于时间安排,我会使用类似的东西:
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)'
...但是,为了使其正常工作,有点假设 cat -A/dev/ttyUSB0
首先启动并阻塞;然后 echo
写入串行端口(并退出);然后 cat -A
输出它从串行端口读取的任何内容 - 然后退出.(而且我不确定串行端口是否完全可以以这种方式运行,也不确定 cat
是否可以像那样任意阻止和退出).
... 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).
确切的方法真的不重要;如果可能的话,我只想避免编写自己的 C 程序来进行这种测试——这就是为什么我的主要兴趣是是否可以使用基本的 Bash/运行这样的全双工测试"Linux(即coreutils
);(如果没有,如果有现成的代码我可以用于类似的事情).
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).
也可能相关:
推荐答案
好吧,我设法使用 pthread
将 writeread.c
放入线程版本(代码在下面 - 我认为 serial.h
没有太大变化;无论如何,它在线程版本中并没有太多使用).我还将速度降低到 115200,现在我可以在下面的示例命令行会话中使用设备确认这些测量:
Well, I managed to put writeread.c
in a threaded version using pthread
(code is below - I don't think serial.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:
$ ./writeread /dev/ttyUSB0 115200 writeread.c 3>myout.txt
stdalt opened; Alternative file descriptor: 3
Opening port /dev/ttyUSB0;
Got speed 115200 (4098/0x1002);
Got file/string 'writeread.c'; opened as file (6131).
write_thread_function spawned
write: 6131
read: 18
read: 64
read: 110
read: 156
read: 202
...
read: 6066
read: 6089
read: 6123
read: 6131
+++DONE+++
Wrote: 6131 bytes; Read: 6131 bytes; Total: 12262 bytes.
Start: 1284462824 s 141104 us; End: 1284462824 s 682598 us; Delta: 0 s 541494 us.
115200 baud for 8N1 is 11520 Bps (bytes/sec).
Measured: write 11322.38 Bps (98.28%), read 11322.38 Bps (98.28%), total 22644.76 Bps.
$ diff writeread.c myout.txt
$
嗯,现在测量结果报告高达 99% 的预期波特率,所以我想这意味着该程序的分析方面应该可以工作.注意:
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
在单个块中执行(如果必要,PC 应该能够处理数据包的排序), - 当
read
以较小的块进行时(可能表明设备不会等待整个块到达 - 相反,它会在收到较小的块后立即开始发送回收到足够的)
- For this device, the
write
is executed in a single chunk (as the PC should be able to handle the sequencing to packets, if necessary), - while the
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)
嗯,我想这就是我最初需要的;我也猜想可能无法通过进程替换来安排 cat
和 echo
在此执行,我们称其为线程",方式:) (现在,我在 2000000 波特率下做同样的事情确实有问题,但这表明设备的编程有问题).
Well, I guess this is what I needed originally; I also guess it is probably not possible to arrange cat
and echo
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).
干杯!
writeread.c - 线程版本
/*
writeread.c - based on writeread.cpp
[SOLVED] Serial Programming, Write-Read Issue - http://www.linuxquestions.org/questions/programming-9/serial-programming-write-read-issue-822980/
build with: gcc -o writeread -lpthread -Wall -g writeread.c
*/
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/time.h>
#include <pthread.h>
#include "serial.h"
int serport_fd;
//POSIX Threads Programming - https://computing.llnl.gov/tutorials/pthreads/#PassingArguments
struct write_thread_data{
int fd;
char* comm; //string to send
int bytesToSend;
int writtenBytes;
};
void usage(char **argv)
{
fprintf(stdout, "Usage:
");
fprintf(stdout, "%s port baudrate file/string
", argv[0]);
fprintf(stdout, "Examples:
");
fprintf(stdout, "%s /dev/ttyUSB0 115200 /path/to/somefile.txt
", argv[0]);
fprintf(stdout, "%s /dev/ttyUSB0 115200 "some text test"
", argv[0]);
}
// POSIX threads explained - http://www.ibm.com/developerworks/library/l-posix1.html
// instead of writeport
void *write_thread_function(void *arg) {
int lastBytesWritten;
struct write_thread_data *my_data;
my_data = (struct write_thread_data *) arg;
fprintf(stdout, "write_thread_function spawned
");
my_data->writtenBytes = 0;
while(my_data->writtenBytes < my_data->bytesToSend)
{
lastBytesWritten = write( my_data->fd, my_data->comm + my_data->writtenBytes, my_data->bytesToSend - my_data->writtenBytes );
my_data->writtenBytes += lastBytesWritten;
if ( lastBytesWritten < 0 )
{
fprintf(stdout, "write failed!
");
return 0;
}
fprintf(stderr, " write: %d - %d
", lastBytesWritten, my_data->writtenBytes);
}
return NULL; //pthread_exit(NULL)
}
int main( int argc, char **argv )
{
if( argc != 4 ) {
usage(argv);
return 1;
}
char *serport;
char *serspeed;
speed_t serspeed_t;
char *serfstr;
int serf_fd; // if < 0, then serfstr is a string
int sentBytes;
int readChars;
int recdBytes, totlBytes;
char* sResp;
char* sRespTotal;
struct timeval timeStart, timeEnd, timeDelta;
float deltasec, expectBps, measReadBps, measWriteBps;
struct write_thread_data wrdata;
pthread_t myWriteThread;
/* Re: connecting alternative output stream to terminal -
* http://coding.derkeiler.com/Archive/C_CPP/comp.lang.c/2009-01/msg01616.html
* send read output to file descriptor 3 if open,
* else just send to stdout
*/
FILE *stdalt;
if(dup2(3, 3) == -1) {
fprintf(stdout, "stdalt not opened; ");
stdalt = fopen("/dev/tty", "w");
} else {
fprintf(stdout, "stdalt opened; ");
stdalt = fdopen(3, "w");
}
fprintf(stdout, "Alternative file descriptor: %d
", fileno(stdalt));
// Get the PORT name
serport = argv[1];
fprintf(stdout, "Opening port %s;
", serport);
// Get the baudrate
serspeed = argv[2];
serspeed_t = string_to_baud(serspeed);
fprintf(stdout, "Got speed %s (%d/0x%x);
", serspeed, serspeed_t, serspeed_t);
//Get file or command;
serfstr = argv[3];
serf_fd = open( serfstr, O_RDONLY );
fprintf(stdout, "Got file/string '%s'; ", serfstr);
if (serf_fd < 0) {
wrdata.bytesToSend = strlen(serfstr);
wrdata.comm = serfstr; //pointer already defined
fprintf(stdout, "interpreting as string (%d).
", wrdata.bytesToSend);
} else {
struct stat st;
stat(serfstr, &st);
wrdata.bytesToSend = st.st_size;
wrdata.comm = (char *)calloc(wrdata.bytesToSend, sizeof(char));
read(serf_fd, wrdata.comm, wrdata.bytesToSend);
fprintf(stdout, "opened as file (%d).
", wrdata.bytesToSend);
}
sResp = (char *)calloc(wrdata.bytesToSend, sizeof(char));
sRespTotal = (char *)calloc(wrdata.bytesToSend, sizeof(char));
// Open and Initialise port
serport_fd = open( serport, O_RDWR | O_NOCTTY | O_NONBLOCK );
if ( serport_fd < 0 ) { perror(serport); return 1; }
initport( serport_fd, serspeed_t );
wrdata.fd = serport_fd;
sentBytes = 0; recdBytes = 0;
gettimeofday( &timeStart, NULL );
// start the thread for writing..
if ( pthread_create( &myWriteThread, NULL, write_thread_function, (void *) &wrdata) ) {
printf("error creating thread.");
abort();
}
// run read loop
while ( recdBytes < wrdata.bytesToSend )
{
while ( wait_flag == TRUE );
if ( (readChars = read( serport_fd, sResp, wrdata.bytesToSend)) >= 0 )
{
//~ fprintf(stdout, "InVAL: (%d) %s
", readChars, sResp);
// binary safe - add sResp chunk to sRespTotal
memmove(sRespTotal+recdBytes, sResp+0, readChars*sizeof(char));
/* // text safe, but not binary:
sResp[readChars] = ' ';
fprintf(stdalt, "%s", sResp);
*/
recdBytes += readChars;
} else {
if ( errno == EAGAIN )
{
fprintf(stdout, "SERIAL EAGAIN ERROR
");
return 0;
}
else
{
fprintf(stdout, "SERIAL read error: %d = %s
", errno , strerror(errno));
return 0;
}
}
fprintf(stderr, " read: %d
", recdBytes);
wait_flag = TRUE; // was ==
//~ usleep(50000);
}
if ( pthread_join ( myWriteThread, NULL ) ) {
printf("error joining thread.");
abort();
}
gettimeofday( &timeEnd, NULL );
// binary safe - dump sRespTotal to stdalt
fwrite(sRespTotal, sizeof(char), recdBytes, stdalt);
// Close the open port
close( serport_fd );
if (!(serf_fd < 0)) {
close( serf_fd );
free(wrdata.comm);
}
free(sResp);
free(sRespTotal);
fprintf(stdout, "
+++DONE+++
");
sentBytes = wrdata.writtenBytes;
totlBytes = sentBytes + recdBytes;
timeval_subtract(&timeDelta, &timeEnd, &timeStart);
deltasec = timeDelta.tv_sec+timeDelta.tv_usec*1e-6;
expectBps = atoi(serspeed)/10.0f;
measWriteBps = sentBytes/deltasec;
measReadBps = recdBytes/deltasec;
fprintf(stdout, "Wrote: %d bytes; Read: %d bytes; Total: %d bytes.
", sentBytes, recdBytes, totlBytes);
fprintf(stdout, "Start: %ld s %ld us; End: %ld s %ld us; Delta: %ld s %ld us.
", timeStart.tv_sec, timeStart.tv_usec, timeEnd.tv_sec, timeEnd.tv_usec, timeDelta.tv_sec, timeDelta.tv_usec);
fprintf(stdout, "%s baud for 8N1 is %d Bps (bytes/sec).
", serspeed, (int)expectBps);
fprintf(stdout, "Measured: write %.02f Bps (%.02f%%), read %.02f Bps (%.02f%%), total %.02f Bps.
", measWriteBps, (measWriteBps/expectBps)*100, measReadBps, (measReadBps/expectBps)*100, totlBytes/deltasec);
return 0;
}
这篇关于串行端口环回/双工测试,在 Bash 或 C 中?(过程替换)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!