如何以跨平台的方式获得(几乎)唯一的系统标识符
How to obtain (almost) unique system identifier in a cross platform way?
我正在寻找一种方法来获得一个数字,当在不同的机器上运行代码时几乎肯定会改变,并且几乎肯定在同一台机器上两次运行之间保持相同。
如果我在Linux中使用shell脚本,我会这样做:
{ uname -n ; cat /proc/meminfo | head -n1 ; cat /proc/cpuinfo ; } | md5sum
但我需要这在c++(与boost),至少在Windows, Linux和Mac。
要生成唯一的机器id,可以从系统上的不同硬件中获取一些序列号。大多数处理器都有一个CPU序列号,每个硬盘都有一个编号,每个网卡都有一个唯一的MAC地址。
你可以得到这些并为机器建立指纹。在将其声明为新机器之前,您可能希望允许更改其中的一些数字。(例如,如果三个中的两个是相同的,那么机器是相同的)。因此,您可以通过升级组件来优雅地处理问题。
我从我的一个项目中剪切了一些代码,这些代码得到了这些数字。
Windows:#include "machine_id.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <intrin.h>
#include <iphlpapi.h>
#ifndef _MSC_VER
#include <cpuid.h>
#else
#include <intrin.h>
#endif
// 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:
#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, %%esint"
"cpuidnt"
"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: %sn", getMachineName());
printf("CPU: %dn", getCpuHash());
printf("Volume: %dn", getVolumeHash());
return 0;
}
我知道,这个问题有点太老了,无法回答。但我在很多场合都遇到过这个问题。我喜欢接受解决方案,但如果你试过代码,你就会知道它有问题。
首先,CPU id是产品id,而不是序列号。因此,如果您在另一台服务器上使用相同的CPU,那么它将无法工作。MAC地址也可以轻松更改。
如果你只想在Linux上完成这个,你可以试试像hal服务。ie .
hal-get-property --udi /org/freedesktop/Hal/devices/computer --key system.hardware.uuid
但是最好的办法可能是如果你可以强制root访问,如果你想亲自动手,那就是查看dmidecode的代码。它将允许您提取机箱,Bios,视频和系统的UUID。
也许您可以从唯一的硬件id -生成几乎唯一的idMAC是普遍唯一的,你也可以使用cpu模型
在我看来,你应该只选择那些可能不会经常改变的东西,如cpu或LAN/WLAN卡。
一个相当可移植的解决方案是使用当前可执行文件的修改时间。stat
函数在unix和windows上可用,尽管API不同,因此您需要使用一些IFDEFs
。
一个二进制文件不可能在同一时间部署到不同的机器上,所以id应该是唯一的。缺点是二进制更新将更改id。
- 何时在引用或唯一指针上使用移动语义
- C++优先级队列,按对象的唯一指针的特定方法升序排列
- 计算排序向量的向量中唯一值的计数
- 如何使用Visual Studio 2017在C++中为参数化对象数组使用唯一指针
- 复制几乎为空的数组的最快方法
- 通过组合不同的类型来创建唯一的id
- 使用Unique_ptr确保工厂中的对象唯一
- c++多进程编写一个唯一的文件
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁
- 如何更改唯一指针向量的可见性
- 在C++的两个字符串中连接以逗号分隔的唯一值
- 共享指针和具有自定义删除程序的唯一指针之间的语法差异背后的任何原因
- C++尝试深度复制唯一指针时出现内存访问冲突
- 具有引用成员的结构是否具有唯一的对象表示形式
- 使用 RTTI 克隆唯一指针的向量
- 为什么兰德每次都给我几乎相同(但略有不同)的数字
- 如何在 c++ 中迭代数组中的唯一元素
- 为什么我无法创建唯一指针
- 堆分配的对象是否存在永不为空的唯一所有者?
- 如何以跨平台的方式获得(几乎)唯一的系统标识符