Linux下获取CPUID、硬盘序列号与MAC地址

本文介绍如何通过编程获取计算机的CPUID、硬盘序列号及网卡的MAC地址等唯一性信息,用于系统软件开发中。文章详细介绍了通过GCC嵌入汇编获取CPUID的方法、使用ioctl系统调用获取硬盘序列号的过程以及通过socket和ioctl获取MAC地址的技术细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在很多系统软件的开发中,需要使用一些系统的唯一性信息。所以,得到主机的CPUID、硬盘序列号及网卡的MAC地址,就成个一件很重要的应用。

本人经过一番google即自己的钻研,基本上实现了这几个功能。需要的准备知识有:

GCC的嵌入汇编,具体的GCC嵌入汇编知识,请参考相关手册
ioctl系统调用,具体的调用方法,请查看手册页
获取CPUID
按照网上提供的说明,CPUID并不是所有的Intel CPU都支持的。如果支持,汇编调用为:eax置0000_0003,调用cpuid。

以下为实现代码(在我的CPU上,并没有得到):

#define cpuid(in,a,b,c,d)  asm("cpuid": "=a" (a), "=b" (b), "=c" (c), "=d" (d) : "a" (in));static intgetcpuid (char *id, size_t max){  int i;  unsigned long li, maxi, maxei, 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, 获得指定文件描述符的标志号

ioctl的第三个参数为struct hd_driveid ,在linux/hdreg.h中,struct hd_driveid的声明有

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,则为没有提供。
思路明确了,以下为实现代码:

static intgetdiskid (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声明如下:

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;},其中,ifrn_name为网络接口的名称,ifr_ifru.ifru_hwaddr为网络接口的MAC地址。
#ifndef MAX_IFINDEX# define MAX_IFINDEX    8#endifstatic intgetmacaddr (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;}

本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/eroswang/archive/2009/10/25/4725121.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值