fasync的解釋


 fasync (fdfileon)
  當我們呼叫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_datafasync則是里面的一個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
#include
#include
#include
#include
#include
#include
#include
#include
#include


#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
#include
#include
#include
#include
#include
#include
#include


#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
#include
#include
#include
#include
#include


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;
    }








http://blog.csdn.net/unbutun/article/details/5039446
http://oss.org.cn/kernel-book/ldd3/ch06s04.html



    全站熱搜

    立你斯 發表在 痞客邦 留言(0) 人氣()