我正在使用SocketCAN在嵌入式设备(SOC / ARM核心/ Linux)上测试CAN接口,我希望使用高效的代码,尽可能快地发送数据进行测试。
我可以打开can设备("can0")作为BSD套接字,并发送带有“写”的帧。这一切都很好。
我的桌面显然可以比can传输速率更快地生成帧(我使用的是500000 bps)。为了有效地发送,我尝试在套接字文件描述符上使用"select“来等待它准备就绪,然后是”写“。但是,不管发送缓冲区的状态如何,"select“似乎都会立即返回,而"write”也不会阻塞。这意味着当缓冲区被填满时,我会从“写”(返回值-1)中得到一个错误,并将errno设置为105 (“没有可用的缓冲区空间”)。
这意味着我必须等待任意一段时间,然后再尝试写,这似乎非常低效率(轮询!)。
下面是我的代码(C,为简洁而编辑):
printf("CAN Data Generator\n");
int skt; // CAN raw socket
struct sockaddr_can addr;
struct canfd_frame frame;
const int WAIT_TIME = 500;
// Create socket:
skt = socket(PF_CAN, SOCK_RAW, CAN_RAW);
// Get the index of the supplied interface name:
unsigned int if_index = if_nametoindex(argv[1]);
// Bind CAN device to socket created above:
addr.can_family = AF_CAN;
addr.can_ifindex = if_index;
bind(skt, (struct sockaddr *)&addr, sizeof(addr));
// Generate example CAN data: 8 bytes; 0x11,0x22,0x33,...
// ...[Omitted]
// Send CAN frames:
fd_set fds;
const struct timeval timeout = { .tv_sec=2, .tv_usec=0 };
struct timeval this_timeout;
int ret;
ssize_t bytes_writ;
while (1)
{
// Use 'select' to wait for socket to be ready for writing:
FD_ZERO(&fds);
FD_SET(skt, &fds);
this_timeout = timeout;
ret = select(skt+1, NULL, &fds, NULL, &this_timeout);
if (ret < 0)
{
printf("'select' error (%d)\n", errno);
return 1;
}
else if (ret == 0)
{
// Timeout waiting for buffer to be free
printf("ERROR - Timeout waiting for buffer to clear.\n");
return 1;
}
else
{
if (FD_ISSET(skt, &fds))
{
// Ready to write!
bytes_writ = write(skt, &frame, CAN_MTU);
if (bytes_writ != CAN_MTU)
{
if (errno == 105)
{
// Buffer full!
printf("X"); fflush(stdout);
usleep(20); // Wait for buffer to clear
}
else
{
printf("FAIL - Error writing CAN frame (%d)\n", errno);
return 1;
}
}
else
{
printf("."); fflush(stdout);
}
}
else
{
printf("-"); fflush(stdout);
}
}
usleep(WAIT_TIME);
}当我将每帧WAIT_TIME设置为一个高值(例如500 uS)以使缓冲区永远不被填充时,我会看到以下输出:
CAN Data Generator
...............................................................................
................................................................................
...etc太好了!在500 uS,我得到54%的CAN总线利用率(根据canbusload实用程序)。
但是,当我尝试延迟0以使传输速率达到最大值时,我会看到:
CAN Data Generator
................................................................................
............................................................X.XX..X.X.X.X.XXX.X.
X.XX..XX.XX.X.XX.X.XX.X.X.X.XX..X.X.X.XX..X.X.X.XX.X.XX...XX.X.X.X.X.XXX.X.XX.X.
X.X.XXX.X.XX.X.X.X.XXX.X.X.X.XX.X.X.X.X.XX..X..X.XX.X..XX.X.X.X.XX.X..X..X..X.X.
.X.X.XX.X.XX.X.X.X.X.X.XX.X.X.XXX.X.X.X.X..XX.....XXX..XX.X.X.X.XXX.X.XX.XX.XX.X
.X.X.XX.XX.XX.X.X.X.X.XX.X.X.X.X.XX.XX.X.XXX...XX.X.X.X.XX..X.XX.X.XX.X.X.X.X.X.最初的点“。显示缓冲区填充;一旦缓冲区满了,"X“就开始出现,这意味着”写“调用失败,错误105。
通过逻辑跟踪,这意味着"select“必须返回,而"FD_ISSET(skt &fds)”为真,尽管缓冲区已满!(或者说我错过了什么?)
SockedCAN的医生们只是说"可以使用写(2)系统调用进行类似的写入帧操作。“
这个职位建议使用"select“。
这个职位建议“写”不会阻止CAN优先权仲裁,但不包括其他情况。
,那么“选择”是正确的方式吗?我的“写”应该阻止吗?我还可以使用哪些其他选项来避免轮询呢?
发布于 2018-03-10 15:18:40
在快速查看埋伏量:184之后,它似乎计算了效率(#data/#总线上的总位)。
另一方面,根据这,对于8字节帧,CAN总线的最大效率约为57%,因此您似乎离这57%不远.我想说的是,你确实是在淹没公共汽车。
当设置500 bus延迟、500 here总线比特率、8字节帧时,它给出的(control+data)比特率为228 here,低于CAN总线的最大比特率,因此这里没有瓶颈。
而且,由于在本例中只监视了一个套接字,所以您实际上不需要pselect。您可以使用pselect和1套接字完成无需pselect和使用write的所有操作。
(发现者:下面,这只是猜测,因为我现在不能测试,对不起。)至于为什么pselect的行为,认为缓冲区可能有字节语义,所以它告诉您仍然有空间为更多的字节(至少1),不一定要为更多的can_frame。所以,当返回时,pselect不通知您可以发送整个可以帧。我想您可以通过使用SIOCOUTQ和Rx缓冲区SO_SNDBUF的最大大小来解决这个问题,但不确定它是否适用于CAN套接字(最好是使用SO_SNDLOWAT标志,但它在Linux的实现中是不可更改的)。
所以,回答你的问题:
(p)select,要么是write,因为你只在等待一个文件描述符,没有真正的区别。SIOCOUTQ和getsockopt‘’ing SO_SNDBUF和substracting.你得亲自检查一下。或者,您也可以将发送缓冲区大小设置为sizeof(can_frame)的倍数,看看当可用的sizeof(can_frame)小于sizeof(can_frame)时,它是否会使您保持信令。无论如何,如果您有兴趣有一个更精确的时间,您可以使用BCM套接字。在那里,您可以指示内核在特定的时间间隔发送特定的帧。一旦设置完毕,进程就在内核空间中运行,没有任何系统调用。这样就避免了用户内核缓冲区问题.我会测试不同的费率,直到canbusload显示总线利用率没有增加为止。
发布于 2021-12-29 15:29:40
选择和投票为我工作与SocketCan的权利。但是,需要谨慎的配置。
一些背景:
在用户应用程序和HW之间,有两个缓冲区:
在这条道路上有2个流量控制点:
现在,假设套接字缓冲区有20个数据包的空间,而驱动程序的qdisc有5个数据包的空间。我们还假设HW有一个TX邮箱。
解决办法是什么?在上述假设中,让我们为8个数据包配置套接字缓冲区。在这种情况下,轮询/选择将阻止用户应用程序后的4个包,确保在驱动器的qdisc中有空间为所有这4个包。
但是,套接字缓冲区被配置为字节,而不是数据包。翻译应按以下方式进行:每个数据包都可以在套接字缓冲区占据~704字节(其中大多数用于套接字结构)。因此,要将套接字缓冲区配置为8个数据包,以字节为单位的大小应该是8*704:
int size = 8*704;
setsockopt(s, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));https://stackoverflow.com/questions/47425873
复制相似问题