Linux块设备UUID探究
在使用mount
命令和配置fstab
时,可以通过LABEL
和UUID
来挂载。从过往经验来看,只有GPT
分区才有UUID
,那MBR
分区的UUID
是保存在哪里呢?是在分区表,保留扇区,还是在文件系统中?是如何处理的?为了解决心中的疑惑,进行如下分析。
测试UUID与文件系统的关系
- 查看当前系统中块设备的
UUID
,没有文件系统时没有UUID
:
$ lsblk -f NAME FSTYPE LABEL UUID MOUNTPOINT sda ├─sda1 xfs b2f2dcbb-03c4-472f-b79c-942895cec1a9 / ├─sda2 swap 2305f62c-86dd-4f32-9f32-c736957996c4 [SWAP] └─sda3 sr0 $ blkid /dev/sda1: UUID="b2f2dcbb-03c4-472f-b79c-942895cec1a9" TYPE="xfs" /dev/sda2: UUID="2305f62c-86dd-4f32-9f32-c736957996c4" TYPE="swap"
- 格式化成
vfat
文件系统后,查看其UUID
,发现只有8
位:
$ mkfs.vfat /dev/sda3 mkfs.fat 3.0.20 (12 Jun 2013) $ lsblk -f NAME FSTYPE LABEL UUID MOUNTPOINT sda ├─sda1 xfs b2f2dcbb-03c4-472f-b79c-942895cec1a9 / ├─sda2 swap 2305f62c-86dd-4f32-9f32-c736957996c4 [SWAP] └─sda3 vfat 72DD-DA7A sr0
- 格式化成
xfs
文件系统,查看其UUID
,是正常的32
位:
$ mkfs.xfs -f /dev/sda3 meta-data=/dev/sda3 isize=512 agcount=4, agsize=1310720 blks = sectsz=512 attr=2, projid32bit=1 = crc=1 finobt=0, sparse=0 data = bsize=4096 blocks=5242880, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0 ftype=1 log =internal log bsize=4096 blocks=2560, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 $ lsblk -f NAME FSTYPE LABEL UUID MOUNTPOINT sda ├─sda1 xfs b2f2dcbb-03c4-472f-b79c-942895cec1a9 / ├─sda2 swap 2305f62c-86dd-4f32-9f32-c736957996c4 [SWAP] └─sda3 xfs cda5c46b-2a4b-47f5-99dd-2083cc5433b4 sr0
- 再一次格式化成
xfs
文件系统,发现UUID
发生了变化:
$ mkfs.xfs -f /dev/sda3 meta-data=/dev/sda3 isize=512 agcount=4, agsize=1310720 blks = sectsz=512 attr=2, projid32bit=1 = crc=1 finobt=0, sparse=0 data = bsize=4096 blocks=5242880, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0 ftype=1 log =internal log bsize=4096 blocks=2560, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 $ lsblk -f NAME FSTYPE LABEL UUID MOUNTPOINT sda ├─sda1 xfs b2f2dcbb-03c4-472f-b79c-942895cec1a9 / ├─sda2 swap 2305f62c-86dd-4f32-9f32-c736957996c4 [SWAP] └─sda3 xfs 2c0872d1-9646-440f-ae6e-c32c49f35ff2 sr0
可以看到没有格式化的块设备没有UUID
,同时UUID
与文件系统类型有关。每格式化一次文件系统,UUID
都会发生变化,初步可以推测UUID
是存放在文件系统中。
谁创建了磁盘的UUID文件
在/dev/disk/by-uuid
目录下,会根据磁盘的UUID
建立很多块设备文件的符号链接?这是谁干的呢?根据常识,CentOS 7
应该是使用的Systemd
提供的UDev
实现的,不过我们还是来细致分析一下。
- 开启系统中的审计功能:
$ auditctl -D $ auditctl -l No rules $ auditctl -w /dev/disk/by-uuid -p war $ auditctl -l -w /dev/disk/by-uuid -p rwa
- 再次格式化一个块设备:
$ mkfs.xfs -f /dev/sda3 meta-data=/dev/sda3 isize=512 agcount=4, agsize=1310720 blks = sectsz=512 attr=2, projid32bit=1 = crc=1 finobt=0, sparse=0 data = bsize=4096 blocks=5242880, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0 ftype=1 log =internal log bsize=4096 blocks=2560, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none $ lsblk -f NAME FSTYPE LABEL UUID MOUNTPOINT sda ├─sda1 xfs b2f2dcbb-03c4-472f-b79c-942895cec1a9 / ├─sda2 swap 2305f62c-86dd-4f32-9f32-c736957996c4 [SWAP] └─sda3 xfs 8b980779-48e3-4012-b0d0-e08a939965ec sr0
- 查看审计日志,可以看到的确是
UDev
干的好事:
$ ausearch -f /dev/disk/by-uuid ... ---- time->Thu Aug 16 15:44:42 2018 type=PROCTITLE msg=audit(1534405482.539:3076): proctitle="/usr/lib/systemd/systemd-udevd" type=PATH msg=audit(1534405482.539:3076): item=2 name="/dev/disk/by-uuid/8b980779-48e3-4012-b0d0-e08a939965ec" inode=5240274 dev=00:05 mode=0120777 ouid=0 ogid=0 rdev=00:00 objtype=CREATE type=PATH msg=audit(1534405482.539:3076): item=1 name="/dev/disk/by-uuid/" inode=10434 dev=00:05 mode=040755 ouid=0 ogid=0 rdev=00:00 objtype=PARENT type=PATH msg=audit(1534405482.539:3076): item=0 name="../../sda3" objtype=UNKNOWN type=CWD msg=audit(1534405482.539:3076): cwd="/" type=SYSCALL msg=audit(1534405482.539:3076): arch=c000003e syscall=88 success=yes exit=0 a0=7ffd033e8070 a1=563cfdee2140 a2=0 a3=ffffffff items=3 ppid=412 pid=1207 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="systemd-udevd" exe="/usr/lib/systemd/systemd-udevd" key=(null) ...
- 查看一下
UDev
的存储设备规则,可以看到块设备文件的UUID
是根据UDev
的$env{ID_FS_UUID_ENC}
环境变量而来:
$ cat /usr/lib/udev/rules.d/60-persistent-storage.rules ... # by-label/by-uuid links (filesystem metadata) ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}" ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}" # by-id (World Wide Name) ENV{DEVTYPE}=="disk", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}" ENV{DEVTYPE}=="partition", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}-part%n" # by-partlabel/by-partuuid links (partition metadata) ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_UUID}=="?*", SYMLINK+="disk/by-partuuid/$env{ID_PART_ENTRY_UUID}" ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/by-partlabel/$env{ID_PART_ENTRY_NAME}" # add symlink to GPT root disk ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_GPT_AUTO_ROOT}=="1", SYMLINK+="gpt-auto-root" ...
UDev是如何获取磁盘的UUID的
- 查看一下块设备的
UDev
环境变量:
$ udevadm info -n /dev/sda3 P: /devices/pci0000:00/0000:00:10.0/host0/target0:0:0/0:0:0:0/block/sda/sda3 N: sda3 S: disk/by-path/pci-0000:00:10.0-scsi-0:0:0:0-part3 S: disk/by-uuid/8b980779-48e3-4012-b0d0-e08a939965ec E: DEVLINKS=/dev/disk/by-path/pci-0000:00:10.0-scsi-0:0:0:0-part3 /dev/disk/by-uuid/8b980779-48e3-4012-b0d0-e08a939965ec E: DEVNAME=/dev/sda3 E: DEVPATH=/devices/pci0000:00/0000:00:10.0/host0/target0:0:0/0:0:0:0/block/sda/sda3 E: DEVTYPE=partition E: ID_BUS=scsi E: ID_FS_TYPE=xfs E: ID_FS_USAGE=filesystem E: ID_FS_UUID=8b980779-48e3-4012-b0d0-e08a939965ec E: ID_FS_UUID_ENC=8b980779-48e3-4012-b0d0-e08a939965ec E: ID_MODEL=VMware_Virtual_S E: ID_MODEL_ENC=VMware\x20Virtual\x20S E: ID_PART_ENTRY_DISK=8:0 E: ID_PART_ENTRY_NUMBER=3 E: ID_PART_ENTRY_OFFSET=117440512 E: ID_PART_ENTRY_SCHEME=dos E: ID_PART_ENTRY_SIZE=41943040 E: ID_PART_ENTRY_TYPE=0x83 E: ID_PART_TABLE_TYPE=dos E: ID_PATH=pci-0000:00:10.0-scsi-0:0:0:0 E: ID_PATH_TAG=pci-0000_00_10_0-scsi-0_0_0_0 E: ID_REVISION=1.0 E: ID_SCSI=1 E: ID_TYPE=disk E: ID_VENDOR=VMware_ E: ID_VENDOR_ENC=VMware\x2c\x20 E: MAJOR=8 E: MINOR=3 E: SUBSYSTEM=block E: TAGS=:systemd: E: USEC_INITIALIZED=376987010725
- 查看在执行格式化时的
UEvent
事件,的确和前面看到的是匹配的:
$ mkfs.xfs -f /dev/sda3 meta-data=/dev/sda3 isize=512 agcount=4, agsize=1310720 blks = sectsz=512 attr=2, projid32bit=1 = crc=1 finobt=0, sparse=0 data = bsize=4096 blocks=5242880, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0 ftype=1 log =internal log bsize=4096 blocks=2560, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 $ lsblk -f NAME FSTYPE LABEL UUID MOUNTPOINT sda ├─sda1 xfs b2f2dcbb-03c4-472f-b79c-942895cec1a9 / ├─sda2 swap 2305f62c-86dd-4f32-9f32-c736957996c4 [SWAP] └─sda3 xfs be01f436-56ea-4198-8ed1-9646b2860fc5 sr0 $ udevadm monitor -kup monitor will print the received events for: UDEV - the event which udev sends out after rule processing KERNEL - the kernel uevent KERNEL[394211.609898] change /devices/pci0000:00/0000:00:10.0/host0/target0:0:0/0:0:0:0/block/sda/sda3 (block) ACTION=change DEVNAME=/dev/sda3 DEVPATH=/devices/pci0000:00/0000:00:10.0/host0/target0:0:0/0:0:0:0/block/sda/sda3 DEVTYPE=partition MAJOR=8 MINOR=3 SEQNUM=4892 SUBSYSTEM=block UDEV [394211.622459] change /devices/pci0000:00/0000:00:10.0/host0/target0:0:0/0:0:0:0/block/sda/sda3 (block) ACTION=change DEVLINKS=/dev/disk/by-path/pci-0000:00:10.0-scsi-0:0:0:0-part3 /dev/disk/by-uuid/be01f436-56ea-4198-8ed1-9646b2860fc5 DEVNAME=/dev/sda3 DEVPATH=/devices/pci0000:00/0000:00:10.0/host0/target0:0:0/0:0:0:0/block/sda/sda3 DEVTYPE=partition ID_BUS=scsi ID_FS_TYPE=xfs ID_FS_USAGE=filesystem ID_FS_UUID=be01f436-56ea-4198-8ed1-9646b2860fc5 ID_FS_UUID_ENC=be01f436-56ea-4198-8ed1-9646b2860fc5 ID_MODEL=VMware_Virtual_S ID_MODEL_ENC=VMware\x20Virtual\x20S ID_PART_ENTRY_DISK=8:0 ID_PART_ENTRY_NUMBER=3 ID_PART_ENTRY_OFFSET=117440512 ID_PART_ENTRY_SCHEME=dos ID_PART_ENTRY_SIZE=41943040 ID_PART_ENTRY_TYPE=0x83 ID_PART_TABLE_TYPE=dos ID_PATH=pci-0000:00:10.0-scsi-0:0:0:0 ID_PATH_TAG=pci-0000_00_10_0-scsi-0_0_0_0 ID_REVISION=1.0 ID_SCSI=1 ID_TYPE=disk ID_VENDOR=VMware_ ID_VENDOR_ENC=VMware\x2c\x20 MAJOR=8 MINOR=3 SEQNUM=4892 SUBSYSTEM=block TAGS=:systemd: USEC_INITIALIZED=6987010725
- 再看看
UDev
的源码,可以看到ID_FS_UUID_ENC
环境变量是根据块设备UEvent
事件中的UUID
变量值获得的:
$ cat src/udev/udev-builtin-blkid.c ... static void print_property(struct udev_device *dev, bool test, const char *name, const char *value) { char s[256]; s[0] = '\0'; if (streq(name, "TYPE")) { udev_builtin_add_property(dev, test, "ID_FS_TYPE", value); } else if (streq(name, "USAGE")) { udev_builtin_add_property(dev, test, "ID_FS_USAGE", value); } else if (streq(name, "VERSION")) { udev_builtin_add_property(dev, test, "ID_FS_VERSION", value); } else if (streq(name, "UUID")) { blkid_safe_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_UUID", s); blkid_encode_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_UUID_ENC", s); } else if (streq(name, "UUID_SUB")) { blkid_safe_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB", s); blkid_encode_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB_ENC", s); } else if (streq(name, "LABEL")) { blkid_safe_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_LABEL", s); blkid_encode_string(value, s, sizeof(s)); udev_builtin_add_property(dev, test, "ID_FS_LABEL_ENC", s); ... } ...
EXT文件系统中的UUID
EXT
系列的EXT2
、EXT3
和EXT4
的超级块等主要格式差不多,在这里不做区分。
- 把块设备格式化成
ext3
文件系统:
$ mkfs.ext3 /dev/sda3 mke2fs 1.42.9 (28-Dec-2013) 文件系统标签= OS type: Linux 块大小=4096 (log=2) 分块大小=4096 (log=2) Stride=0 blocks, Stripe width=0 blocks 1310720 inodes, 5242880 blocks 262144 blocks (5.00%) reserved for the super user 第一个数据块=0 Maximum filesystem blocks=4294967296 160 block groups 32768 blocks per group, 32768 fragments per group 8192 inodes per group Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 4096000 Allocating group tables: 完成 正在写入inode表: 完成 Creating journal (32768 blocks): 完成 Writing superblocks and filesystem accounting information: 完成 $ lsblk -f NAME FSTYPE LABEL UUID MOUNTPOINT sda ├─sda1 xfs b2f2dcbb-03c4-472f-b79c-942895cec1a9 / ├─sda2 swap 2305f62c-86dd-4f32-9f32-c736957996c4 [SWAP] └─sda3 ext3 00dfb75c-f5e9-4907-8a4c-34858f2d98db sr0
- 看一下块设备的
ext3
文件系统信息,其中有Filesystem UUID
一项和块设备的UUID
一致:
$ dumpe2fs /dev/sda3 dumpe2fs 1.42.9 (28-Dec-2013) Filesystem volume name: <none> Last mounted on: <not available> Filesystem UUID: 00dfb75c-f5e9-4907-8a4c-34858f2d98db Filesystem magic number: 0xEF53 Filesystem revision #: 1 (dynamic) Filesystem features: has_journal ext_attr resize_inode dir_index filetype sparse_super large_file Filesystem flags: signed_directory_hash Default mount options: user_xattr acl Filesystem state: clean Errors behavior: Continue Filesystem OS type: Linux Inode count: 1310720 Block count: 5242880 Reserved block count: 262144 Free blocks: 5116558 Free inodes: 1310709 First block: 0 Block size: 4096 Fragment size: 4096 Reserved GDT blocks: 1022 Blocks per group: 32768 Fragments per group: 32768 Inodes per group: 8192 Inode blocks per group: 512 Filesystem created: Thu Aug 16 16:35:55 2018 Last mount time: n/a Last write time: Thu Aug 16 16:35:57 2018 Mount count: 0 Maximum mount count: -1 Last checked: Thu Aug 16 16:35:55 2018 Check interval: 0 (<none>) Reserved blocks uid: 0 (user root) Reserved blocks gid: 0 (group root) First inode: 11 Inode size: 256 Required extra isize: 28 Desired extra isize: 28 Journal inode: 8 Default directory hash: half_md4 Directory Hash Seed: 0d887782-32e2-454b-b6b5-54260a188439 Journal backup: inode blocks Journal features: (none) 日志大小: 128M Journal length: 32768 Journal sequence: 0x00000001 Journal start: 0
- 在内核
ext2
文件系统源码的超级块定义中可以看到s_uuid
这个成员(其实还有个label
成员),就是保存UUID
的位置,有16
字节,刚好32
个16
进制数:
$ vi fs/ext2/ext2.h ... /* * Structure of the super block */ struct ext2_super_block { __le32 s_inodes_count; /* Inodes count */ __le32 s_blocks_count; /* Blocks count */ __le32 s_r_blocks_count; /* Reserved blocks count */ ... __u8 s_uuid[16]; /* 128-bit uuid for volume */ ... }; ...
FAT文件系统中的UUID
- 再看看
FAT
文件系统的数据,这个UUID
首次出现在第67
字节的位置:
$ lsblk -f NAME FSTYPE LABEL UUID MOUNTPOINT sda ├─sda1 xfs b2f2dcbb-03c4-472f-b79c-942895cec1a9 / ├─sda2 swap 2305f62c-86dd-4f32-9f32-c736957996c4 [SWAP] └─sda3 vfat 2E9C-E424 sr0 $ hexdump -C /dev/sda3 -n 5120 | grep -i "24 E4 9C 2e" 00000040 00 00 29 24 e4 9c 2e 4e 4f 20 4e 41 4d 45 20 20 |..)$...NO NAME | 00000c40 00 00 29 24 e4 9c 2e 4e 4f 20 4e 41 4d 45 20 20 |..)$...NO NAME |
- 查看
mkdosfs
工具中fat32
文件系统的结构定义,可以发现,如果使用的是fat32
,则正好是使用偏移为67
字节处的volume_id
,而且只有4
字节,顶多存放8
位的UUID
:
$ vi dosfstools-3.0.20/src/mkfs.fat.c /* __attribute__ ((packed)) is used on all structures to make gcc ignore any * alignments */ struct msdos_volume_info { __u8 drive_number; /* BIOS drive number */ __u8 RESERVED; /* Unused */ __u8 ext_boot_sign; /* 0x29 if fields below exist (DOS 3.3+) */ __u8 volume_id[4]; /* Volume ID number */ __u8 volume_label[11]; /* Volume label */ __u8 fs_type[8]; /* Typically FAT12 or FAT16 */ } __attribute__ ((packed)); struct msdos_boot_sector { __u8 boot_jump[3]; /* Boot strap short or near jump */ __u8 system_id[8]; /* Name - can be used to special case partition manager volumes */ __u8 sector_size[2]; /* bytes per logical sector */ __u8 cluster_size; /* sectors/cluster */ __u16 reserved; /* reserved sectors */ __u8 fats; /* number of FATs */ __u8 dir_entries[2]; /* root directory entries */ __u8 sectors[2]; /* number of sectors */ __u8 media; /* media code (unused) */ __u16 fat_length; /* sectors/FAT */ __u16 secs_track; /* sectors per track */ __u16 heads; /* number of heads */ __u32 hidden; /* hidden sectors (unused) */ __u32 total_sect; /* number of sectors (if sectors == 0) */ union { struct { struct msdos_volume_info vi; __u8 boot_code[BOOTCODE_SIZE]; } __attribute__ ((packed)) _oldfat; struct { __u32 fat32_length; /* sectors/FAT */ __u16 flags; /* bit 8: fat mirroring, low 4: active fat */ __u8 version[2]; /* major, minor filesystem version */ __u32 root_cluster; /* first cluster in root directory */ __u16 info_sector; /* filesystem info sector */ __u16 backup_boot; /* backup boot sector */ __u16 reserved2[6]; /* Unused */ struct msdos_volume_info vi; __u8 boot_code[BOOTCODE_FAT32_SIZE]; } __attribute__ ((packed)) _fat32; } __attribute__ ((packed)) fstype; __u16 boot_sign; } __attribute__ ((packed)); int main(int argc, char **argv) { ... volume_id = (u_int32_t) ((create_timeval.tv_sec << 20) | create_timeval.tv_usec); /* Default volume ID = creation time, fudged for more uniqueness */ ... case 'i': /* i : specify volume ID */ volume_id = strtoul(optarg, &tmp, 16); if (*tmp) { printf("Volume ID must be a hexadecimal number\n"); usage(); } break; ... } ... /* Create the filesystem data tables */ static void setup_tables(void) { unsigned num_sectors; unsigned cluster_count = 0, fat_length; struct tm *ctime; struct msdos_volume_info *vi = (size_fat == 32 ? &bs.fat32.vi : &bs.oldfat.vi); if (atari_format) { /* On Atari, the first few bytes of the boot sector are assigned * differently: The jump code is only 2 bytes (and m68k machine code * :-), then 6 bytes filler (ignored), then 3 byte serial number. */ bs.boot_jump[2] = 'm'; memcpy((char *)bs.system_id, "kdosf", strlen("kdosf")); } else memcpy((char *)bs.system_id, "mkfs.fat", strlen("mkfs.fat")); if (sectors_per_cluster) bs.cluster_size = (char)sectors_per_cluster; if (size_fat == 32) { /* Under FAT32, the root dir is in a cluster chain, and this is * signalled by bs.dir_entries being 0. */ root_dir_entries = 0; } if (atari_format) { bs.system_id[5] = (unsigned char)(volume_id & 0x000000ff); bs.system_id[6] = (unsigned char)((volume_id & 0x0000ff00) >> 8); bs.system_id[7] = (unsigned char)((volume_id & 0x00ff0000) >> 16); } else { vi->volume_id[0] = (unsigned char)(volume_id & 0x000000ff); vi->volume_id[1] = (unsigned char)((volume_id & 0x0000ff00) >> 8); vi->volume_id[2] = (unsigned char)((volume_id & 0x00ff0000) >> 16); vi->volume_id[3] = (unsigned char)(volume_id >> 24); } ... } ...
总结
Linux
根据不同类型的文件系统,把UUID
或者卷ID
(还有LABEL
)保存在文件系统的超级块中;- 在为磁盘创建文件系统时,通过
UDev
消息传递给用户态的UDev
守护进程; - 用户态的
UDev
守护进程根据其中的UUID
消息生成ID_FS_UUID_ENC
或者ID_FS_UUID_ENC
变量; - 用户态的
UDev
守护进程根据相应的规则生成/dev/disk/
目录下的设备文件符号链接。
原文地址:https://my.oschina.net/LastRitter/blog/1936259
相关推荐
-
「玩转树莓派」搭建智能家居远程监控系统 服务器
2019-7-3
-
linux服务器centos系统apache路径不区分大小写的解决办法(适用WDCP面板) 服务器
2020-7-16
-
MySQL中的事务和MVCC 服务器
2020-7-4
-
log4j日志写入redis扩展实现(log4j-redis-appender) 服务器
2020-6-21
-
《用OpenResty搭建高性能服务端》笔记 服务器
2019-9-15
-
Vagrant 入门指南 服务器
2019-2-23
-
详解PHPStudy集成环境升级MySQL数据库版本 服务器
2019-3-18
-
12 个 ip 命令范例 服务器
2019-2-27
-
Kubernetes ResourceQuotaController内部实现理及源码分析 服务器
2020-6-16
-
Docker 简明教程 服务器
2019-3-12