1、什么是Poll方法,功能是什么?

2、Select系统调用(功能)
      Select系统调用用于多路监控,当没有一个文件满足要求时,select将阻塞调用进程。
      int select(int maxfd, fd_set *readfds, fd_set *writefds, fe_set *exceptfds, const struct timeval *timeout)
     Select系统调用(参数)
     1)Maxfd:
           文件描述符的范围,比待检测的最大文件描述符大1
     2)Readfds:
           被读监控的文件描述符集
     3)Writefds:
           被写监控的文件描述符集
     4)Exceptfds:
           被异常监控的文件描述符集;
     5)Timeout:

定时器,Timeout取不同的值,该调用有不同的表现:

1>Timeout值为0,不管是否有文件满足要求,都立刻返回,无文件满足要求返回0,有文件满足要求返回一个正值。
          2>Timeout为NULL,select将阻塞进程,直到某个文件满足要求
          3>Timeout 值 为 正 整 数 , 就 是 等 待 的 最 长 时 间 , 即select在timeout时间内阻塞进程。
3、Select系统调用(返回值)
      Select调用返回时,返回值有如下情况:
      1)正常情况下返回满足要求的文件描述符个数;
      2)经过了timeout等待后仍无文件满足要求,返回值为0;
      3)如果select被某个信号中断,它将返回-1并设置errno为EINTR。
      4)如果出错,返回-1并设置相应的errno。
4、Select系统调用(使用方法)
      1)将要监控的文件添加到文件描述符集
      2)调用Select开始监控
      3)判断文件是否发生变化
        系统提供了4个宏对描述符集进行操作:
        #include <sys/select.h>
        void FD_SET(int fd, fd_set *fdset)
        void FD_CLR(int fd, fd_set *fdset)
        void FD_ZERO(fd_set *fdset)
        void FD_ISSET(int fd, fd_set *fdset)
        宏FD_SET将文件描述符fd添加到文件描述符集fdset中;
        宏FD_CLR从文件描述符集fdset中清除文件描述符fd;
        宏FD_ZERO清空文件描述符集fdset;
        在调用select后使用FD_ISSET来检测文件描述符集fdset中的文件fd发生了变化。
        FD_ZERO(&fds); //清空集合
        FD_SET(fd1,&fds); //设置描述符
        FD_SET(fd2,&fds); //设置描述符
        maxfdp=fd1+1; //描述符最大值加1,假设fd1>fd2
        switch(select(maxfdp,&fds,NULL,NULL,&timeout))
                 case -1: exit(-1);break; //select错误,退出程序
                 case 0:break;
                default:
        if(FD_ISSET(fd1,&fds)) //测试fd1是否可读

5、poll方法

应用程序常常使用select系统调用,它可能会阻塞进程。这个调用由驱动的 poll 方法实现,原型为:unsigned int (*poll)(struct file *filp,poll_table *wait)

Poll设备方法负责完成:
      1)使用poll_wait将等待队列添加到poll_table中。
      2)返回描述设备是否可读或可写的掩码。
      位掩码
      1>POLLIN 设备可读
      2>POLLRDNORM数据可读
      3>POLLOUT\设备可写
      4>POLLWRNORM数据可写
      设备可读通常返回(POLLIN|POLLRDNORM )
      设备可写通常返回(POLLOUT|POLLWRNORM )
6、范例

static unsigned int mem_poll(struct file *filp,poll_table *wait)
{

struct scull_pipe *dev =filp->private_data;
unsigned int mask =0;
/* 把等待队列添加到poll_table */
poll_wait(filp,&dev->inq,wait);
/*返回掩码*/
if (有数据可读)
mask = POLLIN |POLLRDNORM;/*设备可读*/
return mask;

}

7、工作原理
      Poll方法只是做一个登记,真正的阻塞发生在select.c 中的 do_select函数。

8、实例分析

1)poll型设备驱动memdev.h源码

#ifndef _MEMDEV_H_
#define _MEMDEV_H_

#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 0   /*预设的mem的主设备号*/
#endif

#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 2    /*设备数*/
#endif

#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif

/*mem设备描述结构体*/
struct mem_dev                                     
{                                                        
  char *data;                      
  unsigned long size;

wait_queue_head_t inq;

};

#endif /* _MEMDEV_H_ */

2)Poll型设备驱动memdev.c源码

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>

#include <linux/poll.h>
#include "memdev.h"

static mem_major = MEMDEV_MAJOR;
bool have_data = false; /*表明设备有足够数据可供读*/

module_param(mem_major, int, S_IRUGO);

struct mem_dev *mem_devp; /*设备结构体指针*/

struct cdev cdev;

/*文件打开函数*/
int mem_open(struct inode *inode, struct file *filp)
{
    struct mem_dev *dev;
    
    /*获取次设备号*/
    int num = MINOR(inode->i_rdev);

if (num >= MEMDEV_NR_DEVS) 
            return -ENODEV;
    dev = &mem_devp[num];
    
    /*将设备描述结构指针赋值给文件私有数据指针*/
    filp->private_data = dev;
    
    return 0; 
}

/*文件释放函数*/
int mem_release(struct inode *inode, struct file *filp)
{
  return 0;
}

/*读函数*/
static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
  unsigned long p =  *ppos;
  unsigned int count = size;
  int ret = 0;
  struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/

/*判断读位置是否有效*/
  if (p >= MEMDEV_SIZE)
    return 0;
  if (count > MEMDEV_SIZE - p)
    count = MEMDEV_SIZE - p;
    
  while (!have_data) /* 没有数据可读,考虑为什么不用if,而用while */
  {
        if (filp->f_flags & O_NONBLOCK)
            return -EAGAIN;
    
    wait_event_interruptible(dev->inq,have_data);
  }

/*读数据到用户空间*/
  if (copy_to_user(buf, (void*)(dev->data + p), count))
  {
    ret =  - EFAULT;
  }
  else
  {
    *ppos += count;
    ret = count;
   
    printk(KERN_INFO "read %d bytes(s) from %d\n", count, p);
  }
  
  have_data = false; /* 表明不再有数据可读 */
  /* 唤醒写进程 */
  return ret;
}

/*写函数*/
static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
  unsigned long p =  *ppos;
  unsigned int count = size;
  int ret = 0;
  struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/
  
  /*分析和获取有效的写长度*/
  if (p >= MEMDEV_SIZE)
    return 0;
  if (count > MEMDEV_SIZE - p)
    count = MEMDEV_SIZE - p;

/*从用户空间写入数据*/
  if (copy_from_user(dev->data + p, buf, count))
    ret =  - EFAULT;
  else
  {
    *ppos += count;
    ret = count;
    
    printk(KERN_INFO "written %d bytes(s) from %d\n", count, p);
  }
  
  have_data = true; /* 有新的数据可读 */
    
    /* 唤醒读进程 */
    wake_up(&(dev->inq));

return ret;
}

/* seek文件定位函数 */
static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)

    loff_t newpos;

switch(whence) {
      case 0: /* SEEK_SET */
        newpos = offset;
        break;

case 1: /* SEEK_CUR */
        newpos = filp->f_pos + offset;
        break;

case 2: /* SEEK_END */
        newpos = MEMDEV_SIZE -1 + offset;
        break;

default: /* can't happen */
        return -EINVAL;
    }
    if ((newpos<0) || (newpos>MEMDEV_SIZE))
        return -EINVAL;
        
    filp->f_pos = newpos;
    return newpos;

}
unsigned int mem_poll(struct file *filp, poll_table *wait)
{
    struct mem_dev  *dev = filp->private_data; 
    unsigned int mask = 0;
    
   /*将等待队列添加到poll_table表中 */
    poll_wait(filp, &dev->inq,  wait);

if (have_data)

mask |= POLLIN | POLLRDNORM;  /* readable */

return mask;
}

/*文件操作结构体*/
static const struct file_operations mem_fops =
{
  .owner = THIS_MODULE,
  .llseek = mem_llseek,
  .read = mem_read,
  .write = mem_write,
  .open = mem_open,
  .release = mem_release,
  .poll = mem_poll,
};

/*设备驱动模块加载函数*/
static int memdev_init(void)
{
  int result;
  int i;

dev_t devno = MKDEV(mem_major, 0);

/* 静态申请设备号*/
  if (mem_major)
    result = register_chrdev_region(devno, 2, "memdev");
  else  /* 动态分配设备号 */
  {
    result = alloc_chrdev_region(&devno, 0, 2, "memdev");
    mem_major = MAJOR(devno);
  }  
  
  if (result < 0)
    return result;

/*初始化cdev结构*/
  cdev_init(&cdev, &mem_fops);
  cdev.owner = THIS_MODULE;
  cdev.ops = &mem_fops;
  
  /* 注册字符设备 */
  cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);
   
  /* 为设备描述结构分配内存*/
  mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);
  if (!mem_devp)    /*申请失败*/
  {
    result =  - ENOMEM;
    goto fail_malloc;
  }
  memset(mem_devp, 0, sizeof(struct mem_dev));
  
  /*为设备分配内存*/
  for (i=0; i < MEMDEV_NR_DEVS; i++) 
  {
        mem_devp[i].size = MEMDEV_SIZE;
        mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
        memset(mem_devp[i].data, 0, MEMDEV_SIZE);
  
      /*初始化等待队列*/
     init_waitqueue_head(&(mem_devp[i].inq));
     //init_waitqueue_head(&(mem_devp[i].outq));
  }
   
  return 0;

fail_malloc: 
  unregister_chrdev_region(devno, 1);
  
  return result;
}

/*模块卸载函数*/
static void memdev_exit(void)
{
  cdev_del(&cdev);   /*注销设备*/
  kfree(mem_devp);     /*释放设备结构体内存*/
  unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/
}

MODULE_AUTHOR("David Xie");
MODULE_LICENSE("GPL");

module_init(memdev_init);

module_exit(memdev_exit);

3)测试程序app-read.c源码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h> 
 
int main() 

    int fd; 
    fd_set rds;    //声明描述符集合
    int ret; 
    char Buf[128]; 
     
    /*初始化Buf*/ 
    strcpy(Buf,"memdev is char dev!"); 
    printf("BUF: %s\n",Buf); 
     
    /*打开设备文件*/ 
    fd = open("/dev/memdev0",O_RDWR); 
    
    FD_ZERO(&rds);   //清空描述符集合
    FD_SET(fd, &rds); //设置描述符集合
 
    /*清除Buf*/ 
    strcpy(Buf,"Buf is NULL!"); 
    printf("Read BUF1: %s\n",Buf);

ret = select(fd + 1, &rds, NULL, NULL, NULL);//调用select()监控函数
    if (ret < 0)  
    {
        printf("select error!\n");
        exit(1);
    }
    if (FD_ISSET(fd, &rds))   //测试fd1是否可读  
        read(fd, Buf, sizeof(Buf));             
     
    /*检测结果*/ 
    printf("Read BUF2: %s\n",Buf);
    
    close(fd); 
     
    return 0;     
}

Linux高级字符设备驱动 poll方法(select多路监控原理与实现)的更多相关文章

  1. Linux 设备驱动--- Poll 方法 --- Select【转】

    转自:http://blog.csdn.net/yikai2009/article/details/8653842 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] Sele ...

  2. 【转】Linux高级字符设备之Poll操作

    原文网址:http://www.cnblogs.com/geneil/archive/2011/12/04/2275559.html 在用户程序中,select()和poll()也是与设备阻塞与非阻塞 ...

  3. Linux高级字符设备驱动

    转载:http://www.linuxidc.com/Linux/2012-05/60469p4.htm 1.什么是Poll方法,功能是什么? 2.Select系统调用(功能)      Select ...

  4. linux 高级字符设备驱动 ioctl操作介绍 例程分析实现【转】

    转自:http://my.oschina.net/u/274829/blog/285014 1,ioctl介绍 ioctl控制设备读写数据以及关闭等. 用户空间函数原型:int ioctl(int f ...

  5. 3.字符设备驱动------Poll机制

    1.poll情景描述 以之前的按键驱动为例进行说明,用阻塞的方式打开按键驱动文件/dev/buttons,应用程序使用read()函数来读取按键的键值. ) { read(fd, &key_v ...

  6. linux学习--字符设备驱动

    目录 1.字符设备驱动抽象结构 2.设备号及设备节点 2.1 设备号分配与管理 2.2 设备节点的生成 3.打开设备文件 linux驱动有基本的接口进行注册和卸载,这里不再做详细说明,本文主要关注li ...

  7. Linux实现字符设备驱动的基础步骤

    Linux应用层想要操作kernel层的API,比方想操作相关GPIO或寄存器,能够通过写一个字符设备驱动来实现. 1.先在rootfs中的 /dev/ 下生成一个字符设备.注意主设备号 和 从设备号 ...

  8. linux driver ------ 字符设备驱动 之 “ 创建设备节点流程 ”

    在字符设备驱动开发的入门教程中,最常见的就是用device_create()函数来创建设备节点了,但是在之后阅读内核源码的过程中却很少见device_create()的踪影了,取而代之的是device ...

  9. Linux LED字符设备驱动

    // 申请IO资源 int gpio_request(unsigned gpio, const char *label); // 释放IO资源 void gpio_free(unsigned gpio ...

随机推荐

  1. 如何从angular2中的url获取查询参数?

    问题描述 我使用angular2.0.0-beta.7.当组件在像”/path?query=value1″这样的路径上加载时,它被重定向到”/path”.为什么删除了GET参数?如何保留参数? 路由器 ...

  2. jsack

    https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr016.html#BABGHEJB

  3. SQL 中 replace 替换字符串中的字符 ''

    update CfmRcd set reconsource=replace(reconsource,'''',''), cmffile =replace(cmffile,'''',''), cfmda ...

  4. linux怎么关闭iptables linux如何关闭防火墙

    Linux系统下面自带了防火墙iptables,iptables可以设置很多安全规则.但是如果配置错误很容易导致各种网络问题,那么如果要关闭禁用防火墙怎么操作呢,咗嚛本经验以centos系统为例演示如 ...

  5. [Windows Azure] How to use the Windows Azure Blob Storage Service in .NET

    How to use the Windows Azure Blob Storage Service in .NET version 1.7 version 2.0 This guide will de ...

  6. Beginning SDL 2.0(4) YUV加载及渲染

    本文主要内容是基于的“Beginning SDL 2.0(3) SDL介绍及BMP渲染”(以下简称BS3)基础上,将BMP加载及渲染修改为YUV420或I420的原始视频格式.阅读完本部分内容相信你可 ...

  7. c#去除字符串中的空格,回车,换行符,制表符

    string l_strResult = str.Replace("\n", "").Replace(" ","").R ...

  8. LeetCode: Balanced Binary Tree 解题报告

    Balanced Binary Tree Given a binary tree, determine if it is height-balanced. For this problem, a he ...

  9. 在Mac上安装与使用mitmproxy

    [本文出自天外归云的博客园] 介绍 Mitmproxy是一款支持HTTP(S)的中间人代理工具.不同于Fiddler2,burpsuite等类似功能工具,mitmproxy可在终端下运行,并且支持编写 ...

  10. linespace函数

    numpy.linspace numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)[source] ...