You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Linux环境下SCSI设备通过sg_ll_inquiry()查询00页及获取支持页码的方法咨询

Linux环境下SCSI设备通过sg_ll_inquiry()查询00页及获取支持页码的方法咨询

嗨,刚接触SCSI的话确实容易被规范里的各种参数绕晕,我来给你捋清楚怎么通过sg_ll_inquiry()查询00页并获取设备支持的所有查询页码~

首先得澄清一个关键细节:你要获取的支持页码列表,并不是来自普通的Standard Inquiry(EVPD=0时的00页),而是需要发送带EVPD=1标志的Inquiry命令,同时指定Page Code=00h——这是SCSI的VPD(Vital Product Data)查询模式下的00页,专门用来返回设备支持的所有VPD页码。

一、先从命令行快速验证(推荐新手先这么做)

Linux下可以用sg3_utils工具包的sg_inq命令快速测试,先确保你已经安装了工具包:

  • Debian/Ubuntu系:sudo apt install sg3-utils
  • RHEL/CentOS系:sudo yum install sg3_utils

然后执行以下命令:

  1. 查看Standard Inquiry(EVPD=0,00页)的设备基础信息:

    sg_inq --evpd=0 --page=0 /dev/sdX
    

    (把/dev/sdX换成你的SCSI设备路径,比如/dev/sda/dev/sg0

  2. 获取支持的VPD页码列表(EVPD=1,00页):

    sg_inq --evpd=1 --page=0 /dev/sdX
    

    输出里会有类似Supported VPD pages: 00 01 08 30 80 83的内容,这些就是设备支持的可查询页码。

二、用sg_ll_inquiry()写代码实现

如果你需要在程序里实现,要用到libsgutils2库(开发包是libsgutils2-dev,记得先安装),步骤如下:

1. 打开SCSI设备

首先要以读写模式打开设备文件,注意需要足够权限(比如用sudo,或者把用户加入disk组):

int fd = open("/dev/sdX", O_RDWR);
if (fd < 0) {
    perror("Failed to open SCSI device");
    return 1;
}

2. 构造VPD 00页的查询CDB

Inquiry命令的CDB是6字节,格式为:[0x12, EVPD, Page Code, 0, Allocation Length, 0]
我们需要设置EVPD=1(启用VPD模式),Page Code=00h(查询支持的页码列表),Allocation Length设为足够大的值(比如256,因为页码列表不会太长):

unsigned char cdb[6] = {0x12, 0x01, 0x00, 0x00, 0x100, 0x00};

3. 调用sg_ll_inquiry()获取响应

传入文件描述符、CDB、响应缓冲区等参数,设置超时时间(比如1000ms):

unsigned char resp_buf[256] = {0};
int resp_len = sg_ll_inquiry(fd, cdb, sizeof(cdb), resp_buf, sizeof(resp_buf), 1000);
if (resp_len < 0) {
    perror("sg_ll_inquiry failed");
    close(fd);
    return 1;
}

4. 解析支持的页码列表

VPD 00页的响应结构里,从第3个字节(索引2)开始就是支持的页码,每个字节对应一个Page Code:

printf("Supported VPD page codes:\n");
for (int i = 2; i < resp_len; i++) {
    printf("0x%02X ", resp_buf[i]);
}
printf("\n");

5. 进一步查询其他支持的页码

拿到页码后,就可以针对每个页码构造新的CDB,再次调用sg_ll_inquiry()获取对应页面的数据。比如查询0x01页(设备序列号):

// 构造查询0x01页的CDB,Allocation Length设为32(足够容纳序列号)
unsigned char cdb_page1[6] = {0x12, 0x01, 0x01, 0x00, 0x20, 0x00};
unsigned char resp_page1[32] = {0};
int resp_len1 = sg_ll_inquiry(fd, cdb_page1, sizeof(cdb_page1), resp_page1, sizeof(resp_page1), 1000);

if (resp_len1 > 0) {
    printf("Device Serial Number: ");
    // 0x01页的序列号从第5个字节(索引4)开始
    for (int i = 4; i < resp_len1; i++) {
        printf("%c", resp_page1[i]);
    }
    printf("\n");
}

6. 编译和运行

编译时要链接libsgutils2库:

gcc -o scsi_inquiry scsi_inquiry.c -lsgutils2

然后运行(需要权限):

sudo ./scsi_inquiry

三、关键注意事项

  • 不同类型的SCSI设备(磁盘、磁带、光驱等)支持的VPD页码可能不同,要根据设备类型参考对应的SCSI子规范。
  • 调用sg_ll_inquiry()一定要做好错误处理,返回值小于0表示调用失败,可通过errno查看具体原因。
  • 如果看SCSI规范,要重点看Inquiry命令的VPD章节,尤其是Page Code 00h(Supported VPD Pages)的响应格式,之前你可能没注意到EVPD位的设置,导致看的是Standard Inquiry的内容,所以没找到页码列表~

备注:内容来源于stack exchange,提问作者usha

火山引擎 最新活动