明确为什么要引用poll机制?

while(1)

{

  read(fd,&key_val,1);//如果没有按键按下,它会一直在等待。现在想做这么一件事情:如果5s后,没有按键按下,它就会返回。此时就要用到poll机制

}

当应用程序调用poll时,会相应的调用内核空间的sys_poll
sys_poll
  do_sys_poll
    poll_initwait(&table)
    void poll_initwait(struct poll_wqueues *pwq)
      init_poll_funcptr(&pwq->pt, __pollwait);//下面这个函数为该函数的具体实现
      static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc)
      {
        pt->qproc = qproc;//此时就相当于:pt->qproc = __pollwait,所以指针
        //qproc就指向了函数__pollwait
      }
      //接下来看一下__pollwait的具体实现:
      static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,poll_table *p)
      {
        struct poll_table_entry *entry = poll_get_entry(p);
        if (!entry)
        return;
        get_file(filp);
        entry->filp = filp;
        entry->wait_address = wait_address;
        init_waitqueue_entry(&entry->wait, current);
        add_wait_queue(wait_address, &entry->wait);
      }
  从上面的分析可知,poll_initwait(&table)函数只实现了将当前进程加入到wait_address队列中
  do_poll(nfds, head, &table, timeout),会进入一个死循环,在死循环里调用了do_pollfd(pfd, pt)
    do_pollfd(pfd, pt)
      mask = file->f_op->poll(file, pwait);//unsigned int (*poll) (struct file *, struct poll_table_struct *);
      在驱动程序中的file_operations结构体中,定义了.poll=forth_drv_poll, 从而使得poll指针指向了函数forth_drv_poll,
      所以在执行poll函数时,实际上是调用了驱动程序中的forth_drv_poll这个函数。在forth_drv_poll这个函数中,会
      调用poll_wait(file, &button_waitq, wait),看一下该函数的具体实现:
      void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
      {
        if (p && wait_address)
        p->qproc(filp, wait_address, p);//在上面已经分析过,p->qproc指向函数__pollwait。
        因此这条语句相当于执行__pollwait(filp, wait_address, p),从上面的分析可以看出,将当前进程加入到
        wait_address等待队列中。
      }
  注意:在我们的驱动程序中会有一个返回值,假设为mask1.那么此时mask=mask1,而mask又将返回给do_pollfd(pfd, pt)这个函数。内核中有这样一段程序:
          for(;;)
            {
              if (do_pollfd(pfd, pt)) {
                count++;
                pt = NULL;
            }如果mask非零,count++,表示按键按下。

            /*若count不为0或者超时或者有事件发生则会跳出死循环*/
            if (count || !*timeout || signal_pending(current))
            break;
            /*若count为0且未超时且无事件发生则会休眠__timeout时间*/
            schedule_timeout(__timeout);当休眠到timeout这段时间之后,还是没有任何事件发生,会再次进入循环中
            ,当到达if (count || !*timeout || signal_pending(current))时,会跳出循环。
}

应用程序:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>

int main(int argc, char **argv)
{
  int fd;
  unsigned char key_val;
  int ret;

  struct pollfd fds[1];   //poll机制可以同时查看多个文件,在这里我们只查询一个驱动文件。

  fd = open("/dev/buttons", O_RDWR);
  if (fd < 0)
  {
    printf("can't open!\n");
  }

  fds[0].fd = fd;  //所要查询的驱动文件
  fds[0].events = POLLIN; //POLLIN表示有数据等待读取
  while (1)
 {
    ret = poll(fds, 1, 5000);//1表示只有一个文件
    if (ret == 0) //返回值为0,表示超时
  {
    printf("time out\n");
  }
    else
  {
    read(fd, &key_val, 1);
    printf("key_val = 0x%x\n", key_val);
  }
 }

  return 0;
}

/*注意:与本节的程序无关,只是更好的熟悉poll函数

poll函数原型:

#include <poll.h>

int poll(struct pollfd fd[], nfds_t nfds, int timeout);

struct pollfd的结构如下:

struct pollfd{

int fd; // 文件描述符
short event;// 请求的事件
short revent;// 返回的事件 }

*/

驱动程序:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>

static struct class *forthdrv_class;
static struct class_device *forthdrv_class_dev;

volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;

volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

/* 中断事件标志, 中断服务程序将它置1,forth_drv_read将它清0 */
static volatile int ev_press = 0;

struct pin_desc{
unsigned int pin;
unsigned int key_val;
};

/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val;

struct pin_desc pins_desc[4] = {
{S3C2410_GPF0, 0x01},
{S3C2410_GPF2, 0x02},
{S3C2410_GPG3, 0x03},
{S3C2410_GPG11, 0x04},
};

/*
* 确定按键值
*/
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
  struct pin_desc * pindesc = (struct pin_desc *)dev_id;
  unsigned int pinval;

  pinval = s3c2410_gpio_getpin(pindesc->pin);

  if (pinval)
  {
    /* 松开 */
    key_val = 0x80 | pindesc->key_val;
  }
  else
  {
    /* 按下 */
    key_val = pindesc->key_val;
  }

  ev_press = 1; /* 表示中断发生了 */
  wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */

  return IRQ_RETVAL(IRQ_HANDLED);
}

static int forth_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;
}

ssize_t forth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
  if (size != 1)
  return -EINVAL;

  /* 如果没有按键动作, 休眠 */
  wait_event_interruptible(button_waitq, ev_press);

  /* 如果有按键动作, 返回键值 */
  copy_to_user(buf, &key_val, 1);
  ev_press = 0;

  return 1;
}

int forth_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;
}

static unsigned forth_drv_poll(struct file *file, poll_table *wait)
{
  unsigned int mask = 0;
  poll_wait(file, &button_waitq, wait); // 不会立即休眠

  if (ev_press)
  mask |= POLLIN | POLLRDNORM;

  return mask;
}

static struct file_operations sencod_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = forth_drv_open,
.read = forth_drv_read,
.release = forth_drv_close,
.poll = forth_drv_poll,
};

int major;
static int forth_drv_init(void)
{
  major = register_chrdev(0, "forth_drv", &sencod_drv_fops);

  forthdrv_class = class_create(THIS_MODULE, "forth_drv");

  forthdrv_class_dev = class_device_create(forthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */

  gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
  gpfdat = gpfcon + 1;

  gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
  gpgdat = gpgcon + 1;

  return 0;
}

static void forth_drv_exit(void)
{
  unregister_chrdev(major, "forth_drv");
  class_device_unregister(forthdrv_class_dev);
  class_destroy(forthdrv_class);
  iounmap(gpfcon);
  iounmap(gpgcon);
  return 0;
}

module_init(forth_drv_init);

module_exit(forth_drv_exit);

MODULE_LICENSE("GPL");

字符设备驱动程序之poll机制(韦大仙)的更多相关文章

  1. 韦东山驱动视频笔记——3.字符设备驱动程序之poll机制

    linux内核版本:linux-2.6.30.4 目的:我们在中断方式的按键应用程序中,如果没有按键按下,read就会永远在那等待,所以如果在这个程序里还想做其他事就不可能了.因此我们这次改进它,让它 ...

  2. 字符设备驱动笔记——poll机制分析(七)

    poll机制分析 所有的系统调用,基于都可以在它的名字前加上“sys_”前缀,这就是它在内核中对应的函数.比如系统调用open.read.write.poll,与之对应的内核函数为:sys_open. ...

  3. 浅析Linux字符设备驱动程序内核机制

    前段时间在学习linux设备驱动的时候,看了陈学松著的<深入Linux设备驱动程序内核机制>一书. 说实话.这是一本非常好的书,作者不但给出了在设备驱动程序开发过程中的所须要的知识点(如对 ...

  4. 嵌入式Linux驱动学习之路(二十一)字符设备驱动程序总结和块设备驱动程序的引入

    字符设备驱动程序 应用程序是调用C库中的open read write等函数.而为了操作硬件,所以引入了驱动模块. 构建一个简单的驱动,有一下步骤. 1. 创建file_operations 2. 申 ...

  5. LINUX设备驱动程序笔记(三)字符设备驱动程序

          <一>.主设备号和次设备号        对字符设备的訪问时通过文件系统内的设备名称进行的.那些设备名称简单称之为文件系统树的节点,它们通常位于/dev文件夹. 字符设备驱动程 ...

  6. 简单linux字符设备驱动程序

    本文代码参考<LINUX设备驱动程序>第三章 字符设备驱动程序 本文中的“字符设备”是一段大小为PAGE_SIZE的内存空间 功能:向字符设备写入字符串:从字符设备读出字符串 代码: 1. ...

  7. Linux 简单字符设备驱动程序 (自顶向下)

    第零章:扯扯淡 特此总结一下写的一个简单字符设备驱动程序的过程,我要强调一下“自顶向下”这个介绍方法,因为我觉得这样更容易让没有接触过设备驱动程序的童鞋更容易理解,“自顶向下”最初从<计算机网络 ...

  8. ARM Linux字符设备驱动程序

    1.主设备号和次设备号(二者一起为设备号): 一个字符设备或块设备都有一个主设备号和一个次设备号.主设备号用来标识与设备文件相连的驱动程序,用来反  映设备类型.次设备号被驱动程序用来辨别操作的是哪个 ...

  9. 一个简单的演示用的Linux字符设备驱动程序

    实现如下的功能:--字符设备驱动程序的结构及驱动程序需要实现的系统调用--可以使用cat命令或者自编的readtest命令读出"设备"里的内容--以8139网卡为例,演示了I/O端 ...

随机推荐

  1. Linux—磁盘管理

    https://www.cnblogs.com/new-journey/p/10076387.html https://www.cnblogs.com/jiangxiaoxian/p/9610903. ...

  2. 【转载】Spring学习(1)——快速入门--2019.05.19

    原文地址:https://www.cnblogs.com/wmyskxz/p/8820371.html   认识 Spring 框架 Spring 框架是 Java 应用最广的框架,它的成功来源于理念 ...

  3. shell基础、变量、相关脚本

    目录 一.shell基础 书写规范 引号 配置文件 read交互 脚本调式 小节总结 二.变量 变量类型 位置变量 状态变量 替换和删除 变量补充 变量运算 小节总结 三.相关脚本面试题 统计hist ...

  4. 二叉搜索树中第K小的元素

    给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素. 说明:你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数. 示例 1: 输入: root = [ ...

  5. Django3.0 前瞻 支持异步通信

    最近两年,Django的版本号提升得特别快,2.0还没有多久,很快就要到3.0了. 让我们先看看官方的路线图和时间表: 版本号 发布日期 停止更新日期 停止维护日期 3.0 2019-12 2020- ...

  6. Python程序中的进程操作-开启多进程(multiprocess.process)

    目录 一.multiprocess模块 二.multiprocess.process模块 三.process模块介绍 3.1 方法介绍 3.2 属性介绍 3.3 在windows中使用process模 ...

  7. 多模块springboot项目启动访问不了jsp页面

    被springboot项目maven打包.启动.访问折腾的头都大了,一步一个坑,在解决了所有的问题之后,那种欣喜若狂的心情只有自己能体会,决定要好好记录一下了,废话不多说了,直接进入正题. 问题和排查 ...

  8. fastdfs详细安装教程

    前言 最近开始搞的项目涉及到 fastdfs,工欲善其事,必先利其器,于是我自己搭了一台 fastdfs 服务器.坑已经帮大家都踩过了.按照该教程100%能安装成功. 前期准备 一个centos7 一 ...

  9. MongoDB for OPS 04:备份恢复

    写在前面的话 和 MySQL 一样,mongodb 也是需要将数据进行备份的,毕竟天有不测风云,谁也不知道哪天机器就炸了. 备份恢复 mongodb 提供了两种备份恢复手段:mongoexport / ...

  10. Apache ActiveMQ序列化漏洞(CVE-2015-5254)复现

    Apache ActiveMQ序列化漏洞(CVE-2015-5254)复现 一.漏洞描述 该漏洞源于程序没有限制可在代理中序列化的类.远程攻击者可借助特制的序列化的java消息服务(JMS)Objec ...