一、流程简介
drv_poll 要把自己这个线程挂入等待队列 wq 中。
poll流程分为以下两种情况:
1、有中断唤醒
(1)drv_poll return false
(2)休眠
(3)中断事件
(4)唤醒休眠
(5)drv_poll return true
(6)read data。
2、无中断唤醒
(1)drv_poll return false
(2)休眠
(3)内核唤醒休眠,休眠结束
(4)drv_poll return false
(5)休眠
(6)内核唤醒休眠,休眠结束
(7)drv_poll return false
(8)poll等待超时
(9)退出,返回异常。
注意
drv_poll 要把线程挂入队列 wq,但是并不是在 drv_poll 中进入休眠,而是在调用 drv_poll 之后休眠
drv_poll 要返回数据状态
APP 调用一次 poll,有可能会导致 drv_poll 被调用 2 次
线程被唤醒的原因有 2:中断发生了去队列 wq 中把它唤醒,超时时间到了内核把它唤醒
APP 要判断 poll 返回的原因:有数据,还是超时。有数据时再去调用 read 函数
二、kernel API
Poll函数原型:
unsigned int (*poll) (struct file *, struct poll_table_struct *);
将当前线程挂入等待队列函数:
void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p);
(1)填充file_operations结构体
const struct file_operations key_fops = {
.owner = THIS_MODULE,
.open = key_open,
.release = key_release,
.read = key_read,
.write = key_write,
/* 增加 */
.poll = key_poll,
};
(2)key_poll函数的实现
static unsigned int key_poll(struct file *file, struct poll_table_struct *wait) {
poll_wait(file, &gpio_key_wait, wait);
/* POLLIN 有数据可读 POLLRDNORM 等同于 POLLIN */
return key_signal ? POLLIN | POLLRDNORM : 0;
}
关于return的解析:
poll_wait将线程放入队列结束后会休眠,之后进入return只有两种情况:
① 超时,若超时则说明未出现中断唤醒gpio_key_wait,此时key_signal为0
② 发生中断,此时出现中断唤醒gpio_key_wait,此时key_signal为非0,返回POLLIN | POLLRDNORM给APP
说明
如果 key_poll 执行成功,会返回非负值。
用户态程序通过 poll 或 select 调用检测按键事件,会fds.revents & POLLIN做判断
三、App API
相关函数
在应用层,**poll**
是一个用于监视多个文件描述符是否可读、可写或发生错误的系统调用,常用于处理多路 I/O。它与 select
类似,但没有文件描述符数量限制,性能更优。
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数说明:
-
fds
:一个指向pollfd
结构数组的指针,每个结构表示一个被监控的文件描述符及其事件。 -
nfds
:fds
数组的大小,即需要监控的文件描述符数量。 -
timeout
:超时时间(毫秒)。
timeout > 0
:等待指定的毫秒数。timeout = 0
:立即返回,不阻塞。timeout < 0
:无限等待,直到某个事件发生。
返回值:
- 返回正数:有事件发生的文件描述符数量。
- 返回
0
:超时,没有文件描述符发生事件。 - 返回
-1
:发生错误,可通过errno
查看错误信息。
struct pollfd
结构体
pollfd
是一个结构体,定义如下:
struct pollfd {
int fd; // 文件描述符
short events; // 等待的事件类型
short revents; // 实际发生的事件类型
};
常见的事件类型:
- 等待的事件(
events
):POLLIN
:表示有数据可读。POLLOUT
:表示可以写入数据。POLLERR
:发生错误。POLLHUP
:挂起(通常是管道关闭)。POLLPRI
:有紧急数据可读。
- 实际发生的事件(
revents
):- 可能是上述事件的组合,表示实际发生的事件。
代码示例
#include <stdio.h>
#include <poll.h>
#include <unistd.h>
int main() {
struct pollfd fds[1];
int timeout = 5000;
int fd = 0;
fd = open("/dev/diy_key_double");
if(fd == -1) {
perror("open");
return -1;
}
fds[0].fd = fd;
fds[0].events = POLLIN;
fds[0].revents = 0;
while(1) {
int ret = poll(fds, 1, timeout);
if(ret == -1) {
perror("poll");
break;
}
else if (ret == 0) {
printf("Timeout\n");
}
else {
if(fds[0].revents & POLLIN) {
int buf;
int nread = read(fd, &buf, sizeof(buf));
if(nread > 0) {
printf("Read: %d\n", buf);
}
}
else {
printf("Unexpected event\n");
}
}
}
return 0;
}