喜欢IT的一个“武痴”! 喜欢追求新技术、探索技术!
分类: 系统运维
2012-12-20 15:55:19
深入分析Solaris异步IO 概述
异步IO作为一个接口在Solaris很早的版本就已经引入,也就是说Solaris早就支持异步IO并作为输入输出设备缺省接口。异步IO是一个提高机器处理效率的重要步骤,应用程序发出一个IO请求时,无需必须等待IO请求完成才能继续工作,而是在等待数据同时,可以处理其他事务。
早在Solaris 2.5就引入了64位文件IO接口,同时提供两个新的读写API函数:aioread64和 aiowrite64,这两个函数大大扩展了原先的两个异步读写函数aioread64和aiowrite。这两个函数的引入为关系型数据库供应商提供了一个实用工具,借此可以突破在raw设备上文件只有2G大小限制的瓶颈。
深入分析Solaris异步IO 详述
有了异步IO程序,应用程序拥有了实现真正意义上的异步IO 的能力。这是如何实现的呢?!应用程序在发出一个读或者一个写的请求后,被允许使用调用过程或者线程进行其他事务处理,直到收到IO操作完成的通知,或者收到一个错误消息。程序调用aioread和aiowrite被要求作为一个参数传递一个aio_result结构的指针,这个结构有两个结构成员:aio_return和aio_errno。万一发生错误,系统利用这个结构设置errno或者错误号作为一个调用的返回值,以下为该结构定义:
typedef struct aio_result_t {
int aio_return; /* return value of read or write */
int aio_errno; /* errno generated by the IO */
} aio_result_t;
Solaris系统中存在两套完全不同的异步IO接口:一个就是上文提到的aioread和aiowrite,以及POSIX等同程序aio_read和 aio_write,后者是基于POSIX标准实现。实时应用程序很明显在处理不可预知的流程过程中,必须在有限的时间里对这些外部中断做出反应。为了满足这个需求,需要使用非阻塞的IO应用程序,这也就是为啥需要异步IO的原因。POSIX和Solaris的异步IO接口其功能是一样的,真正不同点在于这两套接口实现的语义上。
异步IO的实现采用了lwp(light-weight process轻量进程)系统调用,使用了比较底层用户层线程库。在Solaris中可以使用Solaris线程或者POSIX线程来开发多线程应用程序,这两者线程接口都为库程序在用户模式调用基本函数功能,作为线程得到执行的系统调用是类似_lwp_xxxx的程序。
最初启用的异步函数aioread和aiowrite程序是创建一个IO请求队列,通过用户线程对他们进行处理,当aioread或者aiowrite进入请求时,系统只是简单的将IO请求放入队列并创建一个lwp(轻量进程)去处理IO。IO请求操作完成后lwp返回,而调用进程通过一个特殊信号SINGIO获得通知消息,收到SINGIO信号时将一个信号句柄返回给进程,进程据此采取相应的动作,进程检测读或者写IO返回aio_result结构中aio_return的返回值。除了基于信号的通知方法,还可以选择调用aiowait来获取结果,这些将导致调用线程一直被阻塞,直到IO完成为止。
使用库函数实现的异步IO线程对大多数应用程序来说已经足够了,那些造成重负荷IO的异步实用程序的应用程序来说,也没有必要提供更优化的性能,举例来说,商用的关系型数据库系统只是更为广泛地使用异步IO接口而已。而提供更优性能的、灵活的、涉及创建、管理和计划安排用户线程的接口,这才具有一定的积极意义,而且是必须的。以下将回顾一下一个已经存在kernel中的叫做kaio的异步IO架构。
Kaio第一次出现是在Solaris 2.4的kernel中,在以后的Solaris各发布版本中kaio都存在一些限制,在一些设备和包含支持kaio的软件上,这些限制必须进行处理。从应用程序的立场来看,这些限制是否存在问题,对于存储设备、卷管理软件和一些文件系统,这些都是透明的:如果kaio支持存在,这些设备和软件就会使用它,如果不存在,就会使用原有的基于库函数实现的异步IO。应用程序使用kaio的优点是不需要进行修改,系统将根据分配调度自动决定使用哪种异步IO。
Kaio根据名字就知道是在kernel中实现的异步IO的线程,而非用户线程实现的异步IO,IO的队列是由操作系统创建和管理的。其实现异步IO的基本顺序如下:当应用程序调用aioread或者aiowrite时,系统库程序将对其负责,一旦库程序介入,库函数程序第一将尝试通过Kaio来处理IO请求,那么接着,kaio初始化步骤就被执行,创建一个“cleanup”的线程,cleanup线程将确认其分配的内存中没有其他数据段,一旦这些操作完成,kaio就被调用。
在硬件设备方面,Kaio需要一个特殊的异步IO读和写程序的支持,Solaris在其支持的所有SCSI驱动设备中提供了这样的支持,这也包括了其支持的光纤存在设备,因为这些光纤设备的连接都是通过SCSI协议实现,另外一个实现的限制在于kaio只能为字符设备文件的异步IO工作,也就是说,kaio只能实现raw设备的异步IO。
另外一个kaio支持在于主机操作系统层的卷管理软件创建的raid卷:卷管理的raid设备卷经常由VERTIAS或者Disksuite来创建和管理,这些设备存放在/dev/vx/rdsk或者/dev/md/rdsk目录下,这些仿真设备支持kaio实现的异步程序。
如果有kaio支持,kernel将从队列里分配一个aio_req结构并在相应的设备驱动中调用异步IO程序。在驱动程序里面,kernel所需要的数据结构被设置支持IO,一个叫做aphysio的物理IO程序程序就会介入。同时,在kernel缓冲被设置时,raw设备IO使用kernel physio函数,而设备驱动程序将调用真正的设备IO,Physio程序等待设备驱动程序在kernel模式通过缓冲IO biowait完成。aphysio程序设置异步IO支持的结构和信号机制,然后调用驱动程序,而不用去等待IO的完成。
如果没有kaio支持,程序代码路径将有所不同。如果当libaio程序调用返回一个ENOTSUP错误时,kaio系统调用将进入,系统会完成用户线程级别的异步IO。这个基本上是将IO置于异步IO的队列中,并将aioread或者aiowrite 交给在libaio库初始化时创建的工作线程,为了进入kernel使用pread/pwrite调用进程,线程安排去处理IO。Pread和Pwrite类似read和write功能,允许调用者在开始进行IO时指定一个字节偏移量。本质上,调用进程创建线程去处理IO,允许调用者不需要等待其他工作的完成即可返回,这就是使得用户线程完成的IO看起来像异步IO一样,而实际上完成IO的是Solaris的系统调用(如pread和pwrite)在处理IO。
SUN公司内部标准和测试显示,kaio完成异步IO还是非常有效的。在减压高负载和专用的设备驱动层的支持,异步IO操作使得全局性能更好,更为有效。在相对较小、负载较轻的系统中,异步IO对性能和效能的提升并没有显示出其明显的优势,其提高的的百分点仅为5-6%。当系统的CPU和memory增加,异步IO请求大量增加,kaio能够提交更为灵活的性能调整,在一些标准测试中显示其提高百分比为30%左右。
Kaio作为调用kernel模块实现的,/kernel/sys/kaio在异步IO被第一次调用被load进kernel,可以使用命令modinfo查看kernel是否load该模块:
# modinfo | grep kaio
105 608c4000 2efd 178 1 kaio (kernel Async I/O)
如果命令没有返回任何信息,其表明kernel没有调用kaio模块,可以使用modload命令手工load进来kernel,也可以在/etc/system文件中进行foreload。
对于给定的文件,有个相关的测试程序可以检测是否具有kaio支持,该代码可以进行编译和运行:
/*
* Quick kaio test. Read 1k bytes from a file using async I/O.
* To compile:
* cc -o aio aio.c -laio
*
* To run:
* aio file_name
*/
#include
#include
#include
#include
#define BSIZE 1024
main(int argc, char *argv[])
{
aio_result_t res;
char buf[BSIZE];
int fd;
if ((fd=open(argv[1], O_RDONLY)) == -1) {
perror("open");
exit(-1);
}
aioread(fd, buf, BSIZE, 0L, SEEK_SET, &res);
aiowait(0);
if (res.aio_return == BSIZE) {
......