Linux驱动学习笔记:异步IO

前言前几篇介绍了几种io模型,今天介绍另一种io模型——异步io。
相对于前面的几种io模型,异步io在提交完io操作请求后就立即返回,程序不需要等到io操作完成再去做别的事情,具有非阻塞的特性。
当底层把io操作完成后,可以给提交者发送信号,或者调用注册的回调函数,告知请求提交者io操作已完成。
在信号处理函数或者回调函数中,可以使用异步io接口来获得io的完成情况,比如获取读写操作返回的字节数或错误码、读取的数据等。
相关接口struct aiocb结构体struct aiocb { int aio_fildes; /* file descriptor */ off_t aio_offset; /* file offset for i/o */ volatile void *aio_buf; /* buffer for i/o */ size_t aio_nbytes; /* number of bytes tdo transfer */ int aio_reqprio; /* priority */ struct sigevent aio_sigevent; /* signal information */ int aio_lio_opcode; /* operation for list i/o */};aio_fildes:操作的文件描述符
aio_offset:偏移量,读写操作的起始位置
aio_buf:读写操作的数据缓冲区
aio_nbytes:传输数据的字节数
aio_reqprio:请求权限
aio_lio_opcode:操作码,表示读还是写,lio_read代表读,lio_write代表写。
struct sigevent结构体struct sigevent { int sigev_notify; /* notify type */ int sigev_signo; /* signal number */ union sigval sigev_value; /* notify argument */ void (*sigev_notify_function)(union sigval); /* notify function */ pthread_attr_t *sigev_notify_attributes; /* notify attrs */};sigev_notify:通知类型。sigev_none表示不通知;sigev_signal表示io操作完成后,收到sigev_signo指定的信号;sigev_thread表示io操作完成,内核会创建一个新线程执行一个回调函数,函数由sigev_notify_function指定
sigev_signo:指定的信号
sigev_notify_function:回调函数
sigev_notify_attributes:使用默认属性,一般设置为null
应用层异步io读写函数:
#include int aio_read(struct aiocb *aiocb);int aio_write(struct aiocb *aiocb); //返回值:成功返回0;失败返回-1获取一个异步读、写或者同步操作的完成状态:
#include int aio_error(const struct aiocb *aiocb);调用aio_return函数,可以用来判断异步io的执行情况
#include ssize_t aio_return(const struct aiocb *aiocb);下面以一个实际例子说明异步io的用法
应用层#include #include #include #include #include #include #include #include #include #include #include void aiow_completion_handler (sigval_t sigval){ int ret; struct aiocb *req; req = (struct aiocb *)sigval.sival_ptr; if (aio_error(req) == 0) { ret = aio_return (req) ; printf (aio write %d bytes\\n, ret); } return;}void aior_completion_handler ( sigval_t sigval ){ int ret; struct aiocb *req; req = (struct aiocb * )sigval.sival_ptr; if (aio_error (req > == 0 ) { ret = aio_return (req); if (ret) printf (aio read: %s\\n, (char * ) req- >aio_buf ); } return;} int main(int argc, char *argv []){ int ret; int fd; struct aiocb aiow,aior; fd = open(/dev/vser0, o_rdwr); if (fd == -1) goto fail; memset (&aiow, 0, sizeof (aiow)); memset (&aior, 0, sizeof (aior)); aiow.aio_fildes = fd; aiow.aio_buf = malloc(32); strcpy((char *)aiow.aio_buf,aio test); aiow.aio_nbytes = strlen ( (char * ) aiow.aio_buf ) + 1; aiow.a*io_of f set = 0; aiow.aio_sigevent.sigev_notify = sigev_thread; aiow.aio_sigevent.sigev_notify_function = aiow_completion_handler; aiow.aio_sigevent.sigev_notify_attributes = null; aiow.aio_sigevent.sigev_value.sival_ptr = &aiow; aior.aio_fildes = fd; aior.aio_buf = malloc (32); aior.aio_nbytes = 32; aior.aio_offset = 0; aior.aio_sigevent •sigev_notify = sigev_thread; aior.aio_sigevent.sigev_notify_function = aior_completion_handler; aior.aio_sigevent.sigev_notify_attributes = null; aior.aio_sigevent.sigev_value.sival_ptr = &aior; while(1){ if(aio_write(&aiow) == -1) goto fail; if(aio_read(&aior) == -1) goto fail; sleep(1); } fail: perror(aio test); exit(exit_failure);}1、首先定义两个用于读和写的异步io控制块struct aiob
2、初始化异步io控制块,包括文件描述符、用读写的缓冲区、读写的字节数和回调函数
3、发起一个异步操作, 调用aio_wirte或者aio_read,该函数会立即返回,具体的读写操作会在驱动中完成 。
4、读写完成后,对应的回调函数会被自动调用
在写操作回调函数中,通过aio_error和aio_return获取了io操作的错误码及实际的写操作的返回值。
在读操作回调函数中,除了可以获取完成状态,还可以从aio_buf中获取读取的数据。
注意这里的关键点:1、 调用aio_wirte或者aio_read,该函数会立即返回,具体的读写操作会在驱动中完成 。2、读写完成后,对应的 回调函数会被自动调用 。
驱动层驱动中异步io相关代码如下:
define_kfifo(vsfifo, char, 32);static ssize_t my_read(struct file *flip, char __user *buf, size_t count, loff_t *pos){ int ret; unsigned int copied = 0; ret = kfifo_to_user(&vsfifo, buf, count, &copied); return ret == 0 ? copied : ret;}static ssize_t my_write(struct file *flip, const char __user *buf, size_t count, loff_tt *pos){ int ret; unsigned int copied = 0; ret = kfifo_from_user(&vsfifo, buf, count, &copied); return ret == 0 ? copied :ret;}static ssize_t my_aio_read (struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos){ size_t read = 0; unsigned long i; ssize_t reg; for(i = 0; i ki_filp, iov[i].iov_base, iov[i].iov_len, &pos); if(ret < 0) break; read += ret; } return read ? read : -efault;}static ssize_t my_aio_write (struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segsf loff_t pos){ size_t written = 0; unsigned longi; ssize t ret; for (i = 0;i lci_filp, iov [i].iov_base, iov [i].iov_len, &pos); if(ret < 0) break; written += ret; } return written ? written : -efault;}static struct file_operations my_ops = {...... .aio_read = my_aio_read, .aio_write = my_aio_write,}从驱动中可以看到,我们分别实现了两种读写函数my_read、my_write和my_aio_read、my_aio_write。
在一般的读写操作函数实现中,是这样的:
static struct file_operations my_ops = {...... .read = my_read, .write = my_write,}my_read()和my_wirte()函数实际上实现的是struct file_operations的.read和.write接口。
但是在异步io的实现中,``my_aio_read()和my_aio_write()分别实现的是.aio_read和.aio_write`接口:
static struct file_operations my_ops = {...... .aio_read = my_aio_read, .aio_write = my_aio_write,}而my_aio_read()函数又会去调用my_read()函数,my_aio_write()函数会去调用my_write()函数。
所以实际上,异步io还是要先实现一遍.read和.write的接口函数,然后在.aio_read和.aio_write的接口实现中,去调用之前实现的.read和.write接口。 只不过这里的主要区别是,异步io会多次调用my_read()和my_write()函数 。
以异步读为例,在 my_aio_read函数中,最关键的还是调用了my_read函数,但是my_read函数被调用了nr_serg次,这和分散/聚集操作是类似的,即 一次读操作实际上是分多次进行的,每次读取一定的字节数(iov[i].iov_len),然后分别将读到的数据放入分散的内存区域中(iov[i].iov_base) 。
从驱动中不难发现,异步io可以在驱动中阻塞,但是上层的操作却是非阻塞的。

直流电源EMI滤波器的设计原则
特斯拉Q3实现营收233.5亿美元,毛利率降至17.9%
NASA将阿耳忒弥斯推迟至2026年实施
【设计应用】工业物联网中的智能断路器
Airbiquity携手BlackBerry为OTA更新保驾护航
Linux驱动学习笔记:异步IO
Synopsys推出高性能嵌入式视觉处理器IP
年度超强旗舰又耍猴?满血骁龙835的小米6曝光
其他类型机器人抓手相比使用电动抓手的五大优势
气体传感器成汽车节能减排的所需
NVIDIA宣布将与谷歌云合作打造业内首个AI-on-5G创新实验室
无人机高光谱遥感监测葡萄长势与缺株定位
BEM200型3.7kW变频器上电报E.OH
联芯科技率先通过TD-LTE/TD-SCDMA双模MTnet测试
ChatGPT发布9个月已经成为2023年最大金矿,大家是怎么靠它挣到钱的?
如何将晶体管安装到芯片内
物联网在供应链管理中将如何使企业组织为未来的紧急情况做好准备?
物联网城市生活垃圾处理系统优化方案
串口控制数码管仿真设计教程
财富中文网发布了2020年《财富》中国500强排行榜