linux异步通知

简述

linux下异步方式有两种:异步通知和异步IO(AIO),aio请参考:linux异步IO--aio

异步通知的含义是:一旦设备就绪,则主动通知应用程序,这样应用程序就不需要查询设备状态,准确称谓是“信号驱动的异步I/O”。

Linux的I/O机制经历了一下几个阶段的演进:

1. 同步阻塞I/O: 用户进程进行I/O操作,一直阻塞到I/O操作完成为止。

2. 同步非阻塞I/O: 用户程序可以通过设置文件描述符的属性O_NONBLOCK,I/O操作可以立即返回,但是并不保证I/O操作成功。

3. 异步事件阻塞I/O: 用户进程可以对I/O事件进行阻塞,但是I/O操作并不阻塞。通过select/poll/epoll等函数调用来达到此目的。

4. 异步事件非阻塞I/O: 也叫做异步I/O(AIO),用户程序可以通过向内核发出I/O请求命令,不用等待I/O事件真正发生,可以继续做另外的事情,等I/O操作完成,内核会通过函数回调或者信号机制通知用户进程。这样很大程度提高了系统吞吐量。

阻塞IO意味着一直等待设备可访问后再访问,非阻塞IO中使用poll()意味着查询设备是否可访问,而异步通知则意味着设备通知自身可访问,实现了异步IO。

实现

信号定义在:arch/arm/include/uapi/asm/signal.h

异步通知在linux下实现分为两部分:应用层和内核驱动层。

应用层主要处理signal部分(signal编程),处理步骤如下:

1. 通过F_SETOWN IO控制命令(fcntl(fd, F_SETOWN, getpid()))设置设备文件的拥有者为本进程,这样从设备驱动发出的信号才能被本进程接收到。将本应用程序的进程号告诉内核。

2. 通过F_SETFL IO控制命令(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC))设置文件支持FASYNC,即异步通知模式。开启异步通知。

3. 通过signal()函数连接信号和信号处理函数。

内核驱动层处理步骤:

1. 支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。内核已完成。

2. 支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将执行。

3. 在设备资源可获得时,调用kill_fasync()函数激发相应的信号。

设备驱动中异步通知编程涉及一项数据结构和两个函数,数据结构是fasync_struct,两个函数分别是:

处理FASYNC标志变更的:

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

sig为SIGIO,band可读时POLL_IN,可写时POLL_OUT。

struct fasync_struct {
spinlock_t fa_lock;
int magic;
int fa_fd;
struct fasync_struct *fa_next;
struct file *fa_file;
struct rcu_head fa_rcu;
};

和其他设备驱动一样,将fasync_struct结构体指针放在设备结构体中仍然是最佳选择。

要使用异步通知,需要实现fasync函数:

int (*fasync) (int fd, struct file *filp, int on)

fasync 函数里面一般通过调用 fasync_helper 函数来初始化fasync_struct 结构体指针。当应用程序通过“fcntl(fd, F_SETFL, flags | FASYNC)”改变fasync 标记的时候,驱动程序file_operations 操作集中的fasync函数就会执行。

在关闭驱动文件的时候需要在 file_operations 操作集中的 release 函数中释放 fasync_struct,fasync_struct 的释放函数同样为 fasync_helper。

   struct fasync_struct *async_queue;

// 中断中发送信号
    if(atomic_read(&dev->releasekey)){
        if(dev->async_queue){
            kill_fasync(&dev->async_queue, SIGIO, POLLIN);
        }
    }

static int imx6uirq_fasync(int fd, struct file *filp, int on)
{
    struct imx6uirq_dev *dev = (struct imx6uirq_dev*)filp->private_data;
    return fasync_helper(fd, filp, on, &dev->async_queue);
}

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

static struct file_operations chrtest_fops = {
    .owner = THIS_MODULE,
    .open = imx6uirq_open,
    .read = imx6uirq_read,
    .poll = imx6uirq_poll,
    .fasync = imx6uirq_fasync,
    .release = imx6uirq_release,
};
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <linux/ioctl.h>
#include <stdbool.h>
#include <signal.h>

static int fd = 0;

static void sigio_handler(int signum)
{
    int err = 0;
    unsigned int keyvalue = 0;

    err = read(fd, &keyvalue, sizeof(keyvalue));
    if(err < 0){

    } else {
        printf("sigio signal! key value=%d\n", keyvalue);
    }
}

int main(int argc, char **argv)
{
    char *filename = NULL;

    if(argc != 2){
        printf("Usage: %s filename ", argv[0]);
        exit(-1);
    }

    filename = argv[1];
    fd = open(filename, O_RDWR);
    if(fd < 0){
        printf("Can't open file: %s\n", filename);
        return -1;
    }

    signal(SIGIO, sigio_handler);
    fcntl(fd, F_SETOWN, getpid());
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC);

    while(1){
        sleep(2);
    }

    close(fd);
    return 0;
}

 

参考:

1. Linux 异步IO机制

posted @ 2018-03-18 18:21  yuxi_o  阅读(1447)  评论(0编辑  收藏  举报