In the development of many system software, it is necessary to use some unique information of the system. Therefore, it is a very important application to obtain the CPUID of the host, the serial number of the hard disk and the MAC address of the network card.
The required preparation knowledge is:
1. GCC embedded assembly, specific GCC embedded assembly knowledge, please refer to the relevant manual
2.ioctl system call, the specific call method, please check the man page
get CPUID
According to the instructions provided online, CPUID is not supported by all Intel CPUs. If supported, the assembly call is: eax is set to 0000_0003, and cpuid is called.
Here's the implementation code (on my CPU, didn't get it):
#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;
}
Get hard drive serial number
The implementation of this is to read the /etc/mtab file, find the device file mounted on / (that is, the root directory), then open it, and then use the system call ioctl to achieve.
The second parameter of ioctl is HDIO_GET_IDENTITY, which gets the flag number of the specified file descriptor
The third parameter of ioctl is struct hd_driveid. In linux/hdreg.h, the declaration of struct hd_driveid has
struct hd_driveid {
unsigned short config; /</em> 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
*/
……
};
Among them, serial_no is the serial number of the hard disk. If this is 0, it is not provided.
The idea is clear, the following is the implementation code:
#include <string.h>
#include<stdio.h>
#include <sys/ioctl.h>
#include <linux/hdreg.h>
#include <fcntl.h>
#include <stdlib.h>
#include<ctype.h>
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;
}