我正在使用 SocketCAN 在嵌入式设备(SOC / ARM 内核 / Linux)上测试 CAN 接口,我想使用高效的代码尽可能快地发送数据进行测试。
我可以将 CAN 设备(“can0”)作为 BSD 套接字打开,并使用“write”发送帧。这一切都很好。
我的桌面显然可以比 CAN 传输速率更快地生成帧(我使用的是 500000 bps)。为了有效发送,我尝试在套接字文件描述符上使用“选择”来等待它准备好,然后是“写入”。但是,无论发送缓冲区的状态如何,“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 文档只是说“使用 write(2) 系统调用可以类似地编写 CAN 帧”
这篇文章建议使用“选择”。
这篇文章建议“写”不会阻止 CAN 优先级仲裁,但不涵盖其他情况。
那么“选择”是正确的方法吗?我的“写”应该阻止吗?我可以使用哪些其他选项来避免轮询?