linux驱动开发学习三:异步操作

前面的队列以及锁都是基于阻塞是的操作。要实现同步,还可以通过信号也就是异步的方式来进行。例如在往文件的写入字符后,发送一个信号。捕捉到信号后执行动作。这样就不会造成阻塞,之前的阻塞性IO和POLL,是调用函数进去检查,条件不满足是造成阻塞。

应用层启动异步通知机制就三个步骤:

1 调用signal函数,让指定的信号SIGIO与处理函数sig_handle对应

2 指定一个进程作为文件的”属主(filp-owner)”, 这样内核才知道信号要发给哪个进程

3 在设备文件中添加FASYNC标志,驱动中就会调用xxx_fasync函数。

流程图如下:

 

 

在这里,命名为globalfifo_fasync

static const struct file_operations globalmem_fops={

        .owner=THIS_MODULE,

        .llseek=globalmem_llseek,

        .read=globalmem_read_queue,

        .write=globalmem_write_queue,

        .unlocked_ioctl=globalmem_ioctl,

        .open=globalmem_open,

        .release=globalmem_release,

        .fasync=globalfifo_fasync,

};

globalfifo_fasync函数实现如下,当文件的模式被设置成FASYNC的时候,将会调用这个函数

static int globalfifo_fasync(int fd,struct file *filp,int mode)

{

        struct globalmem_dev *dev=filp->private_data;

        return fasync_helper(fd,filp,mode,&dev->async_queue);

}

在 globalfifo_fasync中实际调用的是fasync_add_entry。

 

 

(1)     申请一个fasync_struct结构体。可以看到这个结构体手链表形式。fa_fd关联了设备文件fd参数

struct fasync_struct {

        spinlock_t                fa_lock;

        int                     magic;

        int                     fa_fd;

        struct fasync_struct *fa_next; /* singly linked list */

        struct file         *fa_file;

        struct rcu_head               fa_rcu;

};

(2)     调用fasync_insert_entry将新分配的fasync_struct插入到fasync列表中去。代码如下,如果能在fasync列表中找到对应的filp文件。则直接返回。如果没有找到,则将new插入到fapp的最前面。插入方法new->fa_fd = fd; new->fa_next = *fapp; 和前面介绍的列表插入方法是一样的。

 

 

4 等操作完成后,比如写完成,此时可以读取数据了,则通过kill_fasync释放一个信号。代码如下

 

 

实际起作用的是kill_fasync_rcu函数,在这个里面,遍历列表中所有的数据,找到owner以及fd。然后发送相应的信号

5 等操作完后fasync_remove_entry将异步队列清空

 

posted @ 2020-02-09 10:08  red_leaf_412  阅读(460)  评论(0编辑  收藏  举报