Linux内核读取磁盘序列号的问题

一向的观点就是“别在内核里面处理字符串”!事实上,确实应该如此!

       Linux内核的块设备驱动有能力读取磁盘的序列号,这个数据存储在磁盘的控制芯片ROM里面。内核应该以怎样的形式将这个序列号呈现给调用者呢?我们ls一下这个目录:

/dev/disk/by-id

ll /dev/disk/by-id/
...

lrwxrwxrwx 1 root root  9 10月 17 10:11 scsi-SATA_ST3500413AS_Z2A2AGQA -> ../../sdb
可以看到,一个磁盘可以用传统的/dev/sdX来索引,也可以by-YY来索引,其中by-id就是以序列号来索引,上述输出中,下划线后面的就是序列号。同样,我们可以用hdparm工具程序来读取,可以读到同样的结果。在hdparm的代码实现中,我们可以看到如下的代码段:
static char *strip (char *s)
{
    char *e;

    while (*s == ' ') ++s;
    if (*s)
        for (e = s + strlen(s); *--e == ' '; *e = '\0');
    return s;
}

static void dump_identity (__u16 *idw)
{
    int i;
    char pmodes[64] = {0,}, dmodes[128]={0,}, umodes[128]={0,};
    char *model = strip(strndup((char *)&idw[27], 40));
    char *fwrev = strip(strndup((char *)&idw[23],  8));
    char *serno = strip(strndup((char *)&idw[10], 20));
    __u8 tPIO;

    printf("\n Model=%.40s, FwRev=%.8s, SerialNo=%.20s", model, fwrev, serno);
...
}
很明显,在显示序列号时,strip去掉了首尾的空格,因为空格显示出来是没有意义的。这十分正常。然而...
       然而在2.6的老版本的内核比如2.6.8版本中,我们看到了do_identify中有下面的调用:
ide_fixstring(id->serial_no, sizeof(id->serial_no), bswap);
那么这个ide_fixstring是干什么的呢?它的实现如下,详细的注释已经给出了答案:
void ide_fixstring (u8 *s, const int bytecount, const int byteswap)
{
    u8 *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */

    if (byteswap) {
        /* convert from big-endian to host byte order */
        for (p = end ; p != s;) {
            unsigned short *pp = (unsigned short *) (p -= 2);
            *pp = ntohs(*pp);
        }
    }
    /* strip leading blanks */
    while (s != end && *s == ' ')
        ++s;
    /* compress internal blanks and strip trailing blanks */
    while (s != end && *s) {
        if (*s++ != ' ' || (s != end && *s && *s != ' '))
            *p++ = *(s-1);
    }
    /* wipe out trailing garbage */
    while (p != end)
        *p++ = '\0';
}
几乎是hdparm的strip更加严格意义上的翻版!这有什么问题呢?问题大了。这个内核没有办法给用户呈现一个原始的磁盘序列号,也就是序列号本身。为何不把处理留给应用程序呢?
       Linux内核应该迅速返回最原始的二进制信息,将解析任务留给应用程序,不光是效率考虑,更多的是内核根本不知道如何去解读这些信息!幸运的是,高版本的内核不再处理磁盘序列号了,仅仅返回了原始信息,不幸的是,它带来了问题!
       要不是工作中遇到了问题,我也不会闲到去折腾什么块设备驱动。系统仅仅升级了内核,然而升级前后都需要读取磁盘序列号和保存的序列号比对,老版本的内核和新版本内核对磁盘序列号读取行为的不同导致出现了不同的结果,内核也就不再对应用程序透明了!那怎么办?只好修改高版本内核驱动去迎合老版本的错误方式了!
       兼容就是一大万人坑,埋葬了不知多少精英!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux读取U盘或者移动硬盘序列号并获取U盘或者移动硬盘分区名。基本原理如下: 当有外置 USB 插入的时候,会产生 /proc/scsi/usb-storage 目录,并在其中产生数字文件(形如 1 2 3 4),此文件存储了设备相关信息。 相应的 /sys/class/scsi_device/ 目录中会有 scsi 设备的目录(ide 硬盘默认无子目录,sata硬盘默认有子目录),以数字开头(形如 1:0:0:0 2:0:0:0) 这个数字与前面 /proc/scsi/usb-storage目录中的相对应,子目录表示sata硬盘。比如 /sys/class/scsi_device/2:0:0:0/device/block 中有USB设备,从该目录下得到U盘或者移动硬盘的分区名,比如sda1。 该demo实现了上述过程,先检查 /proc/scsi/usb-storage 目录,看是否有u盘或者移动硬盘接入,如果有则读取u盘或者移动硬盘的序列号,然后在/sys/class/scsi_device/目录下逐级查找,直到找到u盘或者移动硬盘的分区。 完整代码,可直接编译和测试,嵌入式环境下也是适应的。 在linux测试如下,id表示序列号,sdb1表示是分区名称: # ./a.out udisk dev num:0 udisk id:055CE21B ret:0 dev num:0 found dir:0:0:0:1 found dev dir:0:0:0:1,full path:/sys/class/scsi_device/0:0:0:1 sub founction found dir:sda found sd device dir:sda,full path:/sys/class/scsi_device/0:0:0:1/device/block/sda found dir:0:0:0:2 found dev dir:0:0:0:2,full path:/sys/class/scsi_device/0:0:0:2 sub founction found dir:sdb found sd device dir:sdb,full path:/sys/class/scsi_device/0:0:0:2/device/block/sdb found sd sub device dir:sdb1 found dir:0:0:0:0 found dev dir:0:0:0:0,full path:/sys/class/scsi_device/0:0:0:0 sub funciton Open Directory /sys/class/scsi_device/0:0:0:0/device/block Error:No such file or directory get name return:0,disk name:sdb1 # 因为有的USB设备会有好几个,比如把CD和U盘集成到一起,就会出现上面的情况,有多个子目录去查找。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值