支持异步通知的globalfifo驱动

前言

驱动程序运行在内核空间中,应用程序运行在用户空间中,两者是不能直接通信的。但在实际应用中,在设备已经准备好的时候,我们希望通知用户程序设备已经ok,用户程序可以读取了,这样应用程序就不需要一直查询该设备的状态,从而节约了资源,这就是异步通知。好,那下一个问题就来了,这个过程如何实现呢?简单,两方面的工作。

一 驱动方面

  1. 在设备抽象的数据结构中增加一个struct fasync_struct的指针
  2. 实现设备操作中的fasync函数,这个函数很简单,其主体就是调用内核的fasync_helper函数。
  3. 在需要向用户空间通知的地方(例如中断中)调用内核的kill_fasync函数。
  4. 在驱动的release方法中调用前面定义的fasync函数
    其中fasync_helper和kill_fasync都是内核函数,我们只需要调用就可以了。在1中定义的指针是一个重要参数,fasync_helper和kill_fasync会使用这个参数。

二 应用层方面

  1. 利用signal或者sigaction设置SIGIO信号的处理函数
  2. fcntl的F_SETOWN指令设置当前进程为设备文件owner
  3. fcntl的F_SETFL指令设置FASYNC标志
    完成了以上的工作的话,当内核执行到kill_fasync函数,用户空间SIGIO函数的处理函数就会被调用了。

三代码方面

1.增加异步通知后的globalfifo设备结构体

struct globalfifo_dev
   {

    struct cdev cdev;                            //cdev结构体

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

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

    struct mutex mutex;                          //互斥锁

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

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

    wait_queue_head_t w_wait;                    //阻塞写用的等待队列头

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

   };

2.支持异步通知的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);
}

3支持异步通知的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);                               //定义等待队列

   mutex_ lock(& dev-> mutex);                                    //取得互斥锁

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

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

   //等待fifo未满

   while(dev->current_len==GLOBALFIFO_SIZE){

      if(filp->f_flags&O_NOBLOCK){  
      //如果是非阻塞访问

         ret=-EAGAIN;

         goto out;

      }

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

     mutex_unlock(& dev-> mutex);
     //up(&dev->sem);

     schedule();

      if(signal_pending(current){

           ret=-ERESTARTSYS;

           goto out2;

       }
     mutex_lock(& dev-> mutex);
     //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;

     printk( KERN_ INFO "written %d bytes( s), current_ len:% d\ n", count, 38 dev-> current_ len);

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

    //产生异步读信号

    if(dev->async_queue){

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

       printk( KERN_ DEBUG "%s kill SIGIO\ n", __func__);
    }

   ret=count;

 }

   out:mutex_unlock(& dev-> mutex);//up(&dev->sem);

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

   set_current_state(TASK_RUNNING);

   return ret;

}

4用globalfifo_fasync()函数将文件从异步通知列表中删除

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

 {

   globalfifo_fasync(-1,filp,0);

   return 0;

 } 

四验证结果

监控 globalfifo 异步 通知 信号 的 应用 程序

static void signalio_handler(int signum)
{ 
   printf("receive a signal from globalfifo, signalnum:%d\n",signum);
}

void main( void) 
{  
    int fd, oflags; 
    fd = open("/ dev/ globalfifo", O_ RDWR, S_ IRUSR | S_ IWUSR); 
    if (fd != -1){ 
    //启动信号驱动机制,将SIGIO信号同input_handler函数关联起来,一旦产生SIGIO信号,就会执行input_handler 
    signal(SIGIO, input_handler);    

    //STDIN_FILENO是打开的设备文件描述符,F_SETOWN用来决定操作是干什么的,getpid()是个系统调用,
    //功能是返回当前进程的进程号,整个函数的功能是STDIN_FILENO设置这个设备文件的拥有者为当前进程。
    fcntl(STDIN_FILENO, F_SETOWN, getpid());    

    //得到打开文件描述符的状态
    oflags = fcntl(STDIN_FILENO, F_GETFL);

    //设置文件描述符的状态为oflags | FASYNC属性,一旦文件描述符被设置成具有FASYNC属性的状态,
    //也就是将设备文件切换到异步操作模式。这时系统就会自动调用驱动程序的fasync方法。
    fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);  

    while (1){ 
    sleep( 100); 
    } else {
     printf(" device open failure\ n");
   }
}
(注:上面有一行解释为:这时系统就会自动调用驱动程序的fasync方法,我的理解是:这个时候我们联系一下后面的驱动,我们会发现在驱动层,是在file_operation中对应的fasync的函数调用,这是,会首先调fasync对应的my_fasync函数,而后者会进行  将该设备登记到fasync_queue队列中去,为后续做准备)

以下是几点说明:
1 两个函数的原型
int fasync_helper(struct inode *inode, struct file *filp, int mode, struct fasync_struct **fa);
一个”帮忙者”, 来实现 fasync 设备方法. mode 参数是传递给方法的相同的值, 而 fa 指针指向一个设
备特定的 fasync_struct *
void kill_fasync(struct fasync_struct *fa, int sig, int band);
如果这个驱动支持异步通知, 这个函数可用来发送一个信号到登记在 fa 中的进程.

2.fasync_helper 用来向等待异步信号的设备链表中添加或者删除设备文件, kill_fasync被用来通知拥有相关设备的进程. 它的参数是被传递的信号(常常是 SIGIO)和 band, 这几乎都是 POLL_IN[25](但是这可用来发送”紧急”或者带外数据, 在网络代码里).

猜你喜欢

转载自blog.csdn.net/feike24/article/details/78524521
今日推荐