一 what
poll,顾名思义,就是查询的意思,但是这里的查询,跟我们第一篇中通过在while循环中一直read方式的轮询有什么区别呢?
linux内核提供了poll函数给用户程序查询字符设备的状态,它带有一个超时等待机制,当在规定时间内没有读取到字符设备的状态时,将直接返回,释放掉cpu进入休眠;如果在规定时间内产生了某个中断,我们可以利用read函数去读取字符设备的状态,从本质上来说,poll机制还是利用中断方式来实现的,和上一篇文章中有区别的地方就是,上一篇等待唤醒休眠是在read函数中实现的,而在poll机制中,实在poll函数中实现。
这篇文章也是先以使用内核函数为主,关于内核中的实现细节未做深度分析。
二 why
同样使用中断的原因,也是为避免长时间占用cpu的问题;同时将唤醒动作从read函数中提取出来,使用poll函数来实现。
三 how
a. open函数
static int fourth_drv_open(struct inode *inode, struct file *file)
{
/* 配置GPF0,2为输出引脚 */
/* 配置GPG3,11为输出引脚 */
request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);
return 0;
}
b. read函数
static int fourth_drv_read(struct file *filp, char __user *buf,
size_t size, loff_t *ppos)
{
/* 返回4个引脚电平状态 */
if (size != 1)
return -EINVAL;
/* 如果没有按键动作, 休眠 */
wait_event_interruptible(button_waitq, ev_press);
/* 如果有按键动作, 返回键值 */
copy_to_user(buf, &key_val, 1);
ev_press = 0;
return (sizeof(key_val));
}
c. close函数
int fourth_drv_close(struct inode *inode, struct file *file)
{
free_irq(IRQ_EINT0, &pins_desc[0]);
free_irq(IRQ_EINT2, &pins_desc[1]);
free_irq(IRQ_EINT11, &pins_desc[2]);
free_irq(IRQ_EINT19, &pins_desc[3]);
return 0;
}
d. poll函数
上面的几个函数和《linux字符设备驱动之中断篇(二)》基本一样,重点分析一下这里的poll函数,它调用了poll_wait函数,第三个参数为超时等待时间
static unsigned int fourth_drv_poll(struct file *file, struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait); // 不会立即休眠
if (ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
}
四 test
- 写drvtest用户程序,比如针对按键按下的可执行程序,代码如下
重点关注struct pollfd fds[1]和ret = poll(fds, 1, 5000)两句;
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
/* forthdrvtest
*/
int main(int argc, char **argv)
{
int fd;
unsigned char key_val;
int ret;
struct pollfd fds[1];
fd = open("/dev/buttons", O_RDWR);
if (fd < 0)
{
printf("can't open!\n");
return 0;
}
fds[0].fd = fd;
fds[0].events = POLLIN;
while (1)
{
ret = poll(fds, 1, 5000);
if (ret == 0)
{
printf("time out\n");
}
else
{
read(fd, &key_val, 1);
printf("key_val = 0x%x\n", key_val);
}
}
return 0;
}
- 编译动态库: make
- 编译用户程序: arm-linux-gcc -0 xxx xxx.c
- 上传动态库以及可执行程序到nfs服务器 cp xxx /work/nfs/first_fs
- 加载动态库: insmod xxx.ko
- 修改可执行文件权限: chmod +777 yyy
- 执行可执行程序
之所以采用中断的实现方式,是为了降低cpu的占用率,我们使用top命令查看此时cpu占用率,发现比一直读取的方法明显降低了许多