Linux系统中的fasync用于设备驱动程序与用户空间之间的异步通信。它允许设备驱动程序通知用户空间的进程,当设备状态发生变化时,通过发送SIGIO信号来告知进程。
具体来说,设备驱动程序可以调用fasync_helper()函数来注册一个或多个进程,当设备状态发生变化时,内核会自动发送SIGIO信号给这些进程。而用户空间进程可以通过调用fcntl()函数,并设置F_SETOWN标志来接收SIGIO信号。一旦接收到SIGIO信号,进程就可以执行相应的处理操作,如读取设备数据或进行其他操作。
通过使用fasync,设备驱动程序可以避免阻塞用户进程,可以实现非阻塞I/O操作,提高系统的响应性能。
下面实现的是打开设备文件并启用异步通知功能,然后进入一个无限循环中,等待接收SIGIO信号的触发。当设备发生变化时,设备驱动程序会发送SIGIO信号给这个用户空间程序,触发fasync_handler函数的执行,从而可以进行相应的处理操作。
#include<sys/types.h>
#include<unistd.h>
#include<fcntl.h>
#include<linux/rtc.h>
#include<linux/ioctl.h>
#include<stdio.h>
#include<stdlib.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>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);
}
demo.c
-
下面代码定义了一个名为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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/cdev.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
#include <linux/ctype.h>
#include <linux/pagemap.h>
#include <linux/delay.h>#include <asm/io.h>
#include <asm/irq.h>
#include <asm/signal.h>
#include <asm/uaccess.h>#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);
demo.h
#ifndef _simple_H_
#define _simple_H_#include <linux/ioctl.h> /* 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信号给进程,以实现类似中断的功能。