C++ 如何以跨平台的方式获取(几乎)唯一的系统标识符?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16858782/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:42:25  来源:igfitidea点击:

How to obtain (almost) unique system identifier in a cross platform way?

c++cross-platformuniqueidentifier

提问by cube

I'm looking for a way to get a number which will almost surely change when running the code on different machines and almost surely stay the same between two runs on the same machine.

我正在寻找一种方法来获得一个数字,该数字在不同机器上运行代码时几乎肯定会发生变化,并且几乎肯定会在同一台机器上的两次运行之间保持不变。

If I were doing this as a shell script in Linux, I would use something like this:

如果我在 Linux 中将其作为 shell 脚本执行此操作,我会使用以下内容:

{ uname -n ; cat /proc/meminfo | head -n1 ; cat /proc/cpuinfo ; } | md5sum

But I need this in C++ (with boost) and at least on Windows, Linux and Mac.

但是我需要在 C++ 中使用它(带有 boost),至少在 Windows、Linux 和 Mac 上。

回答by Rafael Baptista

To generate a mostly unique machine id, you can get a few serial numbers from various pieces of hardware on the system. Most processors will have a CPU serial number, the hard disks each have a number, and each network card will have a unique MAC address.

要生成大多数唯一的机器 ID,您可以从系统上的各种硬件中获取一些序列号。大多数处理器都会有一个CPU序列号,每个硬盘都有一个编号,每个网卡都会有一个唯一的MAC地址。

You can get these and build a fingerprint for the machine. You might want to allow some of these numbers to change before declaring it a new machine. ( e.g. if the 2 out of three are the same, then the machine is the same ). So you can deal somewhat gracefully from having a component upgraded.

您可以获得这些并为机器建立指纹。在将其声明为新机器之前,您可能希望允许更改其中一些数字。(例如,如果三个中的两个相同,则机器相同)。因此,您可以通过升级组件来稍微优雅地处理。

I've clipped some code from one of my projects that gets these numbers.

我从我的一个项目中剪辑了一些获得这些数字的代码。

Windows:

视窗:

#include "machine_id.h"   

#define WIN32_LEAN_AND_MEAN        
#include <windows.h>      
#include <intrin.h>       
#include <iphlpapi.h>     


// we just need this for purposes of unique machine id. So any one or two mac's is       
// fine. 
u16 hashMacAddress( PIP_ADAPTER_INFO info )          
{        
   u16 hash = 0;          
   for ( u32 i = 0; i < info->AddressLength; i++ )   
   {     
      hash += ( info->Address[i] << (( i & 1 ) * 8 ));        
   }     
   return hash;           
}        

void getMacHash( u16& mac1, u16& mac2 )              
{        
   IP_ADAPTER_INFO AdapterInfo[32];                  
   DWORD dwBufLen = sizeof( AdapterInfo );           

   DWORD dwStatus = GetAdaptersInfo( AdapterInfo, &dwBufLen );                  
   if ( dwStatus != ERROR_SUCCESS )                  
      return; // no adapters.      

   PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;      
   mac1 = hashMacAddress( pAdapterInfo );            
   if ( pAdapterInfo->Next )       
      mac2 = hashMacAddress( pAdapterInfo->Next );   

   // sort the mac addresses. We don't want to invalidate     
   // both macs if they just change order.           
   if ( mac1 > mac2 )     
   {     
      u16 tmp = mac2;     
      mac2 = mac1;        
      mac1 = tmp;         
   }     
}        

u16 getVolumeHash()       
{        
   DWORD serialNum = 0;   

   // Determine if this volume uses an NTFS file system.      
   GetVolumeInformation( "c:\", NULL, 0, &serialNum, NULL, NULL, NULL, 0 );    
   u16 hash = (u16)(( serialNum + ( serialNum >> 16 )) & 0xFFFF );              

   return hash;           
}        

u16 getCpuHash()          
{        
   int cpuinfo[4] = { 0, 0, 0, 0 };                  
   __cpuid( cpuinfo, 0 );          
   u16 hash = 0;          
   u16* ptr = (u16*)(&cpuinfo[0]); 
   for ( u32 i = 0; i < 8; i++ )   
      hash += ptr[i];     

   return hash;           
}        

const char* getMachineName()       
{        
   static char computerName[1024]; 
   DWORD size = 1024;     
   GetComputerName( computerName, &size );           
   return &(computerName[0]);      
}

Linux and OsX:

Linux 和 OSX:

#include <stdio.h>
#include <string.h>
#include <unistd.h>          
#include <errno.h>           
#include <sys/types.h>       
#include <sys/socket.h>      
#include <sys/ioctl.h>  
#include <sys/resource.h>    
#include <sys/utsname.h>       
#include <netdb.h>           
#include <netinet/in.h>      
#include <netinet/in_systm.h>                 
#include <netinet/ip.h>      
#include <netinet/ip_icmp.h> 
#include <assert.h>

#ifdef DARWIN                    
#include <net/if_dl.h>       
#include <ifaddrs.h>         
#include <net/if_types.h>    
#else //!DARWIN              
// #include <linux/if.h>        
// #include <linux/sockios.h>   
#endif //!DARWIN               

const char* getMachineName() 
{ 
   static struct utsname u;  

   if ( uname( &u ) < 0 )    
   {       
      assert(0);             
      return "unknown";      
   }       

   return u.nodename;        
}   


//---------------------------------get MAC addresses ------------------------------------unsigned short-unsigned short----------        
// we just need this for purposes of unique machine id. So any one or two mac's is fine.            
unsigned short hashMacAddress( unsigned char* mac )                 
{ 
   unsigned short hash = 0;             

   for ( unsigned int i = 0; i < 6; i++ )              
   {       
      hash += ( mac[i] << (( i & 1 ) * 8 ));           
   }       
   return hash;              
} 

void getMacHash( unsigned short& mac1, unsigned short& mac2 )       
{ 
   mac1 = 0;                 
   mac2 = 0;                 

#ifdef DARWIN                

   struct ifaddrs* ifaphead; 
   if ( getifaddrs( &ifaphead ) != 0 )        
      return;                

   // iterate over the net interfaces         
   bool foundMac1 = false;   
   struct ifaddrs* ifap;     
   for ( ifap = ifaphead; ifap; ifap = ifap->ifa_next )                  
   {       
      struct sockaddr_dl* sdl = (struct sockaddr_dl*)ifap->ifa_addr;     
      if ( sdl && ( sdl->sdl_family == AF_LINK ) && ( sdl->sdl_type == IFT_ETHER ))                 
      {    
          if ( !foundMac1 )  
          {                  
             foundMac1 = true;                
             mac1 = hashMacAddress( (unsigned char*)(LLADDR(sdl))); //sdl->sdl_data) + sdl->sdl_nlen) );       
          } else {           
             mac2 = hashMacAddress( (unsigned char*)(LLADDR(sdl))); //sdl->sdl_data) + sdl->sdl_nlen) );       
             break;          
          }                  
      }    
   }       

   freeifaddrs( ifaphead );  

#else // !DARWIN             

   int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP );                  
   if ( sock < 0 ) return;   

   // enumerate all IP addresses of the system         
   struct ifconf conf;       
   char ifconfbuf[ 128 * sizeof(struct ifreq)  ];      
   memset( ifconfbuf, 0, sizeof( ifconfbuf ));         
   conf.ifc_buf = ifconfbuf; 
   conf.ifc_len = sizeof( ifconfbuf );        
   if ( ioctl( sock, SIOCGIFCONF, &conf ))    
   {       
      assert(0);             
      return;                
   }       

   // get MAC address        
   bool foundMac1 = false;   
   struct ifreq* ifr;        
   for ( ifr = conf.ifc_req; (char*)ifr < (char*)conf.ifc_req + conf.ifc_len; ifr++ ) 
   {       
      if ( ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data )          
         continue;  // duplicate, skip it     

      if ( ioctl( sock, SIOCGIFFLAGS, ifr ))           
         continue;  // failed to get flags, skip it    
      if ( ioctl( sock, SIOCGIFHWADDR, ifr ) == 0 )    
      {    
         if ( !foundMac1 )   
         { 
            foundMac1 = true;                 
            mac1 = hashMacAddress( (unsigned char*)&(ifr->ifr_addr.sa_data));       
         } else {            
            mac2 = hashMacAddress( (unsigned char*)&(ifr->ifr_addr.sa_data));       
            break;           
         } 
      }    
   }       

   close( sock );            

#endif // !DARWIN            

   // sort the mac addresses. We don't want to invalidate                
   // both macs if they just change order.    
   if ( mac1 > mac2 )        
   {       
      unsigned short tmp = mac2;        
      mac2 = mac1;           
      mac1 = tmp;            
   }       
} 

unsigned short getVolumeHash()          
{ 
   // we don't have a 'volume serial number' like on windows. Lets hash the system name instead.    
   unsigned char* sysname = (unsigned char*)getMachineName();       
   unsigned short hash = 0;             

   for ( unsigned int i = 0; sysname[i]; i++ )         
      hash += ( sysname[i] << (( i & 1 ) * 8 ));       

   return hash;              
} 

#ifdef DARWIN                
 #include <mach-o/arch.h>    
 unsigned short getCpuHash()            
 {         
     const NXArchInfo* info = NXGetLocalArchInfo();    
     unsigned short val = 0;            
     val += (unsigned short)info->cputype;               
     val += (unsigned short)info->cpusubtype;            
     return val;             
 }         

#else // !DARWIN             

 static void getCpuid( unsigned int* p, unsigned int ax )       
 {         
    __asm __volatile         
    (   "movl %%ebx, %%esi\n\t"               
        "cpuid\n\t"          
        "xchgl %%ebx, %%esi" 
        : "=a" (p[0]), "=S" (p[1]),           
          "=c" (p[2]), "=d" (p[3])            
        : "0" (ax)           
    );     
 }         

 unsigned short getCpuHash()            
 {         
    unsigned int cpuinfo[4] = { 0, 0, 0, 0 };          
    getCpuid( cpuinfo, 0 );  
    unsigned short hash = 0;            
    unsigned int* ptr = (&cpuinfo[0]);                 
    for ( unsigned int i = 0; i < 4; i++ )             
       hash += (ptr[i] & 0xFFFF) + ( ptr[i] >> 16 );   

    return hash;             
 }         
#endif // !DARWIN            

int main()
{

  printf("Machine: %s\n", getMachineName());
  printf("CPU: %d\n", getCpuHash());
  printf("Volume: %d\n", getVolumeHash());
  return 0;
}    

回答by Amirul Islam

I know, the question is bit too old to be answered. But I have on many occasions faced this issue. I like the accept solution, but if you have tried the code then you will know that it has issues.

我知道,这个问题太老了,无法回答。但是我在很多场合都遇到过这个问题。我喜欢接受解决方案,但如果您尝试过代码,那么您就会知道它有问题。

firstly the CPU id is the product ID- it is not the serial. So if you have same CPU in another Server then it is just not going to work. also the MAC Address can be changed with ease.

首先 CPU id 是产品 ID - 它不是序列号。所以如果你在另一台服务器上有相同的 CPU,那么它就不会工作。也可以轻松更改 MAC 地址。

If you are only trying to get this done on Linux- you could try like hal services. ie.

如果您只是想在 Linux 上完成此操作,您可以尝试使用 hal 服务。IE。

hal-get-property --udi /org/freedesktop/Hal/devices/computer --key system.hardware.uuid

But best thing probably to do is if you can enforce root access and if you want to get your hands dirty- is to look at the code for dmidecode. It will allow you to extract UUID of Chasis, Bios, Video and System. You cannot beat that :) and with a few tweaking you can convert it to a class.

但最好的办法可能是,如果您可以强制执行 root 访问权限,并且如果您想弄脏自己的手,那就是查看dmidecode的代码。它将允许您提取 Chasis、Bios、视频和系统的 UUID。你无法击败它 :) 并且通过一些调整你可以将它转换为一个类。

回答by fadedreamz

Maybe you can generate almost unique id from unique hardware ids - MAC is universally unique, you can also use cpu model

也许您可以从唯一的硬件 ID 生成几乎唯一的 ID - MAC 是普遍唯一的,您也可以使用cpu 模型

In my opinion you should pick only those things which may not be changed frequently like cpu or LAN/WLAN cards.

在我看来,您应该只选择那些可能不会经常更改的东西,例如 CPU 或 LAN/WLAN 卡。

回答by Jan Wrobel

One quite portable solution would be to use modification time of a current executable. statfunction is available on unix and windows, although API is different so you would need to use some IFDEFs.

一种非常便携的解决方案是使用当前可执行文件的修改时间。stat函数在 unix 和 windows 上可用,尽管 API 不同,所以你需要使用一些IFDEFs.

A binary is unlikely to be deployed at the exactly same time to different machines, so the ids should be unique. The drawback is that the binary update will change the ids.

二进制文件不太可能同时部署到不同的机器上,因此 ID 应该是唯一的。缺点是二进制更新会更改 ID。