使用 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()
应该以这种方式工作,但当时对我来说并不明显。
我正在研究嵌入式处理器 运行 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()
应该以这种方式工作,但当时对我来说并不明显。