Linux下如何获取硬盘序列号、MAC地址等硬件信息

在很多系统软件的开发中,需要使用一些系统硬件信息。比如CPU、硬盘、网卡等。所以,得到主机的CPUID、硬盘序列号及网卡的MAC地址,就成为一件很重要的功能。

命令获得

要实现这些信息,标准的Linux系统之中,有很多现成的命令,或者说工具,可以直接使用。

比如,要取得网卡的信息,可以使用

ifconfig

或者

ip a

命令。

取得CPU的信息,可以查看/proc/cpuinfo。或者通过/dev/cpu/[id]/cpuid文件取得cpuid。

要取得硬盘的序列号,可以使用lsblk命令。

如要取得/dev/nvme1n1硬盘的序列号,可以执行命令:

lsblk --nodeps -no serial /dev/nvme1n1

。另外,还可以通过tune2fs命令来获得文件系统的信息,比如创建时间。

比如,执行

tune2fs -l /dev/sda1

就可以看到系统的创建时间、最后挂载时间、最后写入时间等。

C/C++语言取得

如何要在C/C++语言中获得,当然可以直接在语言中调用这些命令,但是也可以使用一些更底层的函数来实现。

需要用到的准备知识有:

  • GCC的嵌入汇编。具体的GCC嵌入汇编知识,请参考相关手册。
  • ioctl系统调用。具体的调用方法,请查看手册页

获取CPUID

按照网上提供的说明,CPUID并不是所有的Intel CPU都支持的。如果支持,汇编调用为:eax置0000,再0003,调用cpuid。

以下为实现代码:

int  
getcpuid (char *id, size_t max)  
{  
  unsigned long maxi, eax, ebx, ecx, edx, unused;  
  
  cpuid (0, maxi, unused, unused, unused);  
  maxi &= 0xffff;  
  if (maxi < 3)  
    {  
      return -1;  
    }  
  
  cpuid (3, eax, ebx, ecx, edx);  
  snprintf (id, max, "%08lx %08lx %08lx %08lx", eax, ebx, ecx, edx);  
  fprintf (stdout, "get cpu id: %s\n", id);  
  
  return 0;  
}

获取硬盘序列号

这个的实现,采用的是读取/etc/mtab文件,找到/(即根目录)挂载的设备文件,然后打开它,再用系统调用ioctl来实现的。

ioctl第二个参数为HDIO_GET_IDENTITY,获得指定文件描述符的标志号,第三个参数为struct hd_driveid *。

在linux/hdreg.h中,struct hd_driveid的声明为:

/*  
* Structure returned by HDIO_GET_IDENTITY, as per ANSI NCITS ATA6 rev.1b spec.  
*  
* If you change something here, please remember to update fix_driveid() in  
* ide/probe.c.  
*/  
struct hd_driveid {  
       unsigned short  config;         /* lots of obsolete bit flags */  
       unsigned short  cyls;           /* Obsolete, "physical" cyls */  
       unsigned short  reserved2;      /* reserved (word 2) */  
       unsigned short  heads;          /* Obsolete, "physical" heads */  
       unsigned short  track_bytes;    /* unformatted bytes per track */  
       unsigned short  sector_bytes;   /* unformatted bytes per sector */  
       unsigned short  sectors;        /* Obsolete, "physical" sectors per track */  
       unsigned short  vendor0;        /* vendor unique */  
       unsigned short  vendor1;        /* vendor unique */  
       unsigned short  vendor2;        /* Retired vendor unique */  
       unsigned char   serial_no[20];  /* 0 = not_specified */  
       unsigned short  buf_type;       /* Retired */  
       unsigned short  buf_size;       /* Retired, 512 byte increments
                                        * 0 = not_specified  
                                        */
……
}

其中,serial_no为硬盘的序列号。如果此项为0,则获取失败。

以下为实现代码:

int  
getdiskid (char *id, size_t max)  
{  
  int fd;  
  struct hd_driveid hid;  
  FILE *fp;  
  char line[0x100], *disk, *root, *p;  
  
  fp = fopen ("/etc/mtab", "r");  
  if (fp == NULL)  
    {  
      fprintf (stderr, "No /etc/mtab file.\n");  
      return -1;  
    }  
  
  fd = -1;  
  
  while (fgets (line, sizeof line, fp) != NULL)  
    {  
      disk = strtok (line, " ");  
      if (disk == NULL)  
        {  
          continue;  
        }  
  
      root = strtok (NULL, " ");  
      if (root == NULL)  
        {  
          continue;  
        }  
  
      if (strcmp (root, "/") == 0)  
        {  
          for (p = disk + strlen (disk) - 1; isdigit (*p); p--)  
            {  
              *p = '\0';  
            }  
  
          fd = open (disk, O_RDONLY);  
          break;  
        }  
    }  
  
  fclose (fp);  
  
  if (fd < 0)  
    {  
      fprintf (stderr, "open hard disk device failed.\n");  
      return -1;  
    }  
  
  if (ioctl (fd, HDIO_GET_IDENTITY, &hid) < 0)  
    {  
      fprintf (stderr, "ioctl error.\n");  
      return -1;  
    }  
  
  close (fd);  
  
  snprintf (id, max, "%s", hid.serial_no);  
  fprintf (stdout, "get hard disk serial number: %s\n", id);  
  
  return 0;  
}

获取MAC地址

通过创建一个socket,然后bind特定的IP地址,就可以通过ioctl得到这个套按地绑定的网络接口名称。然后再通过网络接口名称,得到MAC地址。

如果ioctl的第二个参数为SIOCGIFNAME, 则获得指定网络接口的名称;如果ioctl的第二个参数为SIOCGIFHWADDR,则获得指定网络接口的MAC地址。

ioctl的第三个参数为struct ifreq *,在linux/if.h头文件里,struct ifreq声明如下:

/*  
* Interface request structure used for socket  
* ioctl's.  All interface ioctl's must have parameter  
* definitions which begin with ifr_name.  The  
* remainder may be interface specific.  
*/  
  
/* for compatibility with glibc net/if.h */  
#if __UAPI_DEF_IF_IFREQ  
struct ifreq {  
#define IFHWADDRLEN     6  
       union  
       {  
               char    ifrn_name[IFNAMSIZ];            /* if name, e.g. "en0" */  
       } ifr_ifrn;  
  
       union {  
               struct  sockaddr ifru_addr;  
               struct  sockaddr ifru_dstaddr;  
               struct  sockaddr ifru_broadaddr;  
               struct  sockaddr ifru_netmask;  
               struct  sockaddr ifru_hwaddr;  
               short   ifru_flags;  
               int     ifru_ivalue;  
               int     ifru_mtu;  
               struct  ifmap ifru_map;  
               char    ifru_slave[IFNAMSIZ];   /* Just fits the size */  
               char    ifru_newname[IFNAMSIZ];  
               void *  ifru_data;  
               struct  if_settings ifru_settings;  
       } ifr_ifru;  
};  
#endif /* __UAPI_DEF_IF_IFREQ */

其中,ifr_ifrn.ifrn_name为网络接口的名称,ifr_ifru.ifru_hwaddr为网络接口的MAC地址。

源码实现为:

int  
getmacaddr (const char *ip, char *id, size_t max)  
{  
  int i, sockfd;  
  struct sockaddr_in *loc;  
  struct ifreq req[1];  
  
  sockfd = socket (AF_INET, SOCK_DGRAM, 0);  
  if (sockfd < 0)  
    {  
      fprintf (stderr, "Unable to create socket.\n");  
      return -1;  
    }  
  
  for (i = 0; i <= MAX_IFINDEX; ++i)  
    {  
      req->ifr_ifindex = i;  
      if (ioctl (sockfd, SIOCGIFNAME, req) < 0)  
        {  
          fprintf (stderr, "ioctl error: %s\n", strerror (errno));  
          continue;  
        }  
  
      if (ioctl (sockfd, SIOCGIFADDR, req) < 0)  
        {  
          fprintf (stderr, "ioctl interface index [%d] error: %s\n", i,  
                   strerror (errno));  
          continue;  
        }  
  
      loc = (struct sockaddr_in *)(&(req->ifr_ifru.ifru_addr));  
      if (loc->sin_addr.s_addr == inet_addr (ip))  
        {  
          fprintf (stderr, "%s bind at %s.\n", ip, req->ifr_name);  
          break;  
        }  
    }  
  
  if (i == MAX_IFINDEX)  
    {  
      fprintf (stderr, "input IP error.\n");  
      close (sockfd);  
      return -1;  
    }  
  
  if (ioctl (sockfd, SIOCGIFHWADDR, req) < 0)  
    {  
      fprintf (stderr, "ioctl error: %s\n", strerror (errno));  
      close (sockfd);  
      return -1;  
    }  
  
  close (sockfd);  
  snprintf (  
      id, max, "%02X%02X%02X%02X%02X%02X", req->ifr_hwaddr.sa_data[0] & 0xff,  
      req->ifr_hwaddr.sa_data[1] & 0xff, req->ifr_hwaddr.sa_data[2] & 0xff,  
      req->ifr_hwaddr.sa_data[3] & 0xff, req->ifr_hwaddr.sa_data[4] & 0xff,  
      req->ifr_hwaddr.sa_data[5] & 0xff);  
  fprintf (stdout, "MAC address of %s: [%s].\n", req->ifr_name, id);  
  
  return 0;  
}
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值