在内核2.6.0之前,major和minor都是8位,合并组成16位的设备号(即syscall.stat中的dev_t)
static inline u16 old_encode_dev(dev_t dev)
{
return (MAJOR(dev) << 8) | MINOR(dev);
}
static inline dev_t old_decode_dev(u16 val)
{
return MKDEV((val >> 8) & 255, val & 255);
}
http://elixir.free-electrons.com/linux/latest/source/include/linux/kdev_t.h#L38
然而在2.6.0之后,major和minor因为需要支持更多数量的设备,修改为12位major和20位minor,组成32位的dev_t,为了兼容旧版本,在组成dev_t时拆出8位major和8位minor组成dev_t的低16位,高16位由剩下的填补
static inline u32 new_encode_dev(dev_t dev)
{
unsigned major = MAJOR(dev);
unsigned minor = MINOR(dev);
return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
}
static inline dev_t new_decode_dev(u32 dev)
{
unsigned major = (dev & 0xfff00) >> 8;
unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
return MKDEV(major, minor);
}
而即时是2.6.0之后的版本,在也只有在64位机上会使用new的方式
#if BITS_PER_LONG == 32
# define choose_32_64(a,b) a
#else
# define choose_32_64(a,b) b
#endif
#define valid_dev(x) choose_32_64(old_valid_dev(x),true)
#define encode_dev(x) choose_32_64(old_encode_dev,new_encode_dev)(x)
http://elixir.free-electrons.com/linux/latest/source/fs/stat.c#L289
glibc提供的反解方式/usr/include/sys/sysmacros.h,看起来这种方式考虑了major比12位更大的情况
# if defined __GNUC__ && __GNUC__ >= 2 && defined __USE_EXTERN_INLINES
__extension__ __extern_inline __attribute_const__ unsigned int
__NTH (gnu_dev_major (unsigned long long int __dev))
{
return ((__dev >> 8) & 0xfff) | ((unsigned int) (__dev >> 32) & ~0xfff);
}
__extension__ __extern_inline __attribute_const__ unsigned int
__NTH (gnu_dev_minor (unsigned long long int __dev))
{
return (__dev & 0xff) | ((unsigned int) (__dev >> 12) & ~0xff);
}
__extension__ __extern_inline __attribute_const__ unsigned long long int
__NTH (gnu_dev_makedev (unsigned int __major, unsigned int __minor))
{
return ((__minor & 0xff) | ((__major & 0xfff) << 8)
| (((unsigned long long int) (__minor & ~0xff)) << 12)
| (((unsigned long long int) (__major & ~0xfff)) << 32));
}
# endif
__END_DECLS
/* Access the functions with their traditional names. */
# define major(dev) gnu_dev_major (dev)
# define minor(dev) gnu_dev_minor (dev)
# define makedev(maj, min) gnu_dev_makedev (maj, min)
#endif
#endif /* sys/sysmacros.h */
最后自己写了一个golang的反解
func getDiskMajorMinor(path string) (major, minor int, err error) {
stat := syscall.Stat_t{}
err = syscall.Stat(path, &stat)
if nil != err {
return 0, 0, err
}
//refer to "dev_t new_decode_dev(u32 dev)" defined in the kernel header file linux/kdev.h
major = int(((uint32(stat.Dev)) >> 8) & 0xfff)
minor = int((stat.Dev & 0xff) | ((stat.Dev >> 12) & 0xfff00))
return major, minor, nil
}