使用 select() 检测 UIO 设备文件上的块

Using select() to detect a block on a UIO device file

我正在研究嵌入式处理器 运行 Yocto。我有一个修改过的 uio_pdrv_genirq.c UIO 驱动程序。

我正在编写一个库来控制 DMA。有一个函数写入设备文件并启动 DMA。第二个函数旨在通过调用 select() 等待 DMA 完成。当 DMA 正在进行时,设备文件块。完成后,DMA 控制器发出一个中断,释放设备文件上的块。

我使用 read() 让系统按预期工作,但我想切换到 select() 所以我可以包括暂停时间。但是,当我使用 select() 时,它似乎没有识别块并且总是立即 returns(在 DMA 完成之前)。我已经包含了一个简单版本的代码:

int gannet_dma_interrupt_wait(dma_device_t *dma_device,
        dma_direction dma_transfer_direction) {

    fd_set rfds;
    struct timeval timeout;
    int select_res;

    /* Initialize the file descriptor set and add the device file */
    FD_ZERO(&rfds);
    FD_SET(dma_device->fd, &rfds);

    /* Set the timeout period. */
    timeout.tv_sec = 5;
    timeout.tv_usec = 0;

    /* The device file will block until the DMA transfer has completed. */
    select_res = select(FD_SETSIZE, &rfds, NULL, NULL, &timeout);

    /* Reset the channel */
    gannet_dma_reset(dma_device, dma_transfer_direction);

    if (select_res == -1) {
        /* Select has encountered an error */
        perror("ERROR <Interrupt Select Failed>\n");
        exit(0);
    }
    else if (select_res == 1) {
        /* The device file descriptor block released */
        return 0;
    }
    else {
        /* The device file descriptor block exceeded timeout */
        return EINTR;
    }
}

我的代码有什么明显的错误吗?或者有人可以建议 select 的替代方案吗?

这个答案假定可以使用 select() 作为指定设备文件的意图(我使用 select() 仅用于套接字描述符)。作为 select() 的替代函数,您可能需要查看 poll() 函数系列。下面的内容有望至少提供一些提示,告诉您可以通过调用 select().

解决您的问题。

select() 函数的第一个参数必须是最大描述符数加 1。因为你只有一个描述符,你可以直接将它传递给 select() 作为它的第一个参数并添加 1。还要考虑 dma_device 中的文件描述符可能无效。在超时时返回 EINTR 可能实际上是您打算做的,但如果不是这种情况并测试无效的描述符,这里有一个不同的版本供您考虑。 select() 调用可能会被信号中断,在这种情况下,return 值为 -1 并且 errno 将设置为 EINTR。这可以由您的函数在内部处理,如:

FD_ZERO(&rfds);
FD_SET(dma_device->fd, &rfds);

timeout.tv_sec = 5;
timeout.tv_usec = 0;

// restart select() if it's interrupted by a signal;

do {

  select_res = select(dma_device->fd + 1, &rfds, NULL, NULL, &timeout);

}
while( select_res < 0 && errno == EINTR);


if (select_res > 0) {

  // a file descriptor is legible

}
else {

  if (select_res == 0) {

    // select() timed-out
  }
  else  {

    // an error other than a signal occurred

    if (errno == EBADF) {

      // your file descriptor is invalid 

    }
  } 
}

原来UIO驱动包含两个计数器。一个记录 事件数(event_count),另一个记录有多少事件 调用函数知道 (listener->event_count).

当您在 UIO 驱动程序上执行 read() 时,它 return 是事件的数量和 使 listener->event_count 等于 event_count。 IE。听众是 现在更新所有已发生的事件。

当您在 UIO 驱动程序上使用 poll()select() 时,它会检查这两个 数字是不同的,如果它们是 returns(如果它们是相同的 等到它们不同,然后 returns)。它不更新 listener->event_count.

很明显,如果您在调用 select() 之间不执行 read(),那么 listener->event_count 将不匹配 event_count 和第二个 select() 将立即 return。 因此有必要调用 read() 在调用 select().

之间

事后看来,select() 应该以这种方式工作,但当时对我来说并不明显。