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
How to obtain (almost) unique system identifier in a cross platform way?
提问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. stat
function 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。