Linux系统中的fasync用于设备驱动程序与用户空间之间的异步通信。它允许设备驱动程序通知用户空间的进程,当设备状态发生变化时,通过发送SIGIO信号来告知进程。
具体来说,设备驱动程序可以调用fasync_helper()函数来注册一个或多个进程,当设备状态发生变化时,内核会自动发送SIGIO信号给这些进程。而用户空间进程可以通过调用fcntl()函数,并设置F_SETOWN标志来接收SIGIO信号。一旦接收到SIGIO信号,进程就可以执行相应的处理操作,如读取设备数据或进行其他操作。
通过使用fasync,设备驱动程序可以避免阻塞用户进程,可以实现非阻塞I/O操作,提高系统的响应性能。
下面实现的是打开设备文件并启用异步通知功能,然后进入一个无限循环中,等待接收SIGIO信号的触发。当设备发生变化时,设备驱动程序会发送SIGIO信号给这个用户空间程序,触发fasync_handler函数的执行,从而可以进行相应的处理操作。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int fd;
void fasync_handler(int num)
{
printf("fasync_handler entering\n");
}
void main()
{
int i=2;
char data[256];
int oflags=0;
int retval;
signal(SIGIO, fasync_handler);
fd=open("/dev/fcn",O_RDWR);
if(fd==-1)
{
perror("error open\n");
exit(-1);
}
printf("open /dev/fcn successfully\n");
//使能了异步的通知到当前进程
fcntl(fd, F_SETOWN, getpid());
oflags=fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflags | FASYNC);
while(1);
close(fd);
}
下面代码定义了一个名为simple_dev的结构体,用于表示设备的相关信息。在模块加载时,通过register_chrdev_region()函数请求一个主设备号。
创建并初始化一个simple_dev结构体,然后通过cdev_init()函数对其进行初始化。
调用cdev_add()函数将字符设备添加到系统中。定义了简单的设备操作函数,包括打开设备(simple_open)、关闭设备(simple_release)和异步通知(simple_fasync)。
初始化一个定时器simple_timer,当时间到达时,会调用simple_timer_handler函数。
在simple_open函数中,将定时器添加到内核定时器列表中,并启动定时器。
在simple_fasync函数中,通过fasync_helper函数将当前进程添加到异步通知队列fasync_queue中。
在simple_release函数中,调用simple_fasync函数取消异步通知。
模块卸载时,通过cdev_del()函数删除字符设备,释放相关资源。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "demo.h"
MODULE_AUTHOR("fgj");
MODULE_LICENSE("Dual BSD/GPL");
struct simple_dev *simple_devices;
static unsigned char simple_inc=0;
static struct timer_list simple_timer;
static struct fasync_struct *fasync_queue=NULL;
static void simple_timer_handler( unsigned long data)
{
printk("simple_timer_handler...\n");
if (fasync_queue)
{
//POLL_IN可读,POLL_OUT为可写
kill_fasync(&fasync_queue, SIGIO, POLL_IN);
printk("kill_fasync...\n");
}
return ;
}
int simple_open(struct inode *inode, struct file *filp)
{
struct simple_dev *dev;
dev = container_of(inode->i_cdev, struct simple_dev, cdev);
filp->private_data = dev;
simple_timer.function = &simple_timer_handler;
simple_timer.expires = jiffies + 2*HZ;
add_timer (&simple_timer);
printk("add_timer...\n");
return 0;
}
static int simple_fasync(int fd, struct file * filp, int mode)
{
int retval;
printk("simple_fasync...\n");
retval=fasync_helper(fd,filp,mode,&fasync_queue);
if(retval<0)
return retval;
return 0;
}
int simple_release(struct inode *inode, struct file *filp)
{
simple_fasync(-1, filp, 0);
return 0;
}
struct file_operations simple_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.release= simple_release,
.fasync= simple_fasync,
};
/*******************************************************
MODULE ROUTINE
*******************************************************/
void simple_cleanup_module(void)
{
dev_t devno = MKDEV(simple_MAJOR, simple_MINOR);
if (simple_devices)
{
cdev_del(&simple_devices->cdev);
kfree(simple_devices);
}
unregister_chrdev_region(devno,1);
}
int simple_init_module(void)
{
int result;
dev_t dev = 0;
dev = MKDEV(simple_MAJOR, simple_MINOR);
result = register_chrdev_region(dev, 1, "DEMO");
if (result < 0)
{
printk(KERN_WARNING "DEMO: can't get major %d\n", simple_MAJOR);
return result;
}
simple_devices = kmalloc(sizeof(struct simple_dev), GFP_KERNEL);
if (!simple_devices)
{
result = -ENOMEM;
goto fail;
}
memset(simple_devices, 0, sizeof(struct simple_dev));
cdev_init(&simple_devices->cdev, &simple_fops);
simple_devices->cdev.owner = THIS_MODULE;
simple_devices->cdev.ops = &simple_fops;
result = cdev_add (&simple_devices->cdev, dev, 1);
if(result)
{
printk(KERN_NOTICE "Error %d adding DEMO\n", result);
goto fail;
}
init_timer(&simple_timer);
return 0;
fail:
simple_cleanup_module();
return result;
}
module_init(simple_init_module);
module_exit(simple_cleanup_module);
#ifndef _simple_H_
#define _simple_H_
#include /* needed for the _IOW etc stuff used later */
/********************************************************
* Macros to help debugging
********************************************************/
#undef PDEBUG /* undef it, just in case */
#ifdef simple_DEBUG
#ifdef __KERNEL__
# define PDEBUG(fmt, args...) printk( KERN_DEBUG "DEMO: " fmt, ## args)
#else//usr space
# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
#endif
#else
# define PDEBUG(fmt, args...) /* not debugging: nothing */
#endif
#undef PDEBUGG
#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */
//设备号
#define simple_MAJOR 226
#define simple_MINOR 0
#define COMMAND_LEDON 1
#define COMMAND_LEDOFF 2
//设备结构
struct simple_dev
{
struct cdev cdev; /* Char device structure */
};
//函数申明
ssize_t simple_read(struct file *filp, char __user *buf, size_t count,
loff_t *f_pos);
ssize_t simple_write(struct file *filp, const char __user *buf, size_t count,
loff_t *f_pos);
loff_t simple_llseek(struct file *filp, loff_t off, int whence);
int simple_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg);
#endif /* _simple_H_ */
上述代码的实现了一个简单的字符设备驱动程序,并使用定时器实现了异步通知功能,通过调用fasync_helper函数将进程添加到异步通知队列中,当定时器时间到达时,会发送SIGIO信号给进程,以实现类似中断的功能。