- 使用休眠-唤醒的方式等待某个事件发生时,有一个缺点:等待的时间可能很久。
- 可以使用 poll 机制
① APP 不知道驱动程序中是否有数据,可以先调用 poll 函数查询一下,poll 函数可以传入超时时间;
② APP 进入内核态,调用到驱动程序的 poll 函数,如果有数据的话立刻返回;
③ 如果发现没有数据时就休眠一段时间;
④ 当有数据时,比如当按下按键时,驱动程序的中断服务程序被调用,它会记录数据、唤醒 APP;
⑤ 当超时时间到了之后,内核也会唤醒 APP;
⑥ APP 根据 poll 函数的返回值就可以知道是否有数据,如果有数据就调用 read 得到数据
- POLL机制流程
- 并不是在驱动程序的poll函数中休眠,而是在sys_poll中休眠
① drv_poll 要把线程挂入队列 wq,但是并不是在 drv_poll 中进入休眠,而是在调用 drv_poll 之后休眠
② drv_poll 要返回数据状态
③ APP 调用一次 poll,有可能会导致 drv_poll 被调用 2 次
④ 线程被唤醒的原因有 2:中断发生了去队列 wq 中把它唤醒,超时时间到了内核把它唤醒
⑤ APP 要判断 poll 返回的原因:有数据,还是超时。有数据时再去调用 read 函数。
- 驱动编程
- 使用 poll 机制时,驱动程序的核心就是提供对应的 drv_poll 函数。
① 把当前线程挂入队列 wq:poll_wait
APP 调用一次 poll,可能导致 drv_poll 被调用 2 次,但是我们并不需要把当前线程挂入队列 2 次。
可以使用内核的函数 poll_wait 把线程挂入队列,如果线程已经在队列里了,它就不会再次挂入。
② 返回设备状态:
APP 调用 poll 函数时,有可能是查询“有没有数据可以读”:POLLIN,也有可能是查询“你有没有
空间给我写数据”:POLLOUT。
- drv_poll 要返回自己的当前状态:(POLLIN | POLLRDNORM) 或 (POLLOUT | POLLWRNORM)。
- 应用编程
- APP 可以调用 poll 或 select 函数,这 2 个函数的作用是一样的
- poll/select 函数可以监测多个文件,可以监测多种事件
- 实现drv_poll
static unsigned int gpio_key_drv_poll(struct file *fp, poll_table * wait)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
poll_wait(fp, &gpio_key_wait, wait);
return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM; /* 根据环形缓冲区是否为空返回不同状态 */
}
- 实现drv_poll
- 使用poll ```c struct pollfd fds[1]; int timeout_ms = 5000;
// fds[0].fd = fd; fds[0].events = POLLIN;
// ret = poll(fds, 1, timeout_ms); if ((ret == 1) && (fds[0].revents & POLLIN)) { read(fd, &val, 4); printf(“get button : 0x%x\n”, val); } else { printf(“timeout\n”); } ```
- Linux APP 系统调用,基本都可以在它的名字前加上“sys_”前缀,这就是它在内核中对应的函数。比如系统调用 open、read、write、poll,与之对应的内核函数为:sys_open、sys_read、sys_write、sys_poll