linux驱动学习之异步通知

     异步通知是设备状态改变后主动通知应用程序,这样应用程序就不需要阻塞或查询设备了。应用通过信号来处理内核的异步通知,上次使用poll select来查询设备的可读状态,下面这个例子类似,不同的是当设备有数据时主动通知应用去读数据。

    应用的C代码很简单,主要设置对信号的处理方式,内核有数据时会收到SIGIO信号,应用会自动调用signal设置的函数去读数据。


 main.c

#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
unsigned char rdBuf[1024];  
int fd;
void hand_signal(int sign)
{
   if(sign==SIGIO)
   {
     if (read(fd,rdBuf,1024)>0)    
       printf("read data:%s\n",rdBuf);  
   }
}



int main (int *argc,char**argv)
{
    int fd,flags;
    /*处理SIGIO信号 驱动例子中收到数据时向应用发送的也是SIGIO信号*/
    signal(SIGIO,hand_signal);
    fd=open("/dev/moduledev60",O_RDWR);
    if(fd<0)
    {
	printf("open file error\n");	
	return -1;
    }

    /*告诉驱动信号发送给谁 第三个参数传的是进程号*/
    fcntl(fd, F_SETOWN, getpid());
    
    /*设置FASYNC 执行后驱动的fasync方法被调用*/
    flags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, flags | FASYNC);

    while(1);
}



驱动主要代码,读操作只返回最后一次写入的字符串,写操作后向设置的进程发送SIGIO信号

struct file_operations ops=
{
   .owner=THIS_MODULE  ,
   .read =fileops_read ,
   .write=fileops_write,
   .fasync=fileops_fasync,   
   .close=fileops_release
};


int fileops_release(struct inode *inode,struct file *filp)
{
  printk(KERN_ALERT "fileops_release\n");
  fileops_fasync(-1,filp,0);            //第三个参数为0时,第一个参数未用,用来释放申请的内存 
  return 0;
}

ssize_t fileops_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
  unsigned int len;
  if(rdFlag==0) return 0;  
  len=strlen(rdBuf);
  copy_to_user(buff,rdBuf,len);
  rdFlag=0;
  return len;
}

ssize_t fileops_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp)
{
  copy_from_user(rdBuf,buff,count);
  rdBuf[count]=0;
  rdFlag=1;
  kill_fasync(&fasync_queue,SIGIO ,POLL_IN);  //有数据写入 向设置的进程发送SIGIO信号
  printk(KERN_ALERT "signal %d \n",SIGIO);
  return count;
}

int fileops_fasync (int fd, struct file *filp, int mode)
{
   int res=fasync_helper(fd,filp,mode,&fasync_queue); //初始化fasync_queue
   printk(KERN_ALERT "filp %x\n",(int)filp);
   if(res<0) return res;
   else return 0;
}


测试结果

先编译加载驱动
[root@localhost ctest]# insmod moddev.ko

运行应用
[root@localhost ctest]# ./main 打开另一终端向设备写入数据
[root@localhost ctest]# echo 111 > /dev/moduledev60
应用输出,每次数据写入数据后,驱动通知应用读取设备
[root@localhost ctest]# ./main 
read data:111


附fasync_helper源码
/*

 * fasync_helper() is used by some character device drivers (mainly mice)
 * to set up the fasync queue. It returns negative on error, 0 if it did
 * no changes and positive if it added/deleted the entry.
 */
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
{       /*这个函数维护一个线性表,用于创建和释放异步通知结构链表*/
	struct fasync_struct *fa, **fp;
	struct fasync_struct *new = NULL;
	int result = 0;

	if (on) {
		new = kmem_cache_alloc(fasync_cache, GFP_KERNEL); //申请异步通知结构变量
 		if (!new)
			return -ENOMEM;
	}
	write_lock_irq(&fasync_lock);
	for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
		if (fa->fa_file == filp) {
			if(on) {//已经存在filp节点
				fa->fa_fd = fd;
				kmem_cache_free(fasync_cache, new);
			} else {//释放找到的节点
				*fp = fa->fa_next;
				kmem_cache_free(fasync_cache, fa);
				result = 1;
			}
			goto out;
		}
	}

	if (on) {//将申请的节点加入链表
		new->magic = FASYNC_MAGIC;
		new->fa_file = filp;
		new->fa_fd = fd;
		new->fa_next = *fapp;
		*fapp = new;
		result = 1;
	}
out:
	write_unlock_irq(&fasync_lock);
	return result;
}


你可能感兴趣的:(linux驱动学习之异步通知)