fasync(在内核发生某个事情时向应用发信号, 应用需要把那个设备用fcntl改一下。)


分类: kernel   735人阅读  评论(1)  收藏  举报

fasync的解释

 fasync (fd,file,on)
  当我们呼叫fcntl()系统呼叫,并使用F_SETFL命令来设定档案的参数时,VFS就会呼叫fasync ()这个函式,而当读写档案的动作完成时,行程会收到SIGIO的讯息。

=================================================================================

首先,打开目标设备。
第二,设置好目标设备的SIGIO信号处理程序。
第三,需要通过fcntl系统调用,使当前进程变成文件的主人。(这样才能使文件中的信号
发到当前进程)
第四,通过ioctl系统调用,将目标通道设置成异步操作模式。

在驱动程序的fops中,有一个函数
    int (*fasync)(int fd, struct file * file, int on);

在系统调用sys_ioctl的时候,会调用上面的fasync函数。

例如,鼠标器的
int fasync_aux(int fd, struct file * filp, int on)
{
    int retval;

    retval = fasync_helper(fd, filp, on, &queue->fasync);
    if (retval < 0)
        return retval;
    return 0;
}

这里的queue类似于(struct mydev *)filp->private_data,fasync则是里面的一个fasyn
c队列。
fasync_helper的作用是为当前进程创建一个fasync_struct数据结构,然后挂入目标设备
的fasync队列。

然后,在目标设备的常规驱动处理程序中,向该队列发送信号。

例如,鼠标驱动器的
void handle_mouse_event(unsigned char scancode)(这类似于usb驱动中的int urb,一
个回调函数)
{
    ......
    kill_fasync(&queue->fasync, SIGIO, POLL_IN);
    ......
}

即向每一个登记的进程发送SIGIO信号。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/walkingman321/archive/2008/03/09/2161304.aspx

=================================================================================

 

一个简单例子:



#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <asm/signal.h>
#include <asm-generic/siginfo.h>

#define DEVICE_NAME "chardev"

static int chardev_open(struct inode *inodp, struct file *filp);
static ssize_t chardev_read(struct file *filp, char __user *buff, size_t count, loff_t *f_pos);
static ssize_t chardev_write(struct file *filp, const char __user *buff, size_t count, loff_t *f_pos);
static int chardev_fasync(int fd, struct file *filp, int mode);
static int chardev_release(struct inode *inodp, struct file *filp);

static struct file_operations chardev_fops = {
        .open = chardev_open,
        .read = chardev_read,
        .write = chardev_write,
        .fasync = chardev_fasync,
        .release = chardev_release,
};

static struct fasync_struct *async = NULL;
static struct semaphore sem;
static struct cdev *cdevp;
static dev_t devno;
static char buffer[8192];

static int chardev_open(struct inode *inodp, struct file *filp)
{
        return 0;
}

static ssize_t chardev_read(struct file *filp, char __user *buff, size_t count, loff_t *f_pos)
{
        if (down_interruptible(&sem))
                goto err;

        count = strlen(buffer);
        copy_to_user(buff, buffer, count);
 printk("<0>""read buff is %s/n",buffer);
                kill_fasync(&async, SIGIO, POLL_IN);

        up(&sem);
 dump_stack();
        return count;
err:
        return -ERESTARTSYS;
}

static ssize_t chardev_write(struct file *filp, const char __user *buff, size_t count, loff_t *f_pos)
{
        if (down_interruptible(&sem))
                goto err;

        memset(buffer, '/0', sizeof(buffer));
        copy_from_user(buffer, buff, count);

 printk("<0>""write buff is %s/n",buffer);
        up(&sem);

        if (async)
        //        kill_fasync(&async, SIGIO, POLL_IN);

 dump_stack();
        return count;
err:
        return -ERESTARTSYS;
}

static int chardev_fasync(int fd, struct file *filp, int mode)
{
 //printk("<0>""/nin chardev_fasync/n");
 dump_stack();
        return fasync_helper(fd, filp, mode, &async);
}

static int chardev_release(struct inode *inodp, struct file *filp)
{
 //printk("<0>""/nin chardev_release/n");
 //printk("<0>""/nin chardev_release twice/n");
 dump_stack();
        return chardev_fasync(-1, filp, 0);
}

static int __init chardev_init(void)
{
        int ret;

        ret = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
        if (ret < 0)
                goto out;

        init_MUTEX(&sem);
        cdevp = cdev_alloc();
        if (!cdevp)
                goto alloc_err;

        cdev_init(cdevp, &chardev_fops);
        ret = cdev_add(cdevp, devno, 1);
        if (!ret)
                goto out;
alloc_err:
        unregister_chrdev_region(devno, 1);
out:
        return ret;
}

static void __exit chardev_exit(void)
{
        cdev_del(cdevp);

        unregister_chrdev_region(devno, 1);
}

MODULE_LICENSE("GPL");

module_init(chardev_init);
module_exit(chardev_exit);
=================================================================================

原版例子,上面的改了东西,不对

=================================================================================


#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <asm/uaccess.h>

#define DEVICE_NAME "chardev"

static int chardev_open(struct inode *inodp, struct file *filp);
static ssize_t chardev_read(struct file *filp, char __user *buff, size_t count, loff_t *f_pos);
static ssize_t chardev_write(struct file *filp, const char __user *buff, size_t count, loff_t *f_pos);
static int chardev_fasync(int fd, struct file *filp, int mode);
static int chardev_release(struct inode *inodp, struct file *filp);

static struct file_operations chardev_fops = {
        .open = chardev_open,
        .read = chardev_read,
        .write = chardev_write,
        .fasync = chardev_fasync,
        .release = chardev_release,
};

static struct fasync_struct *async = NULL;
static struct semaphore sem;
static struct cdev *cdevp;
static dev_t devno;
static char buffer[8192];

static int chardev_open(struct inode *inodp, struct file *filp)
{
        return 0;
}

static ssize_t chardev_read(struct file *filp, char __user *buff, size_t count, loff_t *f_pos)
{
        if (down_interruptible(&sem))
                goto err;

        count = strlen(buffer);
        copy_to_user(buff, buffer, count);

        up(&sem);
        return count;
err:
        return -ERESTARTSYS;
}

static ssize_t chardev_write(struct file *filp, const char __user *buff, size_t count, loff_t *f_pos)
{
        if (down_interruptible(&sem))
                goto err;

        memset(buffer, '/0', sizeof(buffer));
        copy_from_user(buffer, buff, count);

        up(&sem);

        if (async)
                kill_fasync(&async, SIGIO, POLL_IN);

        return count;
err:
        return -ERESTARTSYS;
}

static int chardev_fasync(int fd, struct file *filp, int mode)
{
        return fasync_helper(fd, filp, mode, &async);
}

static int chardev_release(struct inode *inodp, struct file *filp)
{
        return chardev_fasync(-1, filp, 0);
}

static int __init chardev_init(void)
{
        int ret;

        ret = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
        if (ret < 0)
                goto out;

        init_MUTEX(&sem);
        cdevp = cdev_alloc();
        if (!cdevp)
                goto alloc_err;

        cdev_init(cdevp, &chardev_fops);
        ret = cdev_add(cdevp, devno, 1);
        if (!ret)
                goto out;
alloc_err:
        unregister_chrdev_region(devno, 1);
out:
        return ret;
}

static void __exit chardev_exit(void)
{
        cdev_del(cdevp);

        unregister_chrdev_region(devno, 1);
}

MODULE_LICENSE("GPL");

module_init(chardev_init);
module_exit(chardev_exit);

=================================================================================

测试程序

=================================================================================

/*
 * asynctest.c: use async notification to read stdin
 *
 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
 * Copyright (C) 2001 O'Reilly & Associates
 *
 * The source code in this file can be freely used, adapted,
 * and redistributed in source or binary form, so long as an
 * acknowledgment appears in derived source files.  The citation
 * should list that the code comes from the book "Linux Device
 * Drivers" by Alessandro Rubini and Jonathan Corbet, published
 * by O'Reilly & Associates.   No warranty is attached;
 * we cannot take responsibility for errors or fitness for use.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>

int gotdata=0;
static int i = 0;
void sighandler(int signo)
{
    if (signo==SIGIO)
        gotdata++;
    printf("/nin sighandler %d/n",i);
    return;
}

char buffer[4096];

int main(int argc, char **argv)
{
    int count;
    struct sigaction action;
    FILE *fp;
    int fd = 0;

    fd = open("/dev/test",O_RDWR);
    memset(&action, 0, sizeof(action));
    action.sa_handler = sighandler;
    action.sa_flags = 0;

    sigaction(SIGIO, &action, NULL);

    fcntl(fd, F_SETOWN, getpid());
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC);
 ++i;

    while(i != 2) {
    //printf("/nin while %d/n",++i);
        /* this only returns if a signal arrives */
#if 0
    fp = fopen("log2","aw+");
    fprintf(fp,"/nin while %d/n",++i);
    fclose(fp);
#endif
        sleep(2); /* one day */
        if (!gotdata)
            continue;
        count=read(fd, buffer, 4);
    printf("/nin while %d/n",++i);
        /* buggy: if avail data is more than 4kbytes... */
        write(fd,buffer,count);
    //printf("/nin while %d/n",++i);
        gotdata=0;
    }
}

=================================================================================

总结:

         fasync这个东西就是为了使驱动的读写和application的读写分开,使得application可以在驱动读写时去做别的事,通过kill_fasync

(kill_fasync(&async, SIGIO, POLL_IN);)发SIGIO信号给应用,应用通过fcntl把自己这个SIGIO的信号换成自己的响应函数,当驱动发

(kill_fasync(&async, SIGIO, POLL_IN);)给应用时应用就调用了自己的handler去处理。fasync_helper作用就是初始化fasync这个东西,包括分配内存和设置属性。最后记得在驱动的release里把fasync_helper初始化的东西free掉。

=================================================================================

POLL_IN POLL_OUT

=================================================================================

驱动程序向用户程序发信号
--------------------------------------------- 
    当设备有IO事件发生,就有机制保证向应用进程发送信号,显然设备驱动程序扮演重要角色,实际终端tty、网络socket等的标准实现已经包括了实时信号驱动的支持,所以,在Linux中它们可以如上直接使用。但有些设备的驱动程序还并没有支持,所以需要定制设备驱动程序。以下两个API应该是可以屏蔽所有相关琐碎操作(类似send_sig())的标准接口:
    int fasync_helper (int fd, struct file *filp, int mode, struct fasync_struct **fa);      
    void kill_fasync (struct fasync_struct **fa, int sig, int band);
    
    如果需要支持异步通知机制,如下设备结构中需要有异步事件通知队列(它应该与睡眠队列类似),并且增加fasync()接口的实现(该函数将本进程登记到async_queue上去)。 当一个打开的文件FASYNC标志变化时(调用fcntl()函数,设置FASYNC文件标志时),fasync()接口将被调用。 

    struct kpp_dev {
        struct cdev cdev;
        struct fasync_struct *async_queue;
    };
     
    static int kpp_fasync(int fd, struct file *filp, int mode)
    {
        struct kpp_dev *dev = filp->private_data; 
        return fasync_helper(fd, filp, mode, &dev->async_queue);
    }

    事件发生的时机,就是中断服务程序或相应的软中断中调用kill_fasync():
    if (dev->async_queue)
        kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
    如果是写操作,就是POLL_OUT。注意,无论用户进程设定了什么期望的信号,在这个环节,发送的一般就是SIGIO。注意在设备文件关闭(release方法)时,注意执行fasync(),使得本文件的操作从上述的设备异步事件等待链表中剥离。

    static int kpp_release(struct inode *inode, struct file *filp)
    {
        kpp_fasync(-1, filp, 0);
        return 0;
    }

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值