支持异步通知的globalfifo驱动

来源:互联网 发布:软件质量控制方法 编辑:程序博客网 时间:2024/05/15 07:52

前言

驱动程序运行在内核空间中,应用程序运行在用户空间中,两者是不能直接通信的。但在实际应用中,在设备已经准备好的时候,我们希望通知用户程序设备已经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](但是这可用来发送”紧急”或者带外数据, 在网络代码里).

原创粉丝点击