异步通知是设备状态改变后主动通知应用程序,这样应用程序就不需要阻塞或查询设备了。应用通过信号来处理内核的异步通知,上次使用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;
}