您的位置:首页 > 运维架构 > Linux

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

2009-10-25 10:02 447 查看

在很多系统软件的开发中,需要使用一些系统的唯一性信息。所以,得到主机的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 int
getcpuid (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 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声明如下:

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
#endif

static 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;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: