Linux 异步通知技术简介

异步通知的全称是”信号驱动的异步IO”,通过”信号”的方式,放期望获取的资源可用时,驱动会主动通知指定的应用程序,和应用层的”信号”相对应,这里使用的是信号”SIGIO“。操作步骤是

  1. 应用层程序将自己注册为接收来自设备文件的SIGIO信号的进程
  2. 驱动实现相应的接口,以期具有向所有注册接收这个设备驱动SIGIO信号的应用程序发SIGIO信号的能力。
  3. 驱动在适当的位置调用发送函数,应用程序即可接收到SIGIO信号。

整个机制的框架:

应用层接收SIGIO

和其他信号一样,应用层需要注册一个信号处理函数,
注册的方式还是使用signal()sigaction()

此外,应用层还需要把自己加入到驱动的通知链表中,加入的代码如下

fcntl(dev_fd,F_SETOWN,getpid());
int oflags = fcntl(dev_fd,F_GETFL);
fcntl(dev_fd,F_SETFL,oflags|FASYNC);
...
while(1);

完成了上面的工作,应用层的程序就可以静待SIGIO的到来了。

驱动发送SIGIO

应用层注册好了,最终的发送还是看设备驱动的处理方式,为了使设备支持异步通知机制,参照应用层的接口,驱动程序中涉及3项工作。

  1. 支持F_SETOWN命令,能在这个命令中下设置filp->f_owner为对应进程的ID,这部分内核已经做了
  2. 支持F_SETFL,每当FASYNC标志改变时,驱动程序中的fasync()将得以执行,so,驱动中要实现fasync()
  3. 当设备资源可用时,通过kill_fasync()发送SIGIO

为了在内核中实现上面这三个功能,驱动需要使用1个结构+2个API,结构是struct fasync_struct,函数是fasync_helper()kill_fasync()

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

fasync_helper()的作用是将一个fasync_struct的对象注册进内核,应用层执行fcntl(dev_fd,F_SETFL,oflags|FASYNC)时会回调驱动的fops.fasync(),所以通常将fasync_helper()放到fasync()的实现中。

/**
 *fasync_helper - 将一个fasync_struct对象注册进内核
 *@fd:文件描述符,由fasync传入
 *@filp:file指针,由fasync传入
 *@sig:信号类型,通常使用的就是SIGIO
 *@dev_fasync:事前准备的fasync_struct对象指针的指针
 */
int fasync_helper(int fd, struct file * filp, int sig, struct fasync_struct ** dev_fasync);   

下面这个API就是释放SIGIO,根据需求的不同放到不同的位置。

/**
 *kill_fasync - 释放一个信号
 *@dev_fasync:事前使用fasync_helper注册进内核的fasync_struct对象指针的指针
 *@filp:file指针,由fasync传入
 *@sig:信号类型,通常使用的就是SIGIO
 *@flag:标志,通常,如果资源可读用POLLIN,如果资源可写用POLLOUT
 */
void kill_fasync(struct fasync_struct **dev_fasync, int sig, int flag);

驱动模板

下面这个驱动模板针对在硬件中断到来(资源可用)的时候向应用层发信号,实际的操作中表明资源可用的情境还有很多

static struct fasync_struct *fasync = NULL;

static irqreturn_t handler(int irq, void *dev)
{
    kill_fasync(&fasync, SIGIO, POLLIN);
    return IRQ_HANDLED;
}
static int demo_fasync(int fd, struct file *filp, int mode)
{
    return fasync_helper(fd, filp, mode, &fasync);
}
struct file_operations fops = {
    ...
    .fasync = demo_fasync,
    ...
}
static int __init demo_init(void)
{
    ...
    request_irq(irq, handler, IRQF_TRIGGER_RISING, "demo", NULL);
    ...
}

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Discover more from sketch2sky

Subscribe now to keep reading and get access to the full archive.

Continue reading