Linux异步通知和异步IO

Linux异步通知和异步IO

在设备驱动中使用通知可以使得在进行对设备的访问时,由驱动主动通知应用程序进行访问。

应用可以发起IO请求后立即返回,之后再查询IO完成情况、或者IO完成后被调用。

1.异步通知

异步通知,就是让驱动去告诉应用,底层硬件发生了什么事,而不是应用主动地去查询驱动,这对系统的性能有一个很大的提升。

1.1 信号

信号 描述
SIGHUP 1 终端挂起
SIGINT 2 终端中断(CTRL+C或者DELETE)
SIGQUIT 3 终端退出(CTRL+\)
SIGILL 4 非法指令异常
SIGTRAP 5 实现相关的硬件异常。一般是调试异常
SIGIOT 6 实现相关的硬件异常,一般对应SIGABRT
SIGBUS 7 某种特定的硬件异常,通常由内存访问引起
SIGFPE 8 数学相关的异常,如被0除,浮点溢出,等等
SIGKILL 9 无法处理和忽略。中止某个进程
SIGUSR1 10 用户自定义signal 1
SIGSEGV 11 非法内存访问
SIGUSR2 12 用户自定义signal 2
SIGPIPE 13 半关闭管道的写操作已经发生
SIGALRM 14 计时器到期
SIGTERM 15 请求中止进程,kill命令缺省发送
SIGSTKFLT 16 Linux专用,数学协处理器的栈异常
SIGCHLD 17 子进程停止或退出的时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略
SIGCONT 18 当被stop的进程恢复运行的时候,自动发送
SIGSTOP 19 中止进程。无法处理和忽略。
SIGTSTP 20 终端停止信号,一般是Ctrl+Z
SIGTTIN 21 当后台的进程尝试读取Terminal的时候发送
SIGTTOU 22 当后台的进程尝试写Terminal的时候发送
SIGURG 23 紧急的套接字事件
SIGXCPU 24 当CPU时间限制超时的时候
SIGXFSZ 25 文件超过大小限制
SIGVTALRM 26 虚拟时钟信号
SIGPROF 27 时钟信号描述
SIGWINCH 28 当Terminal的窗口大小改变的时候,发送给Foreground Group的所有进程
SIGIO 29 异步IO事件
SIGPOLL SIGIO 当某个事件发送给Pollable Device的时候发送
SIGPWR 30 断电重启
SIGSYS 31 非法系统调用

信号接收

void (*signal(int signum,void (*handler))(int)))(int);
//简化
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
===================================================================================
#include <signal.h>
#include<stdio.h>

void handler()
{
    printf(“hello\n”);
}

void main()
{
    int i;
    signal(SIGALRM,handler);
    alarm(5);
    for(i=1;i<7;i++)
    {
        printf(“sleep %d ...\n”,i);
        sleep(1);
    }
}
struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
};

int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);
====================================================================================
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
static void int_hander(int s)
{
    printf("Catch a signal sigint\n");
}
int main(void)
{
    int i;
    struct sigaction act, oact;
    act. sa_handler = int_hander;
    sigemptyset(&act. sa_mask); //清空此信号集
    act. sa_flags = 0;
    sigaction(SIGINT, &act, &oact);
    //signal(SIGINT, SIG_IGN);
    while(1)
    {
        for(i=0; i<5; i++)
        {
            write(1, ".", 1);
            sleep(1);
        }
        write(1, "\n", 1);
    }
    sigaction(SIGINT, &oact, NULL); //恢复成原始状态
    return 0;
}

1.2 异步通知接口

1.2.1 数据结构

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

1.2.2 初始化async_struct

struct fasync_struct *fasync_alloc(void);

//初始化fasync,包括分配内存和设置属性。得在驱动的release里把fasync_helper初始化的东西释放掉
int fasync_helper(int, struct file *, int, struct fasync_struct **);

1.2.3 释放fasync_struct

void fasync_free(struct fasync_struct *);

1.2.4 发送异步通知

//发送信号给fasync_struct结构体所描述的PID,触发应用程序的信号处理函数
void kill_fasync(struct fasync_struct **, int, int);

1.3 驱动实例

1.3.1 驱动程序

#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/slab.h>
#include <linux/device.h>
#define MEM_SIZE 256
#define MEM_NAME "mem"

struct mem_dev
{
    struct cdev dev;
    char mem[MEM_SIZE];
    struct fasync_struct *async_queue;
};

static struct mem_dev *mem_dev_p;
static dev_t mem_devno;
static struct class *mem_class;

static ssize_t mem_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
    struct mem_dev *dev_p = filp->private_data;
    if (count > MEM_SIZE)
        count = MEM_SIZE;
    if (copy_to_user(buf, dev_p->mem, count))
    {
        return -EFAULT;
    }

    return count;
}

static ssize_t mem_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
    struct mem_dev *dev_p = filp->private_data;

    if (count > MEM_SIZE)
        count = MEM_SIZE;
    if (copy_from_user(dev_p->mem, buf, count))
    {
        return -EFAULT;
    }

    if (dev_p->async_queue)
    {
        //发送SIGIO信号
        kill_fasync(&dev_p->async_queue, SIGIO, POLL_IN);
    }

    return count;
}

static int mem_fasync(int fd, struct file *filp, int mode)
{
    struct mem_dev *dev_p = filp->private_data;
    //初始化设置fasync
    return fasync_helper(fd, filp, mode, &dev_p->async_queue);
}

static int mem_open(struct inode * inode , struct file * filp)
{
    filp->private_data = mem_dev_p;
    return 0;
}

static int mem_release(struct inode * inode, struct file *filp)
{
    //文件关闭时清楚fasync资源
    mem_fasync(-1, filp, 0);
    return 0;
}

static const struct file_operations mem_fops = 
{
    .owner = THIS_MODULE,
    .open = mem_open,
    .release = mem_release,
    .read = mem_read,
    .write = mem_write,
    .fasync = mem_fasync,
};


static int __init my_mem_init(void)
{
    int ret;
    ret = alloc_chrdev_region(&mem_devno, 0, 1, MEM_NAME);
    if (ret)
    {
        goto out_1;
    }

    mem_dev_p = kmalloc(sizeof(struct mem_dev), GFP_KERNEL);
    if (NULL == mem_dev_p)
    {
        ret = -ENOMEM;
        goto out_2;
    }

    memset(mem_dev_p, 0, sizeof(struct mem_dev));

    cdev_init(&mem_dev_p->dev, &mem_fops);
    mem_dev_p->dev.owner = THIS_MODULE;
    mem_dev_p->dev.ops = &mem_fops;
    ret = cdev_add(&mem_dev_p->dev, mem_devno, 1);
    if (ret)
    {
        goto out_3;
    }

    mem_class = class_create(THIS_MODULE, "mem_driver");
    device_create(mem_class, NULL, mem_devno, NULL, "mem_fasync");

    printk("=====mem_init=====\n");
    return 0;

out_3: kfree(mem_dev_p);
out_2: unregister_chrdev_region(mem_devno, 1);
out_1: return ret;
}


static void __exit my_mem_exit(void)
{
    device_destroy(mem_class, mem_devno);
    class_destroy(mem_class);

    cdev_del(&mem_dev_p->dev);
    kfree(mem_dev_p);
    unregister_chrdev_region(mem_devno, 1);

    printk("=====mem_exit=====\n");
}

MODULE_LICENSE("GPL");
module_init(my_mem_init);
module_exit(my_mem_exit);

1.3.2 测试程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>

void catch_sigio(int signu)
{
    printf("catch signo\n");
}

int main(void)
{
    int flags;
    if (SIG_ERR == signal(SIGIO, catch_sigio))
    {
        printf("signal failed\n");
        return -1;
    }

    int fd;
    fd = open("/dev/mem_fasync", O_RDWR);
    if (-1 == fd)
    {
        perror("open");
        return -2;
    }
    printf("open success\n");

    //设置一个进程作为一个文件的属主(owner)
    fcntl(fd, F_SETOWN, getpid());
    flags = fcntl(fd, F_GETFL);
    //给文件设置FASYNC标志,以启用异步通知机制
    fcntl(fd, F_SETFL, flags | FASYNC);

    while (1)
    {
        sleep(1);
        write(fd,0,0);
    }
    return 0;
}

2.异步IO

2.1 glibc AIO接口

2.1.1 aio_read()

/* 提交一个异步读 */
int aio_read(struct aiocb *aiocbp);

2.1.2 aio_write()

/* 提交一个异步写 */
int aio_write(struct aiocb *aiocbp); 

2.1.3 aio_error()

/* 查看一个异步请求的状态(进行中EINPROGRESS?还是已经结束或出错?) */
int aio_error(const struct aiocb *aiocbp);

2.1.4 aio_return()

/* 查看一个异步请求的返回值 */
ssize_t aio_return(struct aiocb *aiocbp);

示例

#include <aio.h>
...
int fd,ret;
struct aiocb my_aiocb;

fd = open("file.txt",O_RDONLY);
if(fd < 0)
    perror("open");

/* 清零aiocb结构体 */
bzero(&my_aiocb,sizeof(struct aiocb));

/* 为aiocb请求分配数据缓冲区 */
my_aiocb.aio_buf = malloc(BUFSIZE + 1);
if(!my_aiocb.aio_buf)
    perror("malloc");

/* 初始化aiocb的成员 */
my_aiocb.aio_fildes = fd;
my_aiocb.aio_nbytes = BUFSIZE;
my_aiocb.aio_offset = 0;

ret = aio_read(&my_aiocb);
if(ret < 0)
    perror("aio_read");
while(aio_error(&my_aiocb) == EINPROGRESS)
    continue;
if((ret = aio_return(&my_aiocb)) > 0)
{
    /* 获取异步读返回值 */
}
else
{
    /* 读失败处理 */
}

2.1.5 aio_suspend()

/* 阻塞等待请求完成 */
int aio_suspend(const struct aiocb * const list[], int nent, const struct timespec *timeout); 

示例

struct aiocb *cblist[MAX_LIST];
/* 清除aiocb结构体链表 */
bzero((char *)cblist,sizeof(cblist));
/* 将一个或更多的aiocb放入aiocb的结构体链表中 */
cblist[0] = &my_aiocb;
ret = aio_read(&my_aiocb);
ret = aio_suspend(cblist,MAX_LIST,NULL);

2.1.6 aio_cancel()

/* 取消一个异步请求(成功AIO_CANCELED,失败AIO_NOTCANCELED) */
int aio_cancel(int fildes, struct aiocb *aiocbp); 

2.1.7 lio_listio()

/* 可以同时发起多个传输 */
int lio_listio(int mode,struct aiocb *list[],struct sigevent *sig);

示例

struct aiocb aiocb1,aiocb2;
...
/* 准备第一个aiocb */
aiocb1.aio_fildes = fd;
aiocb1.aio_buf = malloc(BUFSIZE + 1);
aiocb1.aio_nbytes = next_offset;
aiocb1.aio_lio_opcode = LIO_READ;
.../* 准备多个aiocb */
bzero((char *)list,sizeof(list));

/* 将aiocb填入链表 */
list[0] = &aiocb1;
list[0] = &aiocb2;
...
ret = lio_listio(LIO_WAIT,list,MAX_LIST,NULL);

2.1.8 应用实例

““c

include

include

include

include

include

include

include

include

#include <aio.h>  
#include <fcntl.h>  
#include <unistd.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <errno.h>  
#include <string.h>  
void async_read(sigval_t val)  
{  
  struct aiocb *ptr =(struct aiocb *)val.sival_ptr;  
  printf("read=%s", (char *)ptr->aio_buf);  
}  
int main(void)  
{  
  struct aiocb cb;  
  char sbuf[100];  
  int ret;  
  bzero(&cb, sizeof(cb));  
  cb.aio_fildes = 0;  
  cb.aio_buf = sbuf;  
  cb.aio_nbytes = 100;  
  cb.aio_offset = 0;  
  cb.aio_sigevent.sigev_value.sival_ptr = &cb;  
  cb.aio_sigevent.sigev_notify = SIGEV_THREAD;  
  cb.aio_sigevent.sigev_notify_function =async_read;  
  cb.aio_sigevent.sigev_notify_attributes = NULL;  
  ret = aio_read(&cb);  
  if (ret == -1) {  
    perror("aio_read");  
    exit(1);  
  }  
  int i = 0;  
  while (1) {  
    printf("%dn",i++);  
    sleep(1);  
  }  
  return 0;  
}

2.2 Linux内核AIO与libaio

//libaio提供下面五个主要API函数
int io_setup(int maxevents, io_context_t *ctxp);
int io_destroy(io_context_t ctx);
int io_submit(io_context_t ctx, long nr, struct iocb *ios[]);
int io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt);
int io_getevents(io_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout);

//这五个宏定义都是操作struct iocb的结构体

void io_set_callback(struct iocb *iocb, io_callback_t cb);
void io_prep_pwrite(struct iocb *iocb, int fd, void *buf, size_t count, long long offset);
void io_prep_pread(struct iocb *iocb, int fd, void *buf, size_t count, long long offset);
void io_prep_pwritev(struct iocb *iocb, int fd, const struct iovec *iov, int iovcnt, long long offset);
void io_prep_preadv(struct iocb *iocb, int fd, const struct iovec *iov, int iovcnt, long long offset);

示例

#define _GUN_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <inttypes.h>
#include <stdlib.h>
#include <libaio.h>

#define BUF_SIZE 4096

int main(int argc,char **argv)
{
    io_context_t ctx = 0;
    struct iocb cb;
    struct iocb cbs[1];
    unsigned char *buf;
    struct io_event events[1];
    int ret;
    int fd;

    if(argc < 2)
    {
        printf("the command format:aior [FILE]\n");
        exit(1);
    }

    fd = open(argv[1],O_RDWR | O_DIRECT);
    if(fd < 0)
    {
        perror("open errror");
        goto err;
    }

    /* Allocate aligned memory */
    ret = posix_memalign((void **)&buf,512,(BUF_SIZE + 1));
    if(ret < 0)
    {
        perror("posix_memalign failed");
        goto err1;
    }
    memset(buf,0,BUF_SIZE + 1);

    ret = io_setup(128,&ctx);
    if(ret < 0)
    {
        printf("io_setup error: %s",strerror(-ret));
        goto err2;
    }

    /* setup IO control block */
    io_prep_pread(&cb,fd,buf,BUF_SIZE,0);

    cbs[0] = &cb;
    ret = io_submit(ctx,1,cbs);
    if(ret != 1)
    {
        if(ret < 0)
        {
            printf("io_submit error: %s",strerror(-ret));
        }
        else
        {
            fprintf(stderr,"could not sumbit IOs");
        }
        goto err3;
    }

    /* get the reply */
    ret = io_getevents(ctx,1,1,events,NULL);
    if(ret != 1)
    {
        if(ret < 0)
        {
            printf("io_getevents error: %s",strerror(-ret));
        }
        else
        {
            fprintf(stderr,"could not get Events");
        }
        goto err3
    }

    if(events[0].res2 == 0)
    {
        printf("AIO error: %s",strerror(-events[0].res));
        goto err3;
    }

    if((ret = io_destroy(ctx)) < 0)
    {
        printf("io_destroy error: %s",strerror(-ret));
        goto err2;
    }
    free(buf);
    close(fb);
    return 0
err3: 
    if((ret = io_destroy(ctx)) < 0)
    {
        printf("io_destroy error: %s",strerror(-ret));
    }
err2:
    free(buf);
err1: 
    close(fd);
err:
    return -1
}

2.3 AIO与设备驱动

用户空间调用io_submit()后,对应于用户传递的每一个iocb结构,内核会生成一个与之对应的kiocb结构。io_submit()系统调用间接引起file——operation中的aio_read、aio_writed的调用。

ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*aio_fsync) (struct kiocb *, int datasync);

猜你喜欢

转载自blog.csdn.net/wyy626562203/article/details/81365141