• globalfifo设备驱动


    把globalmem中的全局内存变成一个FIFO,只有当FIFO中有数据的时候(即有进程把数据写到这个FIFO而且没有被读进程读空),读进程才能把数据读出,而且读取后的数据会从globalmem的全局内存中被拿掉;只有当fifo非满时(即还有一些空间未被写,或写满后被读进程读出了数据),写进程才能写入数据。现在将globalmem重命名为“globalfifo",在globalfifo中,读fifo将唤醒写fifo,写fifo也将唤醒读fifo.

    (一) 支持阻塞操作的globalfifo设备驱动

       在globalfifo设备结构体上需要添加两个等待队列头,分别对应于读和写。

       struct globalfifo_dev

       {

        struct cdev cdev;                            //cdev结构体

        unsigned int current_len;                    //fifo有效数据长度

        unsigned char mem[GLOBALFIFO_SIZE];          //全局内存

        struct semaphore sem;                        //并发控制用的信号量

        wait_queue_head_t r_wait;                    //阻塞读用的等待队列头

        wait_queue_head_t w_wait;                    //阻塞写用的等待dui lie

       };

        等待队列需要在设备驱动模块加载函数中调用init_waitqueue_head()被初始化。

        int gloaalfifo_init(void)

        {

         int ret;

         dev_t devno=MKDEV(globalfifo_major,0);

         //申请设备号

        if(globalfifo_major)

          ret=register_chrdev_region(devno,1,"globalfifo");

        else

          {          //动态申请设备号

           ret=alloc_chrdev_region(&devno,0,1,"globalfifo");

           globalfifo_major=MAJOR(devno);

          }

         if(ret<0)

            return ret;

           //动态申请设备结构体的内存

           globalfifo_devp=kmalloc(sizeof(struct globalfifo_dev),GFP_KERNEL);

           if(!globalfifo_devp)

           {

             ret=-ENOMEM;

             goto fail_malloc;

           }

           memset(globalfifo_devp,0,sizeof(struct_globalfifo_dev));

           globalfifo_setup_cdev(globalfifo_devp,0);

           init_MUTEX(&globalfifo_devp->sem);                 //初始化信号量

           init_waitqueue_head(&globalfifo_devp->r_wait);      //初始化读等待队列

           init_waitqueue_head(&globalfifo_devp->w_wait);      //初始化写等待队列

           return 0;

           fail_malloc:unregister_chrdev_region(devno,1);

           return ret;

     }

    设备驱动读函数中应该增加等待globalfifo_devp->w_wait被唤醒的语句;而在写操作中唤醒globalfifo_devp->r_wait.

        static ssize_t globalfifo_read(struct file *filp,char_user *buf,size_t count,loff_t *ppos)

        {

          int ret;

          struct globalfifo_dev *dev=filp->private_data;       //获得设备结构体指针

          DECLARE_WAITQUEUE(wait,current);                     //定义等待队列

          down(&dev->sem);                                     //获得信号量

          add_wait_queue(&dev->r_wait,&wait);                  //进入读等待队列头

          //等待fifo非空

          while(dev->current_len==0)

           {

            if(filp->f_flags &o_nonblock)

            {

              ret=-EAGAIN;

              goto out;

            }

           _set_current_state(TASK_INTERRUPTIBLE);           //改变进程状态为睡眠

          up(&dev->sem);

          schedule();                                        //调度其他进程执行

          if(signal_pending(current))                        //如果是因为信号唤醒

          {

           ret=-ERESTARTSYS;

           goto out2;

          }

           down(&dev->sem);

         }

         //拷贝到用户空间

         if(count > dev->current_len)

           count=dev->current_len;

         if(copy_to_user(buf,dev->mem,count)

           {

             ret=-EFAULT;

             goto out;

            }

            else

                {

                  memcpy(dev->mem,dev->mem+count,dev->current_len-count);         //fifo数据前移

                  dev->current_len-=count;                                        //有效数据长度减少。

                 printk(KERN_INFO "read %d bytes(s),current_len:%d ",count,dev->current_len);

                 wake_up_interruptible(&dev->w_wait);                             //唤醒写等待队列

                 ret=count;

               }

               out:up(&dev->sem);                                                 //释放信号量

               out2:remove_wait_queue(&dev->w_wait,&wait);                        //移除等待队列

               set_current_state(TASK_RUNNING);

               return ret;

         }

         //globalfifo写操作

        static ssize_t globalfifo_write(struct file *filp,const char _ _user *buf,size_t count, loff_t *ppos)

       {

        struct globalfifo_dev *dev=filp->private_data;          //获得设备结构体指针

        int ret;

        DECLARE_WAITQUEUE(&dev->w_wait,&wait);                  //进入写等待队列

        down(&dev->sem);                                        //获得信号量

        add_wait_queue(&dev->w_wait,&wait);                     //进入写等待队列头

        //等待fifo非满

        while(dev->current_len=GLOBALFIFO_SIZE)

        {

          if(filp->f_flags &O_NONBLOCK )

          {

                //如果是非阻塞访问

               ret=-EAGAIN;

               goto out;

           }

         _set_current_state(TASK_INTERRUPTIBLE);  //改变进程状态为睡眠

        up(&dev->sem);                   

        schedule();                    //调度其它进程执行

        if(signal_pending(current))

        {

               //如果是因为信号唤醒

          ret=-ERESTARTSYS;

          goto out2;

         }

        down(&dev->sem);

      }

      if(count>GLOBALFIFO_SIZE-dev->current_len)

      count=GLOBALFIFO_SIZE-dev->current_len;

      if(copy_from_user(dev->mem+dev->current_len,buf,count))

      {

       ret=-EFAULT;

       goto out;

       }

      else

         {
         dev->current_len+=count;

         printk(KERNEL_INFO"written %d byte(S),current_len:%d ",count,dev->current_len);

         wake_up_interruptible(&dev->r_wait);     //唤醒读等待队列

         ret=count;

        }

        out:up(&dev->sem);           

        out2:remove_wait_queue(&dev->w_wait,&wait);

        set_current_state(TASK_RUNNING);

        return ret;

      }

                                             (二)支持轮询操作的globalfifo驱动

     在global的poll()函数中,首先将设备结构体中的r_wait和w_wait等待队列添加到等待列表,然后通过判断dev->current_len是否等于0来获得设备的可读状态,通过判断dev->current_len是否等于GLOBALFIFO_SIZE来获得设备的可写状态。

       global设备驱动的poll()函数

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

                                {

                                 unsigned int mask=0;

                                 struct globalfifo_dev *dev=filp->private_data;     //获得设备结构体指针

                                 down(&dev-sem);                                    //获得信号量

                                 poll_wait(filp,&dev->r_wait,wait);

                                 poll_wait(filp,&dev->w_wait,wait);                  

                                 //fifo非空

                                 if(dev->current_len!=0)

                                   mask|=POLLIN|POLLRDNORM;             //标示数据可获得

                                 if(dev->current_len!=GLOBALFIFO_SIZE)

                                   mask|=POLLOUT|POLLWRNORM;            //标示数据可写入

                              up(&dev->sem);          //释放信号量

                              return mask;   

                  }

         注意要把globalfifo_poll赋给globalfifo_fops的poll成员。

                                           (三) 支持异步通知的globalfifo设备驱动

     首先应该将异步通知的结构体指针加入到globalfifo设备的结构体里面去。

      

    struct globalfifo_dev

       {

        struct cdev cdev;                            //cdev结构体

        unsigned int current_len;                    //fifo有效数据长度

        unsigned char mem[GLOBALFIFO_SIZE];          //全局内存

        struct semaphore sem;                        //并发控制用的信号量

        wait_queue_head_t r_wait;                    //阻塞读用的等待队列头

        wait_queue_head_t w_wait;                    //阻塞写用的等待dui lie

        struct fasync_struct *async_queue;           //异步结构体指针,用于读

       };

        支持异步通知的globalfifo设备驱动fasync()函数

            static int globalfifo_fasync(int fd,struct file *filp,int mode)

            {

              struct globalfifo_dev *dev=filp->private_data;

              return fasync_helper(fd,filp,mode,&dev->async_queue);

            }

      支持异步通知的globalfifo设备驱动写函数

            static ssize_t globalfifo_write(struct file *filp,const char _user *buf,size_t count,loff_t *ppos)

            {

              struct globalfifo_dev *dev=filp->private_data;                 //获得设备结构体指针

              int ret;

              DECLARE_WAITQUEUE(wait,current);                               //定义等待队列

              down(&dev->sem);                                               //取得信号量

              add_wait_queue(&dev->w_wait,&wait);                            //进入写等待队列

              //等待fifo未满

            if(dev->current_len==GLOBALFIFO_SIZE)

               {

                 if(filp->f_flags&O_NOBLOCK)

                 {           //如果是非阻塞访问

                   ret=-EAGAIN;

                   goto out;

                 }

                 _set_current_state(TASK_INTERRUPTIBLE)                 //改变进程状态为睡眠

                 up(&dev->sem);

                schedule();

                if(signal_pending(current)

                  ret=-ERESTARTSYS;

                  goto out2;

               }

             down(&dev->sem);

       }

      //从用户空间拷贝到内核空间

       if(count >GLOBALFIFO_SIZE- dev->current_len)

        count=GLOBALFIFO_SIZE-dev->current_len;

       if(copy_form_user(dev->mem+dev->current_len,buf,count))

        {
         ret=-EFAULT;

         goto out;

      }

       else

           {

               dev->current_len+=count;

               wake_up_interruptible(&dev->r-wait);      //唤醒读等待队列

            }

         //产生异步读信号

              if(dev->async_queue)

                kill_fasync(&dev->async_queue,SIGIO,POLL_IN);

                 ret=count;

     }

     out:up(&dev->sem);

     out2:remove_wait_queue(&dev->w_wait,&wait);

    set_current_state(TASK_RUNNING);

    return ret;

    }

    最后记得在release()函数里用globalfifo_fasync()函数将文件从异步通知列表中删除

     int globalfifo_release(struct inode *inode,struct file *filp)

     {

       globalfifo_fasync(-1,filp,0);

       return 0;

     }

                    

  • 相关阅读:
    多态
    抽象类和接口
    面向对象3
    类的继承 设计模式
    面向对象2
    面向对象
    复习
    对json的简单认识
    关于AJAX
    PHP配置开发环境
  • 原文地址:https://www.cnblogs.com/God-boy1/p/3672057.html
Copyright © 2020-2023  润新知