• Docker从认识到实践再到底层原理(二-2)|Namespace+cgroups


    在这里插入图片描述

    前言

    那么这里博主先安利一些干货满满的专栏了!

    首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助。

    然后就是博主最近最花时间的一个专栏《Docker从认识到实践再到底层原理》希望大家多多关注!


    Namespace

    Namespace可以隔离的一些资源如下。

    Namespace系统调用参数被隔离的全局系统资源引入内核版本
    UTSCLONE_NEWUTS主机和域名2.6.19
    IPCCLONE_NEWIPC信号量、消息队列和共享内存、进程间通信2.6.19
    PIDCLONE_NEWPID进程编号2.6.24
    NetworkCLONE_NEWNET网络设备、网络栈、端口等2.6.29
    MountCLONE_NEWNS文件系统挂载点2.6.19
    UserCLONE_NEWUSER用户和用户组3.8

    Namespace空间隔离实战

    dd命令

    Linux dd 命令用于读取、转换并输出数据dd 可从标准输入或文件中读取数据,根据指定的格式来转换数据,再输出到文件、设备或标准输出。

    dd OPTION
    
    • 1

    参数

    • if=文件名:输入文件名,默认为标准输入。即指定源文件
    • of文件名:输出文件名,默认为标准输出。即指定目的文件
    • ibs=bytes:一次读入 bytes 个字节,即指定一个块大小为 bytes 个字节
    • obs=bytes:一次输出 bytes 个字节,即指定一个块大小为 bytes 个字节
    • bs=bytes:同时设置读入/输出的块大小为 bytes 个字节。
    • cbs=bytes:一次转换 bytes 个字节,即指定转换缓冲区大小
    • skip=blocks:从输入文件开头跳过 blocks 个块后再开始复制
    • seek=blocks:从输出文件开头跳过 blocks 个块后再开始复制
    • count=blocks:仅拷贝 blocks 个块,块大小等于 ibs 指定的字节数
    • conv=<关键字>:关键字有11种。

    可以用它来生成一个指定大小的空白文件

    dd if=/dev/zero of=test.img bs=8k count=1024
    
    • 1

    /dev/zero是操作系统的一个特殊文件,可以产生连续不断的空白字符流

    这里就可以产生一个8M的文件。

    (base) [yufc@ALiCentos7:~/Src/Bit-Courses/DockerSrc/data]$ dd if=/dev/zero of=test.img bs=8k count=1024
    1024+0 records in
    1024+0 records out
    8388608 bytes (8.4 MB, 8.0 MiB) copied, 0.00964433 s, 870 MB/s
    (base) [yufc@ALiCentos7:~/Src/Bit-Courses/DockerSrc/data]$ ll -h
    total 8.0M
    -rw-rw-r-- 1 yufc yufc 8.0M Aug 29 19:50 test.img
    (base) [yufc@ALiCentos7:~/Src/Bit-Courses/DockerSrc/data]$ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    也可以用来做一个大小写转换

    先创建一个test.txt然后里面写Hello World!
    在这里插入图片描述
    在这里插入图片描述

    mkfs命令

    用于在设备上创建 Linux 文件系统,俗称格式化,比如我们使用 U 盘的时候可以格式化。

    mkfs [-V] [-t fstype] [fs-options] filesys [blocks]
    
    • 1

    参数

    Shell
    -t fstype : 指定要建立何种文件系统;如 ext3,ext4
    filesys : 指定要创建的文件系统对应的设备文件名; blocks:指定文件系统的磁盘块数。
    -V : 详细显示模式 
    fs-options : 传递给具体的文件系统的参数
    
    • 1
    • 2
    • 3
    • 4
    • 5

    我们可以把刚才dd命令生成的test.img格式化成一个磁盘。

    (base) [yufc@ALiCentos7:~/Src/Bit-Courses/DockerSrc/data]$ ls
    out.txt  test.img  test.txt
    (base) [yufc@ALiCentos7:~/Src/Bit-Courses/DockerSrc/data]$ mkfs -t ext4 ./test.img
    mke2fs 1.42.9 (28-Dec-2013)
    ./test.img is not a block special device.
    Proceed anyway? (y,n) y
    Discarding device blocks: done                            
    Filesystem label=
    OS type: Linux
    Block size=1024 (log=0)
    Fragment size=1024 (log=0)
    Stride=0 blocks, Stripe width=0 blocks
    2048 inodes, 8192 blocks
    409 blocks (4.99%) reserved for the super user
    First data block=1
    Maximum filesystem blocks=8388608
    1 block group
    8192 blocks per group, 8192 fragments per group
    2048 inodes per group
    
    Allocating group tables: done                            
    Writing inode tables: done                            
    Creating journal (1024 blocks): done
    Writing superblocks and filesystem accounting information: done
    
    (base) [yufc@ALiCentos7:~/Src/Bit-Courses/DockerSrc/data]$ ll
    total 1184
    -rw-rw-r-- 1 yufc yufc      12 Aug 29 19:56 out.txt
    -rw-rw-r-- 1 yufc yufc 8388608 Aug 29 20:09 test.img
    -rw-rw-r-- 1 yufc yufc      12 Aug 29 19:54 test.txt
    (base) [yufc@ALiCentos7:~/Src/Bit-Courses/DockerSrc/data]$
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    df命令

    Linux df(英文全拼:disk free) 命令用于显示目前在 Linux 系统上的文件系统磁盘使用情况统计。

    df [OPTION]... [FILE]...
    
    • 1

    参数

    -a, --all 包含所有的具有 0 Blocks 的文件系统
    -h, --human-readable 使用人类可读的格式(预设值是不加这个选项的...)
    -H, --si 很像 -h, 但是用 1000 为单位而不是用 1024
    -t, --type=TYPE 限制列出文件系统的 TYPE
    -T, --print-type 显示文件系统的形式
    
    • 1
    • 2
    • 3
    • 4
    • 5

    案例

    #查看磁盘使用情况 
    df -h 
    #查看磁盘的系统类型 
    df -Th
    
    • 1
    • 2
    • 3
    • 4
    (base) [yufc@ALiCentos7:~/Src/Bit-Courses/DockerSrc/data]$ df -h
    Filesystem      Size  Used Avail Use% Mounted on
    devtmpfs        857M     0  857M   0% /dev
    tmpfs           868M     0  868M   0% /dev/shm
    tmpfs           868M  572K  867M   1% /run
    tmpfs           868M     0  868M   0% /sys/fs/cgroup
    /dev/vda1        40G   26G   13G  68% /
    tmpfs           174M     0  174M   0% /run/user/1000
    (base) [yufc@ALiCentos7:~/Src/Bit-Courses/DockerSrc/data]$ df
    Filesystem     1K-blocks     Used Available Use% Mounted on
    devtmpfs          877396        0    877396   0% /dev
    tmpfs             887988        0    887988   0% /dev/shm
    tmpfs             887988      572    887416   1% /run
    tmpfs             887988        0    887988   0% /sys/fs/cgroup
    /dev/vda1       41152812 26333864  12915240  68% /
    tmpfs             177600        0    177600   0% /run/user/1000
    (base) [yufc@ALiCentos7:~/Src/Bit-Courses/DockerSrc/data]$
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    mount命令

    mount 命令用于加载文件系统到指定的加载点。此命令的也常用于挂载光盘,使我们 可以访问光盘中的数据,因为你将光盘插入光驱中,Linux 并不会自动挂载,必须使用 Linux mount 命令来手动完成挂载。

    Linux 系统下不同目录可以挂载不同分区和磁盘设备,它的目录和磁盘分区是分离的, 可以自由组合(通过挂载)

    不同的目录数据可以跨越不同的磁盘分区或者不同的磁盘设备。 挂载的实质是为磁盘添加入口(挂载点)。

    什么是挂载?

    比如U盘插进电脑之后,在某个目录下弹出来,这个目录就是挂载点。就是给这个U盘添加入口。

    mount [-l]
    mount [-t vfstype] [-o options] device dir
    
    • 1
    • 2

    参数

    常见参数
    显示已加载的文件系统列表:
    -t: 加载文件系统类型支持常见系统类型的 ext3,ext4iso9660,tmpfs,xis 等,大部分情况可以不指定,mount可以自己识别
    -o options 主要用来描述设备或档案的挂接方式。
    	loop:用来把一个文件当成硬盘分区挂接上系统
    	ro:采用只读方式挂接设备
    	rw:采用读写方式挂接设备
    device: 要挂接(mount)的设备
    dir: 挂载点的目录
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    unshare命令

    unshare 主要能力是使用与父程序不共享的名称空间运行程序。

    unshare [options] program [arguments]
    
    • 1

    参数

    参数含义
    -i, --ipc不共享 IPC 空间
    -m, --mount不共享 Mount 空间
    -n, --net不共享 Net 空间
    -p, --pid不共享 PID 空间
    -u, --uts不共享 UTS 空间
    -U, --user不共享用户
    -V, --version版本查看
    –fork执行 unshare 的进程 fork 一个新的子进程,
    在子进程里执行 unshare 传入的参数
    –mount-proc执行子进程前,将 proc 优先挂载过去

    在这里插入图片描述

    这幅图里面,一开始unshare创建了一个子进程,隔离的进程是bash

    所以unshare下面我去修改hostname,是修改这个子进程的hostname,然后exit也是退出这个子进程而已,不会影响外面的hostname。

    进程隔离实战操作(PID隔离)
    unshare -p /bin/bash
    
    • 1

    我们会发现,直接报错。

    (base) [yufc@ALiCentos7:~/Src/Bit-Courses/DockerSrc/data]$ unshare -p /bin/bash
    unshare: unshare failed: Operation not permitted
    (base) [yufc@ALiCentos7:~/Src/Bit-Courses/DockerSrc/data]$
    
    • 1
    • 2
    • 3

    为什么会报这个错呢?

    因为如果直接-p去隔离PID信息,会导致新的进程看不见原来父进程(bash)的信息,会导致他后续会有问题。

    也就是说这个新进程,不知道自己的父进程是谁了,这是不允许的。所以。

    unshare -p --fork /bin/bash # 这样即可
    
    • 1

    我们再打开另一个bash,然后ps -ef查看一下进程信息。

    发现是一样的,并没有完成隔离。

    (base) [yufc@ALiCentos7:~]$ ps -ef
    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 Aug16 ?        00:00:36 /usr/lib/systemd/systemd --switched-root --system --deserial
    root         2     0  0 Aug16 ?        00:00:00 [kthreadd]
    root         4     2  0 Aug16 ?        00:00:00 [kworker/0:0H]
    root         6     2  0 Aug16 ?        00:00:03 [ksoftirqd/0]
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    [root@ALiCentos7:/home/yufc/Src/Bit-Courses/DockerSrc/data]$ ps -ef
    UID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 Aug16 ?        00:00:36 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
    root         2     0  0 Aug16 ?        00:00:00 [kthreadd]
    root         4     2  0 Aug16 ?        00:00:00 [kworker/0:0H]
    root         6     2  0 Aug16 ?        00:00:03 [ksoftirqd/0]
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这是为什么呢,这是因为我们/proc目录下本来就有很多进程,大家都看得到的。

    因此要采用另一个参数。

    unshare -p --fork --mount-proc /bin/bash
    
    • 1

    此时再次ps -ef查看进程。

    (base) [yufc@ALiCentos7:~/Src/Bit-Courses/DockerSrc/data]$ sudo unshare -p --fork --mount-proc /bin/bash
    [root@ALiCentos7:/home/yufc/Src/Bit-Courses/DockerSrc/data]$ ps -efUID        PID  PPID  C STIME TTY          TIME CMD
    root         1     0  0 09:48 pts/0    00:00:00 /bin/bash
    root        20     1  0 09:48 pts/0    00:00:00 ps -ef
    [root@ALiCentos7:/home/yufc/Src/Bit-Courses/DockerSrc/data]$
    
    • 1
    • 2
    • 3
    • 4
    • 5

    此时已经看不到什么进程了,我们完成进程隔离了。

    mount隔离(文件系统隔离)

    第一步:打开第一个 shell 窗口 A,执行命令,df -h ,查看主机默认命名空间的磁盘挂载情况。

    (base) [yufc@ALiCentos7:~/Src/Bit-Courses/DockerSrc/data]$ df -h
    Filesystem      Size  Used Avail Use% Mounted on
    devtmpfs        857M     0  857M   0% /dev
    tmpfs           868M     0  868M   0% /dev/shm
    tmpfs           868M  576K  867M   1% /run
    tmpfs           868M     0  868M   0% /sys/fs/cgroup
    /dev/vda1        40G   25G   13G  67% /
    tmpfs           174M     0  174M   0% /run/user/1000
    /dev/loop0      6.8M   77K  6.2M   2% /home/yufc/Src/Bit-Courses/DockerSrc/data/testmymount
    (base) [yufc@ALiCentos7:~/Src/Bit-Courses/DockerSrc/data]$ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    第二步:打开一个新的shell窗口,执行Mount隔离命令

    unshare --mount --fork /bin/bash
    
    • 1

    第三步:在窗口 B 中添加新的磁盘挂载

    [root@ALiCentos7:/home/yufc]$ cd /home/yufc/Src/Bit-Courses/DockerSrc/data
    [root@ALiCentos7:/home/yufc/Src/Bit-Courses/DockerSrc/data]$ dd if=/dev/zero of=data2.img bs=8k count=10240
    10240+0 records in
    10240+0 records out
    83886080 bytes (84 MB, 80 MiB) copied, 0.0747759 s, 1.1 GB/s
    [root@ALiCentos7:/home/yufc/Src/Bit-Courses/DockerSrc/data]$ ls
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    格式化这个文件。

    [root@ALiCentos7:/home/yufc/Src/Bit-Courses/DockerSrc/data]$ mkfs -t ext4 ./data2.img
    mke2fs 1.42.9 (28-Dec-2013)
    ./data2.img is not a block special device.
    Proceed anyway? (y,n) y
    Discarding device blocks: done                            
    Filesystem label=
    OS type: Linux
    Block size=1024 (log=0)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    挂载到当前的一个目录下。

    [root@ALiCentos7:/home/yufc/Src/Bit-Courses/DockerSrc/data]$ tree .
    .
    ├── data2.img
    ├── out.txt
    ├── test.img
    ├── testmymount
    │   └── lost+found
    ├── testmymount2
    └── test.txt
    
    3 directories, 4 files
    [root@ALiCentos7:/home/yufc/Src/Bit-Courses/DockerSrc/data]$ cd /home/yufc/Src/Bit-Courses/DockerSrc/data^C
    [root@ALiCentos7:/home/yufc/Src/Bit-Courses/DockerSrc/data]$ mount -t ext4 ./data2.img ./testmymount2
    [root@ALiCentos7:/home/yufc/Src/Bit-Courses/DockerSrc/data]$ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    我们发现,在这个bash下面是可以看到这个挂载点的。

    在这里插入图片描述
    但是,外面的bash是看不到的!

    在这里插入图片描述

    因此我们成功完成了一次挂载点的隔离!

    现在无论我们在testmymount2这个文件夹里面去添加,或者修改任何文件,外面都是找不到的!

    因此,在当前的进程中,是有文件隔离的!!

    Cgroups

    什么是cgroups

    cgroups(Control Groups)是 linux 内核提供的一种机制,这种机制可以根据需求把系列系统任务及其子任务整合(或分隔)到按资源划分等级的不同组内,从而为系统资源管理提供一个统一的框架。简单说,cgroups 可以限制、记录任务组所使用的物理资源。本质上来说,cgroups 是内核附加在程序上的一系列钩子(hook),通过程序运行时对资源的调度触发相应的钩子以达到资源追踪和限制的目的。

    cgroups的用途
    • Resource limitation: 限制资源使用,例: 内存使用上限/cpu 的使用限制

    • Prioritization: 优先级控制,例:CPU 利用/磁盘IO 吞吐

    • Accounting:一些审计或一些统计

    • Control: 挂起进程/恢复执行进程

    cgroups可以控制的子系统
    pidstat命令

    pidstat是 sysstat 的一个命令,用于监控全部或指定进程的 CPU、内存、线程、设备1O 等系统资源的占用情况。Pidstat 第一次采样显示自系统启动开始的各项统计信息后续采样将显示自上次运行命令后的统计信息。用户可以通过指定统计的次数和时间来获得所需的统计信息。

    pidstat [ 选项 ] [ <时间间隔> ] [ <次数> ]
    
    • 1
    -u : 默认参数,显示各进程的 CPU 使用统计
    -r : 显示各进程的内存使用统计
    -d : 显示各进程的IO使用情况
    -p : 指定进程号,ALL表示所有进程
    -C : 指定命令
    -l : 显示命令名和所有参数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    stress

    stress 是 Linux 的一个压力测试工具,可以对 CPU、Memory、IO、磁盘进行压力测试。

    stress [OPTION [ARG]]
    
    • 1
    -c, --cpu N : 产生 N个进程,每个进程都循环调用 sqrt 函数产生 CPU 压力。
    -i, --io N : 产生 N个进程,每个进程循环调用 sync 将内存缓冲区内容写到磁盘上,产生I0 压力。通过系统调用 sync 刷新内存缓冲区数据到磁盘中,以确保同步。如果缓冲区内数据较少,写到磁盘中的数据也较少,不会产生IO压力。在 SSD 磁盘环境中尤为明显,很可能 iowait 总是 0,却因为大量调用系统调用 sync,导致系统 CPU 使用率 sys 升高。
    -m, --vm N : 产生 N 个进程,每个进程循环调用 malloc/free 函数分配和释放内存。
    --vm-bytes B : 指定分配内存的大小
    --vm-keep : 一直占用内存,区别于不断的释放和重新分配(默认是不断释放并重新分配内存)
    -d, --hdd N : 产生 N个不断执行 write和 unlink 函数的进程(创建文件,写入内容,删除文件)
    --hdd-bytes B : 指定文件大小
    -t, --timeout N : 在 N秒后结束程序
    -q, --quiet:程序在运行的过程中不输出信息
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    实操。

    我们在打开另一个shell,对cpu进行一个监控

    pidstat -C stress -p ALL -u 2 10000
    
    • 1

    这里的意思是,监控stress这个命令相关的所有进程的cpu使用情况,2s输出一次,一共输出10000次。

    在另一个shell中启动stress。

    stress -c 1
    
    • 1

    此时我们可以通过shell查看发现,此时cpu已经打满了。

    在这里插入图片描述

    测试一下-i选项。

    在这里插入图片描述

    测试一下-m选项

    stress -m 1 --vm-bytes 50m # 申请50mb的内存
    
    • 1
    pidstat -C stress -p ALL -r 2 10000 # 查看内存就不是-u了,而是-r
    
    • 1

    在这里插入图片描述

    测试一下-d选项。

    然后监控读写信息。

    stress -d 1
    
    • 1
    pidstat -C stress -p ALL -d 2 10000
    
    • 1

    在这里插入图片描述

    测试一下-t选项

    stress -d 1 -t 3 # 3s后停止
    
    • 1
    cgroups版本信息查看
    (base) [yufc@ALiCentos7:~]$ cat /proc/filesystems | grep cgroup
    nodev   cgroup
    (base) [yufc@ALiCentos7:~]$
    
    • 1
    • 2
    • 3

    如果看到 cgroup2,表示支持 cgroup v2。

    cgroups子系统查看
    (base) [yufc@ALiCentos7:~]$ cat /proc/cgroups
    #subsys_name    hierarchy       num_cgroups     enabled
    cpuset  8       1       1
    cpu     2       4       1
    cpuacct 2       4       1
    memory  7       2       1
    devices 9       1       1
    freezer 11      1       1
    net_cls 4       1       1
    blkio   10      1       1
    perf_event      3       1       1
    hugetlb 6       1       1
    pids    5       1       1
    net_prio        4       1       1
    (base) [yufc@ALiCentos7:~]$ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    cgroups挂载信息查看
    (base) [yufc@ALiCentos7:~]$ mount | grep cgroup 
    tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
    cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
    cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
    cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
    cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)
    cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
    cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
    cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
    cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
    cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
    cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
    cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
    (base) [yufc@ALiCentos7:~]$
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    查看一个进程上的cgroup限制

    以当前 shell 进程为例,查看进程的 cgroup。

    (base) [yufc@ALiCentos7:~]$ cat /proc/$$/cgroup
    11:freezer:/
    10:blkio:/
    9:devices:/
    8:cpuset:/
    7:memory:/
    6:hugetlb:/
    5:pids:/
    4:net_prio,net_cls:/
    3:perf_event:/
    2:cpuacct,cpu:/
    1:name=systemd:/user.slice/user-1000.slice/session-2594.scope
    (base) [yufc@ALiCentos7:~]$ 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    树莓派开机自动播放U盘里的照片和视频
    H5游戏开发-搭建开发环境
    影响Windows 和 macOS平台,黑客利用 Adobe CF 漏洞部署恶意软件
    没有二十年功力,写不出Thread.sleep(0)这一行“看似无用”的代码!
    HBuilder左侧工具栏不见了
    lua-web-utils和proxy程序示例
    【附源码】计算机毕业设计JAVA校园社团管理平台
    HTML+CSS+JS实现【别踩鸡块】,ikun粉快来瞅瞅(含源码链接在文末+思路)
    华为十年架构师实战经验总结:大规模分布式系统架构与设计实战
    树莓派4b装系统到运行 Blazor Linux 本地程序全记录
  • 原文地址:https://blog.csdn.net/Yu_Cblog/article/details/132615756