zoukankan      html  css  js  c++  java
  • 驱动支持select函数

    I/O多路转接至今还不是 POSIX的组成部分。SVR4和 4.3 + BSD都提供select函数以执行I/O多路转接。poll函数只由SVR4 提供。SVR4 实际上用poll实现select。I/O多路转接的基本思想是:先构造一张有关描述符的表,然后调用一个函数,它要到这些描述符中的一个已准备好进行 I/O时才返回。在返回时,它告诉进程哪一个描述符已准备好可以进行 I/O。

    I/O多路转接在服务器端用的比较多,可以同时处理多个连接的接入,但是也有缺陷,貌似只能接受1024个接入,因此现在又了epoll,当然这不是讨论的重点了。

    select的函数原型为:

    int select(int  numfds,  fd_set  *readfds,  fd_set  *writefds,  fd_set  *exceptfds,  struct  timeval  *timeout);

    其中 readfds、writefds、exceptfds 分别是被 select()监视的读、写和异常处理的文件描述符集合,numfds 的值是需要检查的号码最高的文件描述符加 1。timeout 参数是一个指向 struct timeval类型的指针,它可以使select()在等待timeout时间后若没有文件描述符准备好则返回。

    struct timeval 数据结构为:  

    struct timeval  

    {  

        int tv_sec; /* seconds */  

        int tv_usec; /* microseconds */  

    };

    除此之外,我们还将使用下列 API:

    FD_ZERO(fd_set *set)――清除一个文件描述符集;  

    FD_SET(int fd,fd_set *set)――将一个文件描述符加入文件描述符集中;  

    FD_CLR(int fd,fd_set *set)――将一个文件描述符从文件描述符集中清除;  

    FD_ISSET(int fd,fd_set *set)――判断文件描述符是否被置位。

    select 用于查询设备的状态,以便用户程序获知是否能对设备进行非阻塞的访问,需要设备驱动程序中的poll 函数支持。 驱动程序中 poll 函数中最主要用到的一个 API 是 poll_wait,其原型如下:

    void poll_wait(struct file *filp, wait_queue_heat_t *queue, poll_table * wait);

    poll_wait 函数所做的工作是把当前进程添加到 wait 参数指定的等待列表(poll_table)中。

    需要说明的是,poll_wait 函数并不阻塞,程序中 poll_wait(filp, &outq, wait)这句话的意思并不是说一直等待 outq 信号量可获得,真正的阻塞动作是上层的 select/poll 函数中完成的。select/poll 会在一个循环中对每个需要监听的设备调用它们自己的 poll 支持函数以使得当前进程被加入各个设备的等待列表。若当前没有任何被监听的设备就绪,则内核进行调度(调用 schedule)让出 cpu 进入阻塞状态,schedule 返回时将再次循环检测是否有操作可以进行,如此反复;否则,若有任意一个设备就绪,select/poll 都立即返回。

    针对前面的文章的程序,我们在驱动中加入对poll的支持,程序改进为:

    /*  global_poll.c */

    #include <linux/module.h>

    #include <linux/init.h>

    #include <linux/fs.h>

    #include <asm/uaccess.h>

    #include <linux/wait.h>

    #include <linux/semaphore.h>

    #include <linux/device.h>

    #include <linux/cdev.h>

    #include <linux/sched.h>

    #include <linux/poll.h>

    MODULE_LICENSE("GPL");

    #define init_MUTEX(LOCKNAME) sema_init(LOCKNAME,1)

    #define DEVICE_NAME "CDEV_ZHU"

    static struct class *cdev_class;

    struct cdev dev_c;

    dev_t dev;

    static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);

    static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);

    static unsigned int globalvar_poll(struct file *filp, poll_table *wait);

    struct file_operations globalvar_fops =

    {

    read: globalvar_read,

    write: globalvar_write,

    poll: globalvar_poll,

    };

    static int global_var = 0;

    static struct semaphore sem;

    static wait_queue_head_t outq;

    static int flag = 0;

    static int __init globalvar_init(void)

    {

    int ret,err;

    ret = alloc_chrdev_region(&dev,0,1,DEVICE_NAME) ;

    if (ret)

    {

    printk("globalvar register failure");

    }

    else

    {

    cdev_init(&dev_c,&globalvar_fops);

    err = cdev_add(&dev_c,dev,1);

    if(err)

    {

    printk(KERN_NOTICE "error %d adding FC_dev\n",err);

    unregister_chrdev_region(dev, 1);

    return err;

    }

    else

    {

    printk("device register success! \n");

    }

    cdev_class = class_create(THIS_MODULE,DEVICE_NAME);

    if(IS_ERR(cdev_class))

    {

    printk("ERR:cannot create a cdev_class\n");

    unregister_chrdev_region(dev, 1);

    return -1;

    }

    device_create(cdev_class, NULL, dev, 0, DEVICE_NAME);

    init_MUTEX(&sem);

    init_waitqueue_head(&outq);

    }

    return ret;

    }

    static void __exit globalvar_exit(void)

    {

    device_destroy(cdev_class,dev);

    class_destroy(cdev_class);

    unregister_chrdev_region(dev,1);

    printk("device ");

    }

    static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)

    {

    if (wait_event_interruptible(outq, flag != 0))

    {

    return - ERESTARTSYS;

    }

    if (down_interruptible(&sem))

    {

    return - ERESTARTSYS;

    }

    flag = 0;

    if (copy_to_user(buf, &global_var, sizeof(int)))

    {

    up(&sem);

    return - EFAULT;

    }

    up(&sem);

    return sizeof(int);

    }

    static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)

    {

    if (down_interruptible(&sem))

    {

    return - ERESTARTSYS;

    }

    if (copy_from_user(&global_var, buf, sizeof(int)))

    {

    up(&sem);

    return - EFAULT;

    }

    up(&sem);

    flag = 1;

    wake_up_interruptible(&outq);

    return sizeof(int);

    }

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

    {

    unsigned int mask = 0;

    poll_wait(filp, &outq, wait);

    if(flag != 0)

    {

    mask |= POLLIN | POLLRDNORM;

    }

    return mask;

    }

    module_init(globalvar_init);

    module_exit(globalvar_exit);

    下面的应用程序等待/dev/globalvar_poll 可读,但是设置了 5 秒的等待超时,若超过 5 秒仍然没有数据可读,则输出“No data within 5 seconds”:

    /* poll_read.c */

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int main()
    {
        int fd, num;
        fd_set rfds;
        struct timeval tv;
        fd = open("/dev/CDEV_ZHU", O_RDWR, S_IRUSR | S_IWUSR);
        if (fd != -1)
        {
            while (1)
            {
                //查看 globalvar 是否有输入
                  FD_ZERO(&rfds);
                FD_SET(fd, &rfds);
    
                //设置超时时间为 5s
                tv.tv_sec = 5;
                tv.tv_usec = 0;
    
                select(fd + 1, &rfds, NULL, NULL, &tv); //注:select 的文件描述符要加"1",最后一个参数设为“NULL”,则会永久等待
                //数据是否可获得?
                  if (FD_ISSET(fd, &rfds))
                {
                      read(fd, &num, sizeof(int));               
                      printf("The globalvar is %d\n", num);
    
                    //输入为 0,退出
                        if (num == 0)
                     {
                        close(fd);
                        break;
                     }
                }
                else
                    printf("No data within 5 seconds.\n");
            }
    
        }
        else
        {
            printf("device open failure\n");
        }
       return 0;
    }

    还需要另外一个应用程序进行写入,可以使用上上篇文章“死锁”中的“globalvar_write.c”作为写入。测试结果如下:

  • 相关阅读:
    深入浅出聊优化:从Draw Calls到GC
    关于Unity中植物树木烘焙后没有影子的解决方法
    Marvelous Designer 服装设计与模拟
    DAZ studio 4.9基础
    在下载SOPC代码的过程中遇到的一些错误
    开发工程师人生之路
    简易信号发生器的设计
    HDU A Simple Math Problem (矩阵快速幂)
    HDU Queuing (递推+矩阵快速幂)
    POJ 3233 Matrix Power Series(矩阵快速幂)
  • 原文地址:https://www.cnblogs.com/zhuyp1015/p/2527118.html
Copyright © 2011-2022 走看看