我正在开发运行 Yocto 的嵌入式处理器。我有一个修改过的 uio_pdrv_genirq.c UIO 驱动程序。
我正在编写一个库来控制 DMA。有一个函数可以写入设备文件并启动 DMA。第二个函数旨在通过调用 select() 等待 DMA 完成。当 DMA 正在进行时,设备文件会阻塞。完成后,DMA 控制器发出中断,释放设备文件上的块。
我使用 read() 让系统按预期工作,但我想切换到 select(),以便我可以包含超时。然而,当我使用 select() 时,它似乎无法识别该块并且总是立即返回(在 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;
}
}
我的代码有什么明显的错误吗?或者有人可以建议替代选择吗?
原来UIO驱动包含两个计数器。一个记录了 事件数量(
event_count
),另一个记录有多少个事件
调用函数知道 (listener->event_count
)。
当您在 UIO 驱动程序上执行
read()
时,它会返回事件数并
使 listener->event_count
等于 event_count
。 IE。听者是
现在了解已发生的所有事件。
当您在 UIO 驱动程序上使用
poll()
或 select()
时,它会检查这两个是否
数字不同,如果不同则返回(如果相同则返回
等待直到它们不同然后返回)。它不会更新
listener->event_count
。
显然,如果您在调用
read()
之间不执行 select()
那么
listener->event_count
与 event_count
和第二个不匹配
select()
会立即返回。 因此需要调用
read()
在致电 select()
之间。
事后看来,
select()
应该以这种方式工作似乎很明显,但当时对我来说并不明显。
这个答案假设可以将 select() 用于指定的设备文件(我仅将 select() 用于套接字描述符)。作为 select() 的替代函数,您可能需要查看 poll() 系列函数。接下来的内容至少有望提供有关如何解决调用 select() 问题的提示。
select() 函数的第一个参数必须是最大描述符编号加 1。由于只有一个描述符,因此可以将其直接传递给 select() 作为其第一个参数并加 1。还要考虑文件描述符dma_device 中的可能无效。在超时时返回 EINTR 实际上可能是您想要做的事情,但如果情况并非如此,并且为了测试无效描述符,这里有一个不同的版本供您考虑。 select() 调用可能会被信号中断,在这种情况下,返回值为 -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
}
}
}