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
然后执行以下命令:
查看Standard Inquiry(EVPD=0,00页)的设备基础信息:
sg_inq --evpd=0 --page=0 /dev/sdX(把
/dev/sdX换成你的SCSI设备路径,比如/dev/sda或/dev/sg0)获取支持的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




