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