SocketCAN select() 和 write() 不会阻塞

2023-12-01

我正在使用 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 时,我的 CAN 总线利用率为 54%(根据 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 文档只是说“可以使用 write(2) 系统调用以类似方式写入 CAN 帧"

这个帖子建议使用“选择”。

这个帖子建议“写入”不会阻塞 CAN 优先级仲裁,但不包括其他情况。

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


快速浏览后总线负载:184,似乎它计算效率(#data/#总线上的总位数)。

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

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

另外,由于在这种情况下仅监视 1 个套接字,因此您不需要pselect, 真的。你能做的一切pselect和 1 个插座无需pselect并使用write.

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

所以,回答你的问题:

  1. “选择”是正确的做法吗?嗯,两种方法都可以(p)select or write,由于您只等待一个文件描述符,因此没有真正的区别。
  2. 我的“写”应该被阻止吗?如果发送缓冲区中没有可用的单个字节,则应该这样做。
  3. 我还可以使用哪些其他选项来避免轮询?也许通过ioctl'ing SIOCOUTQ and 获取套接字'ing SO_SNDBUF并减去...你需要自己检查一下。或者,也许您可​​以将发送缓冲区大小设置为sizeof(can_frame)看看它是否让你在小于时发出信号sizeof(can_frame)可用。

无论如何,如果您有兴趣获得更精确的计时,您可以使用 BCM 套接字。在那里,你可以instruct内核以特定的时间间隔发送特定的帧。一旦设置,进程就在内核空间中运行,无需任何系统调用。这样就避免了用户内核缓冲区问题。我会测试不同的利率,直到canbusload显示公交车利用率没有上升。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SocketCAN select() 和 write() 不会阻塞 的相关文章

  • 在 C/C++ 中获得正模数的最快方法

    通常在我的内部循环中 我需要以 环绕 方式索引数组 因此 例如 如果数组大小为 100 并且我的代码要求元素 2 则应该给它元素 98 高级语言 例如 Python 可以简单地使用my array index array size 但由于某
  • 在 Xamarin 中隐藏软键盘

    如何隐藏软键盘以便在聚焦时显示Entry在 Xamarin forms 便携式表单项目中 我假设我们必须为此编写特定于平台的渲染器 但以下内容不起作用 我创建自己的条目子类 public class MyExtendedEntry Entr
  • 信号处理程序有单独的堆栈吗?

    信号处理程序是否有单独的堆栈 就像每个线程都有单独的堆栈一样 这是在 Linux C 环境中 来自 Linux 手册页signal 7 http kernel org doc man pages online pages man7 sign
  • 如何在 SqlDataReader.Read() 期间从死锁异常中恢复

    我的 NET 应用程序的事件日志显示 它在从 Sql Server 读取数据时偶尔会出现死锁 这种情况通常非常罕见 因为我们已经优化了查询以避免死锁 但有时仍然会发生 过去 我们在调用ExecuteReader函数在我们的SqlComman
  • GCC 和 ld 找不到导出的符号...但它们在那里

    我有一个 C 库和一个 C 应用程序 尝试使用从该库导出的函数和类 该库构建良好 应用程序可以编译 但无法链接 我得到的错误遵循以下形式 app source file cpp text 0x2fdb 对 lib namespace Get
  • 从 WebBrowser 控件 C# 获取滚动值

    我试图在 WebBrowser 控件中获取网页的 Y 滚动索引 但无法访问内置滚动条的值 有任何想法吗 对于标准模式下的 IE 使用文档类型 正如你所说 scrollTop是的财产元素 而不是 HtmlDocument htmlDoc th
  • C++ php 和静态库

    我创建了一个library a 其中包含 cpp 和 h 文件 其中包含很多类 嵌套类和方法 我想在 php 示例中包含这个静态库并尝试使用它 我想提一下 我是 php 新手 我已经在 test cpp 文件中测试了我的 libray a
  • 检查 RoutedEvent 是否有任何处理程序

    我有一个自定义 Button 类 当单击它时 打开特定窗口 它总是执行相同的操作 我添加了一个可以在按钮的 XAML 中分配的 Click 事件 就像常规按钮一样 当它被单击时 我想执行 Click 事件处理程序 如果已分配 否则我想执行默
  • 将标量添加到特征矩阵(向量)

    我刚刚开始使用 Eigen 库 无法理解如何向所有矩阵成员添加标量值 假设我有一个矩阵 Eigen Matrix3Xf mtx Eigen Matrix3Xf Ones 3 4 mtx mtx 1 main cxx 104 13 error
  • 如何在c的case语句中使用省略号?

    CASE expr no commas ELLIPSIS expr no commas 我在c的语法规则中看到了这样的规则 但是当我尝试重现它时 int test float i switch i case 1 3 printf hi 它失
  • C# 中的 strstr() 等效项

    我有两个byte 我想找到第二个的第一次出现byte 在第一个byte 或其中的一个范围 我不想使用字符串来提高效率 翻译第一个byte to a string会效率低下 基本上我相信就是这样strstr 在 C 中做 最好的方法是什么 这
  • IEnumerable.Except 不起作用,那么我该怎么办?

    我有一个 linq to sql 数据库 非常简单 我们有 3 个表 项目和用户 有一个名为 User Projects 的连接表将它们连接在一起 我已经有了一个获得的工作方法IEnumberable
  • 每个数据库多个/单个 *.edmx 文件

    我有一个通过 ADO net 数据服务与数据库交互的项目 数据库很大 近 150 个具有依赖关系的表 该项目几年前开始 当时使用的是数据集 现在我们正在转向实体模型关系 由于我们添加了更多需要使用的表 该模型正在不断增长 这是管理这一切的正
  • 矩阵到数组 C#

    这将是转换方阵的最有效方法 例如 1 2 3 4 5 6 7 8 9 into 1 2 3 4 5 6 7 8 9 in c 我在做 int array2D new int 1 2 3 4 5 6 7 8 9 int array1D new
  • 在简单注入器中解析具有自定义参数的类

    我正在使用以下命令创建 WPF MVVM 应用程序简易注射器作为 DI 容器 现在 当我尝试从简单注入器解析视图时遇到一些问题 因为我需要在构造时将参数传递到构造函数中 而不是在将视图注册到容器时 因此这不是适用的 简单注入器将值传递到构造
  • QFileDialog::getSaveFileName 和默认的 selectedFilter

    我有 getSaveFileName 和一些过滤器 我希望当用户打开 保存 对话框时选择其中之一 Qt 文档说明如下 可以通过将 selectedFilter 设置为所需的值来选择默认过滤器 我尝试以下变体 QString selFilte
  • 使用 QtWebEngine 将 C++ 对象暴露给 Qt 中的 Javascript

    使用 QtWebkit 可以通过以下方式将 C 对象公开给 JavascriptQWebFrame addToJavaScriptWindowObject如中所述https stackoverflow com a 20685002 5959
  • ASP.NET Core MVC 视图组件搜索路径

    在此处的文档中 https learn microsoft com en us aspnet core mvc views view components view aspnetcore 2 2 https learn microsoft
  • xsi:type 属性搞乱了 C# XML 反序列化

    我使用 XSD exe 根据 XML 架构 xsd 文件 自动生成 C 对象 我正在反序列化 OpenCover 输出 但其中一个部分类未正确生成 这是导致异常的行
  • 从 JavaScript 中的 OnClientClick 事件中阻止 C# 中的 asp:Button OnClick 事件?

    我有一个asp Button在我的网页上 它调用 JavaScript 函数和代码隐藏方法 后者进行调用以导航到另一个页面 在 JavaScript 函数中 我正在检查条件 如果不满足这个条件 我想中止导航 以便OnClick方法未被调用

随机推荐