《电子技术应用》
您所在的位置:首页 > 嵌入式技术 > 设计应用 > Linux下SCSI API研究及应用
Linux下SCSI API研究及应用
许先斌 彭润年 王慧星
摘要: Linux SCSI体系结构及API数据结构的操作原理和相关的系统调用,运用SCSI API实现了有关的数据存储。
Abstract:
Key words :

  摘  要: Linux SCSI体系结构及API数据结构的操作原理和相关的系统调用,运用SCSI API实现了有关的数据存储。

  关键词: SCSI通用驱动器  SCSI接口  数据存储

 

  随着计算机网络技术和计算机图形图像处理技术的飞速发展,影视行业对计算机技术的依赖程度越来越大。影视数字化编辑播出系统是一个信息量巨大、精度高、实时性强的高速复杂的分布式多媒体网络信息系统。它所需要的网络服务器的规模很大,因此对服务器存储技术要求非常高。目前服务器的存储技术主要分为二大类:直接连接存储技术DAS(Direct-Attached Storage)和附网存储技术NAS(Network-Attached Storage)。当前DAS的主流技术是Ultra 3 SCSI和RAID。由于受IDE设备扩展性的限制及IDE设备缺乏可替换技术的支持,目前IDE RAID的应用还不多。因此服务器的DAS技术一直和SCSI技术的发展紧密关联,而且在市场上SCSI技术的影响也很广泛。

  为了建立影视数字化与空间数据组织及管理的原型实验室,作为其先期的工作,需要对SCSI技术有深入的研究与分析,以期实现大容量数据的高效分布式存储及调度。本文对SCSI接口和Linux下的SCSI API进行了详细的分析,并运用此API作了与数据存储相关的实现。

1  SCSI接口

  SCSI指小型计算机系统接口(Small Computer System Interface),最早研制于1979年,原是为小型机研制出的一种接口技术。但随着电脑技术的发展,现在它已被完全移植到了普通微机上,如硬盘、光驱、打印机、光盘刻录机等设备。

  SCSI接口从技术和性能上始终拥有顶级设备的特征。SCSI接口与IDE接口相比具有以下特点:(1)适应面广。使用IDE接口时,会受到系统IRQ(中断号)及IDE通道的限制,一般情况下每个IDE通道使用一个IRQ,所接的IDE设备最多不能超过15个。而使用SCSI时,所接的设备可以超过15个,且所有这些设备只占用一个IRQ。(2)多任务。SCSI允许在对一个设备传输数据的同时对另一个设备进行数据查找,这对于服务器来说是非常重要的。因为服务器常常需要同时处理几个进程,并且对服务器硬盘的操作也经常是既有读数据,又有写数据。(3)宽带宽。理论上,目前最快的SCSI总线带宽为160Mbps,这意味着硬盘的传输速率最高可达160Mbps(这是理论值,实际应用中会降低)。而目前最快的IDE接口硬盘的外部速度为100Mbps(即ATA-100,这也是理论值),而且由于各方面的限制,现在所能用的最高速度只有33Mbps。(4)较少的CPU占用率。使用传统IDE接口时,CPU需要全程控制数据的传输操作,所以在IDE传输数据的过程中,CPU不能执行任何操作,直到传输结束才可执行后续的指令。而SCSI在进行数据传输时,CPU在将传输指令传给SCSI后就可立即处理后续的指令,传输的工作则交给SCSI卡上的处理芯片自行负责,直到SCSI处理完毕、发出信号通知CPU后,CPU再进行后续处理,因此占用CPU资源较少。

2 Linux下的SCSI API

  由于SCSI具有多任务的特点,因此它在多任务操作系统(如Linux、Windows NT)中可以获得更高的性能。为了能够更好地对Linux的SCSI子系统进行管理,本文先对SCSI体系结构和与系统相关的应用接口进行分析,然后再运用API对数据存储相关方面进行实现。

2.1 SCSI体系结构

  从图1中可以看出,上层支持用户-内核接口,SD属于磁盘类,SR属于CD-ROM子系统,它们都有一个中断设备接口。ST是用来读写磁带的字符驱动器,SG是用一个字符设备接口连接设备的命令,它们都有一个字符设备接口。SCSI中间层定义了内部接口,为上层和下层驱动器提供服务。

 

  通过SG驱动器的请求可以分为三个阶段:(1)从用户接受请求,预留所需要的资源。如果需要,在用户区的数据先被传送到内核缓冲区,然后将请求提交给SCSI中间层(然后给适配器)去执行。(2)假设SCSI适配器支持中断,当请求完成时中断就被接收。当中断到达时,数据传输即完成。也就是说,如果SCSI命令是READ发的,则数据就在内核缓冲区或者在用户缓冲区。(3)用户通过一个调用取出请求的结果。如果需要,内核缓冲区中的数据会被传送到用户层,此时与这个请求相关的内核资源被释放。通常write()调用就是第一阶段的发送请求,之后read()调用取得数据或者出错信息。另外SCSI中间层自动维护一个队列以支持多个用户请求。

2.2 主要数据结构

  当前通用SCSI-3驱动器的最主要数据结构是struct sg_io_hdr。其余的一些数据结构是关于特定驱动器信息(如struct sg_scsi_id,struct sg_req_info等)的。另外,老版本的SCSI接口struct sg_header是SCSI-2的主要控制结构。

  sg_io_hdr中的主要成员有:interface_id,一般情况下它被置为‘S’,标志接口的版本;dxfer_direction标志数据的传输模式;SG_SXFER_NONE标记测试设备是否就绪;SG_SXFER_TO_DEV标记一个写命令;SG_SXFER_FROM_DEV标记一个读命令;SG_SXFER_UNKNOWN用在当前应用程序不清楚具体传输模式的情况下;cmdp指向一个SCSI命令的指针;cmd_len代表了命令的长度;dxferp表示用户内存缓冲空间,存放被传输数据;dxfer_len表示缓冲空间的大小;sbp表示错误信息的存放地点。其余一些数据成员是辅助性的成员,本文不再介绍。此外,有关命令操作码可以参看scsi.h文件。

2.3 系统调用

  成功地打开一个sg设备文件名,就在文件描述符和连接的SCSI设备间建立了一个连接。sg设备保存着在SCSI设备和文件描述符层(如保留缓冲区)中的状态信息和资源。即使SCSI设备被拔掉,一个应用仍然可以拥有该设备的文件描述符。如USB这样的热拔插设备就可以被拔掉,但它的文件描述符仍然存在。此时大多数试图访问该设备的系统调用会生成ENODEV错误,poll()调用会产生一个POLLHUP错误,但close()调用会正常完成。如对这个文件进行open()操作,则也会产生一个ENODEV错误。

  (1)open()调用

  调用的形式为open(const char*filename,int flags)。

  filename为sg设备文件名。flags为以下标记或者为以下标记的组合:

  O_RDONLY:只能用于read()和ioclt()操作中。

  O_RDWR:允许所有的系统调用执行。

  O_EXCL:在处理之前等待与SCSI设备相关的其他打开关闭。当其他人打开了一个SCSI设备,如果设置了O_NONBLOCK就会产生EBUSY。该标记不允许和O_RDONLY、O_EXCL一起用。

  O_NONBLOCK:设置non_blocking模式。这个标记被ioclt(SG_IO)忽略。

  在以上的标记中,O_RDONLY和O_RDWR必需设置,其他标记可以任选。

  (2)write()调用

  调用形式为write(int sg_fd,const void*buffer,size_t count)。

buffer应该指向数据类型为sg_io_hdr_t的对象,count为(sg_io_hdr_t)的大小。如果write()调用成功,count就作为结果返回,命令请求就被发送给SCSI设备。

  (3)read()调用

  调用形式为read(int sg_d,void*buffer,size_t count)。

  buffer应该指向数据类型为sg_io_hdr_t的对象,count为(sg_io_hdr_t)的大小。如果read()调用成功,则count作为结果返回。通常read()返回的是最早被完成的命令请求。

  (4)poll()调用

  调用的形式为poll(struct pollfd*ufds,unsigned int nfds,int timeout)。这个调用用于测试sg文件的当前状态。POLLIN说明可以进行read()调用,POLLOUT则代表可以发送write()命令,POLLHUP意味一个设备被分离需要进行清理工作,POLLERR则表示出错。

  (5)close()调用

  调用的形式为close(int sg_fd)。close()调用应该在所有write()及read()调用完成之后进行。返回0代表成功,-1表示错误。

3  SCSI API在数据存储中的应用

  在一台双CPU HP服务器(512MB)上挂载了二个Ultra3 Lvd SCSI硬盘,系统平台为RedHat Linux 7.3,其内核为Linux2.4.18。往内存中随机写入200MB数据,然后按一定的策略将其分布储存到二个SCSI硬盘,其算法流程见图2。

读操作所涉及到函数是int sg_read(int sg_fd,unsigned char*buff,int blocks,int from_block,int bs,int*diop)。其中sg_fd代表数据源所在设备的句柄,buff是获得数据的存储区,这个函数从sg_fd的from_block处开始读取blocks*bs大小的数据到buff中去。diop标志是否使用DMA方式。下面是用C语言编写的读操源程序的主要部分,略去了一些完整性控制。

 

  

 

  按照上述的算法,将200MB的数据分布地写到二个SCSI硬盘上,同时也将200MB的数据写到一个SCSI硬盘上进行对比测试。测试时使用Linux系统下的time函数获得运行时间,图3的纵坐标表示传输200MB数据所需要的总时间。从图3中可以得到单硬盘传输200MB数据所需要的平均时间为12.4112s,平均的传输速率为16.115Mbps,这和系统提供的拷贝命令的传输速率差不多。按上述算法存储200MB数据所花的平均时间为8.8314s,平均的传输速率是22.647Mbps。不难发现分布储存的效率比一个硬盘储存要高40.5%,具体数据参见图3。

 

此内容为AET网站原创,未经授权禁止转载。