上一篇文章写到中断机制,采用了等待队列的方式实现了按键中断。但是你会发现,应用程序在读取按键值的时,当没有按键按下,则一直处于睡眠态。无法继续往下执行。所以我们用其他办法来解决这个问题。
首先来讲解一下阻塞与非阻塞的区别:
阻塞:指应用程序在执行设备操作时(读/写设备),若不能获得资源,则挂起进程直到满足可操作的条件后再进行操作。被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足。举例:用户以阻塞的方式访问设备,若设备的资源不能获取,驱动程序的xxx_read()、xxx_write()等操作中将进程阻塞直到资源可以获取才返回;
非阻塞:非阻塞操作的进程在不能进行设备操作时,并不挂起,它要么放弃,要么不停地查询,直至可以进行操作为止。举例:若用户以非阻塞的方式访问设备文件,则当设备资源不可获取时,设备驱动的xxx_read()、xxx_write()等操作应立即返回,read()、write()等系统调用也随即被返回,应用程序收到-EAGAIN返回值。
阻塞、和非阻塞方式的问题:
1、假如有两个设备需要操作。
2、如果使用阻塞方式,两个read都会阻塞,但第一个read时阻塞了。而在这时,第二个设备返回,进程也不会醒来及时处理。直到第一个设备返回后,才可以读取第二个设备。
3、如果使用非阻塞方式,就会不断的轮询,CPU又变得很忙。
4、为了解决不能同时操作多个设备的方法:①使用poll、select方法。②使用多线程方法。
poll机制的解析:其中poll()函数和select()函数的功能同等。
poll函数的解析:
使用poll函数,需要包含头文件:#include <poll.h>
int poll(structpollfd fds[], nfds_t nfds, int timeout);
参数说明:
fds:是一个struct pollfd结构类型的数组,用于存放需要检测其状态的文件描述符集;
nfds:用于标记数组fds中的结构体元素的总数量;
timeout:是poll函数调用阻塞的时间,单位:毫秒;如果timeout==0,那么poll() 函数立即返回而不阻塞,如果设置为负数,那么poll()函数会一直阻塞下去,直到所检测的文件描述符上的感兴趣的事件发生是才返回。
返回值:
>0:数组fds中准备好读、写或出错状态的那些文件描述符的总数量
==0:此时poll超时
-1:poll函数调用失败,同时会自动设置全局变量errno
其中 《struct pollfd》结构体原型:
struct pollfd {
int fd;
short events;
short revents;
};
变量说明:
fd:文件描述符
events:等待需要测试的事件
revents:实际发生的事件,返回结果
其中events和revents的值:
常量 | 说明 |
---|---|
POLLIN | 普通或优先级带数据可读 |
POLLRDNORM | 普通数据可读 |
POLLRDBAND | 优先级带数据可读 |
POLLPRI | 高优先级数据可读 |
POLLOUT | 普通数据可写 |
POLLWRNORM | 普通数据可写 |
POLLWRBAND | 优先级带数据可写 |
POLLERR | 发生错误 |
POLLHUP | 发生挂起 |
POLLNVAL | 描述字不是一个打开的文件 |
poll系统调用分析:
poll系统调用在内核中的入口函数是sys_poll();
分析内核源码,可以看出它的调用关系:
sys_poll()
do_sys_poll()
do_poll()
for (;;) {
for (; pfd != pfd_end; pfd++) {
if (do_pollfd(pfd, pt)) {
count++;
}
}
if (count || timed_out)
break;
poll_schedule_timeout();
}
其中do_pollfd()函数:
do_pollfd(struct pollfd *pollfd, poll_table *pwait)
{
fd = pollfd->fd;
file = fget_light(fd, &fput_needed);
if (file->f_op && file->f_op->poll) {
mask = file->f_op->poll(file, pwait);
}
pollfd->revents = mask;
return mask;
}
通过上面的关系,最后进入一个死循环。然后调用do_pollfd,通过do_pollfd函数可以看出,调用了驱动的poll的方法。然后返回其poll方法的返回值。如果返回非0,则count为非0值。通过这个判断if (count || timed_out)可以看出,当count和timed_out为非0时,则跳出死循环。所以我们可以看出,跳出死循环:有事件产生和时间溢出。如果没有事件产生,则进入睡眠。
代码实现:
基本是在上一篇文章《中断机制》的代码实现中修改一些内容。
驱动代码:增加poll的方法。
static unsigned button_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_wait_queue, wait);
if(ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
}
增加poll方法到file_operations结构体中:
static struct file_operations buttons_fops = {
.owner = THIS_MODULE,
.open = button_open,
.read = button_read,
.release = button_close,
.poll = button_poll,
};
应用程序的实现:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#define file_name "/dev/button"
int main(int argc, char **argv)
{
int fd;
char key_val;
int ret;
struct pollfd fds[1];
fd = open(file_name, O_RDWR);
if(fd < 0) {
printf("error: can`t open %s", file_name);
return 0;
}
fds[0].fd = fd;
fds[0].events = POLLIN;
for(;;) {
ret = poll(fds, 1, 1000);
if(ret == 0) {
printf("time out");
}
else {
read(fd, &key_val, 1);
printf("button input: %d\r\n", key_val);
}
}
return 0;
}
本文分享自 Rice 嵌入式开发技术分享 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!