前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >linux poll机制《Rice linux 学习笔记》

linux poll机制《Rice linux 学习笔记》

作者头像
Rice加饭
发布2022-05-09 21:10:27
1.1K0
发布2022-05-09 21:10:27
举报
文章被收录于专栏:Rice嵌入式Rice嵌入式

上一篇文章写到中断机制,采用了等待队列的方式实现了按键中断。但是你会发现,应用程序在读取按键值的时,当没有按键按下,则一直处于睡眠态。无法继续往下执行。所以我们用其他办法来解决这个问题。

首先来讲解一下阻塞与非阻塞的区别:

阻塞:指应用程序在执行设备操作时(读/写设备),若不能获得资源,则挂起进程直到满足可操作的条件后再进行操作。被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足。举例:用户以阻塞的方式访问设备,若设备的资源不能获取,驱动程序的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》结构体原型:

代码语言:javascript
复制
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();

分析内核源码,可以看出它的调用关系:

代码语言:javascript
复制
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()函数:

代码语言:javascript
复制
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的方法。

代码语言:javascript
复制
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结构体中:

代码语言:javascript
复制
static struct file_operations buttons_fops = {
  .owner    = THIS_MODULE,
  .open    = button_open,
  .read   = button_read,
  .release = button_close,
  .poll   = button_poll,
};

应用程序的实现:

代码语言:javascript
复制
#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;
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rice 嵌入式开发技术分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档