gpt4 book ai didi

c - SocketCAN select() 和 write() 不阻塞

转载 作者:太空宇宙 更新时间:2023-11-04 04:20:32 25 4
gpt4 key购买 nike

我正在使用 SocketCAN 测试嵌入式设备(SOC/ARM 内核/Linux)上的 CAN 接口(interface),我想使用高效代码尽快发送数据进行测试。

我可以将 CAN 设备(“can0”)作为 BSD 套接字打开,并使用“write”发送帧。这一切都很好。

我的桌面显然可以比 CAN 传输速率更快地生成帧(我使用的是 500000 bps)。为了有效发送,我尝试在套接字文件描述符上使用“select”等待它准备好,然后是“write”。但是,无论发送缓冲区的状态如何,“select”似乎都会立即返回,“write”也不会阻塞。这意味着当缓冲区填满时,我从“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”开始出现,表示“write”调用失败并出现错误 105。

通过逻辑追踪,这意味着“select”一定已经返回并且“FD_ISSET(skt, &fds)”为真,尽管缓冲区已满! (或者我错过了什么?)。

SockedCAN 文档只是说“Writing CAN frames can be done similarly, with the write(2) system call

This post建议使用“选择”。

This post建议“写入”不会阻止 CAN 优先级仲裁,但不包括其他情况。

那么“选择”是正确的做法吗?我的“写”应该阻止吗?我可以使用哪些其他选项来避免轮询?

最佳答案

快速查看 canbusload:184 后,它似乎计算效率(#data/#total bits on the bus)。

另一方面,根据this ,对于 8 字节帧,CAN 总线的最大效率约为 57%,因此您似乎离 57% 不远……我会说您确实在充斥总线。

当设置 500uS 延迟、500kbps 总线比特率、8 字节帧时,它为您提供 228kbps 的(控制+数据)比特率,低于 CAN 总线的最大比特率,因此,这里没有瓶颈。

另外,因为在这种情况下只有 1 个套接字被监视,所以您真的不需要 pselect。您可以使用 pselect 执行所有操作,而无需使用 pselect 并使用 write 即可完成 1 个套接字。

(免责声明:下文中,这只是猜测,因为我现在无法测试,抱歉。)至于为什么pselect的行为,认为缓冲区可能有字节语义,所以它告诉你还有空间容纳更多的bytes(至少1个),不一定更多can_frame。因此,返回时,pselect 不会通知您可以发送整个 CAN 帧。我想你可以通过使用 SIOCOUTQ 和 Rx 缓冲区的最大大小 SO_SNDBUF 来解决这个问题,但不确定它是否适用于 CAN 套接字(最好是使用SO_SNDLOWAT 标志,但它在 Linux 的实现中不可更改)。

所以,回答你的问题:

  1. “选择”是正确的做法吗?好吧,您可以采用两种方式,(p)selectwrite,因为您只是在等待一个文件描述符,所以没有真正的区别。
  2. 我的“写入”应该阻塞吗?如果发送缓冲区中没有可用的单个字节,应该阻塞。
  3. 我可以使用哪些其他选项来避免轮询?也许通过 ioctl'ing SIOCOUTQgetsockopt' ing SO_SNDBUF 和减法......你需要自己检查一下。或者,也许您可​​以将发送缓冲区大小设置为 sizeof(can_frame) 的倍数,并查看它是否让您在小于 sizeof(can_frame) 可用时发出信号。

无论如何,如果您对更精确的计时感兴趣,可以使用 BCM socket 。在那里,您可以指示内核以特定间隔发送特定帧。一旦设置,进程在内核空间运行,没有任何系统调用。这样就避免了用户内核缓冲区问题。我会测试不同的速率,直到 canbusload 显示总线利用率没有上升。

关于c - SocketCAN select() 和 write() 不阻塞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47425873/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com