容器技术 — Cgroups 与 Namespaces 支撑实现的操作系统虚拟化

目录

文章目录

  • 目录
  • 操作系统虚拟化(容器技术)的发展历程
  • Chroot
  • Cgroups
    • Cgroup Subsystems
    • Cgroup Filesystem
    • Cgroup Hierarchy
    • Cgroups 的操作规则
    • Cgroups 的代码实现
  • Namespaces
    • UTS namespace
    • PID namespace
    • IPC namespace
    • Mount namespace
    • Network namespace
    • User namespace
  • Docker 对 Cgroups 和 Namespaces 的应用
  • 参考文档

操作系统虚拟化(容器技术)的发展历程

1979 年,UNIX 的第 7 个版本引入了 Chroot 特性。Chroot 现在被认为是第一个操作系统虚拟化(Operating system level virtualization)技术的原型,本质是一种操作系统文件系统层的隔离技术。

2006 年,Google 发布了在 Linux 上运行的 Process Container(进程容器)技术,其目标是提供一种类似于 Virtual Mahine(计算机虚拟化技术)的、但主要针对 Process 的操作系统级别资源限制、优先级控制、资源审计能力和进程控制能力。

2007 年,Google 推动 Process Container 代码合入 Linux Kernel。同时由于 Container 这一命名在 Kernel 具有许多不同的含义,所以为了避免代码命名的混乱,就将 Process Container 更名为了 Control Groups,简称:Cgroups。

2008 年,Linux 社区整合了 Chroot、Cgroups、Namespaces、SELinux、Seccomp 等多种技术并发布了 LXC(Linux Container)v0.1.0 版本。LXC 通过将 Cgroups 的资源配额管理能力和 Namespace 的资源视图隔离能力进行组合,实现了完备的轻量级操作系统虚拟化。

2013 年 3 月 15 日,在加利福尼亚州圣克拉拉召开的 Python 开发者大会上,DotCloud 的创始人兼首席执行官 Solomon Hvkes 在一场仅 5 分钟的微型演讲中,首次发布了基于 LXC 封装的 Docker Container,并于会后将其源码开源并托管到 Github。

在这里插入图片描述

Chroot

Chroot 是一个可供 User Process 调用的 System Call 接口,可以让一个 Process 把指定的目录作为根目录(Root Directory),随后 Process 所有的文件系统操作都只能在这个指定目录中进行。故称之为 Change Root。

chroot() 的函数原型非常简单:

  • 调用权限:Root 用户。
  • 形参列表
    • path:一个指向字符串的指针,是一个绝对路径,表示将 Process 的根目录更改为的该目录路径。
  • 函数返回
    • 成功:返回 0;
    • 失败:返回 -1。
#include <unistd.h>int chroot(const char *path);

需要注意的是,在更改了 Process 的根目录后,Process 只能访问新的根目录以及其子目录中的文件和资源。因此,在调用 chroot() 后,应确保 Process 所需要访问的所有文件和资源都存在于新的根目录下。

chroot() 目前主要主要用于:

  1. 安全隔离场景:限制将 Process 的访问范围,以此提高系统的安全性。
  2. 调试环境场景:创建一个与主系统隔离的环境,用于调试、测试和运行 Process。
  3. 系统救援场景:在 Linux 操作系统损坏或遭受攻击时,可以使用 chroot 将 Process 切换到受损系统的根目录中,以便进行修复和救援操作。

可见,chroot() 确实在 Linux File System(文件系统)层面提供了针对 Process 的隔离性,但并不提供完全的安全隔离,无法阻止其他方式的攻击。因此,要想实现 Processes 之间的安全隔离,还需要需采取其他安全措施。

Cgroups

Cgroups(Control Groups)是 Linux Kernel 提供的一种针对 User Process 或 Kernel Thread 的操作系统资源配额与管理技术,主要包括以下 4 个方面:

  1. 资源配额:限制进程对某一系统资源的用量配额。
  2. 优先级:当发生资源竞争时,优先保障哪些进程的资源使用。
  3. 审计:监控及报告进程对资源限制及使用。
  4. 控制:控制进程的状态,例如:运行、挂起、恢复。

Cgroups 设计与实现的核心概念如下图所示,包括:

  1. libcgroups:提供了一组编程接口库和应用程序。
  2. Tasks:User Process 和 Kernel Thread 的统一抽象。因为在 Kernel 中 User Process 或 Kernel Thread 实际上只通过 clone() SCI 传递的参数不同来进行区分,它们都使用了 task_struct 描述。
  3. Subsystems:可控资源的类型定义。
  4. Control Group(cgroup):是一个用于关联若干 Tasks 和 Subsystems 的资源控制组描述。下文中使用小写的 cgroup 来表述一个特定的 Control Group。
  5. Cgroup Filesystem:通过 VFS(虚拟文件系统)统一文件接口的方式向 Userspace 提供 cgroup 的配置入口。

在这里插入图片描述

Cgroup Subsystems

Cgroups 将多种可被管控的系统资源类型定义为 Subsystems(子系统),包括有:

  • cpu:限制 Task 的单颗 CPU Core 使用率。
  • cpuset:限制 Task 使用的 CPU Core 集合。
  • cpuacct:统计 Task 的 CPU 使用报告(Accounting)。
  • memory:限制 Task 使用的 Memory 容量。
  • hugetlb:限制 Task 的大页内存容量。
  • devices:限制 Task 能够访问的设备。
  • blkio:限制 Task 的 Block I/O 使用率。
  • net_cls:限制 Task 的网络数据包类型(Network Classifier)和 Net I/O 使用率。
  • net_prio:设置 Task 的网络流量(Network Traffic)处理优先级。
  • namespace:限制 Task 使用不同的 Namespaces。
  • freezer:挂起或者恢复指定的 Task。
  • perf_event:允许使用 perf 工具来进行监控。
  • pids:限制 cgroup 关联的 Tasks 数量。
  • 等等。

这些 Subsystems 的定义主要是为了提供相应的配置入口,而具体对系统资源进行限制的实现则是充分复用了 Kernel 自身的多种功能模块来完成,例如:

  • cpu Subsystem 依赖 Kernel Process Scheduler 实现。
  • memory Subsystem 依赖 Kernel Memory Manager 实现。
  • net_cls Subsystem 依赖 Kerne Traffic Control 实现。
  • 等等。

通过 CLI 可以查看系统中支持的 Cgroup Subsystems:

$ sudo yum install libcgroup-tools$ lssubsys -a
cpuset
cpu,cpuacct
blkio
memory
devices
freezer
net_cls,net_prio
perf_event
hugetlb
pids
rdma

Cgroup Filesystem

Cgroups 通过 Kernel VFS(Virtual File System,虚拟文件系统)文件接口的方式向 Userspace 提供了统一的 cgroup 配置入口。

通过 CLI 可以查看当前 Cgroup Filesystem 的挂载路径与内容:

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
...
tmpfs            16G     0   16G   0% /sys/fs/cgroup$ ll /sys/fs/cgroup/
总用量 0
drwxr-xr-x. 4 root root  0 61 16:22 blkio
lrwxrwxrwx. 1 root root 11 61 16:22 cpu -> cpu,cpuacct
lrwxrwxrwx. 1 root root 11 61 16:22 cpuacct -> cpu,cpuacct
drwxr-xr-x. 4 root root  0 61 16:22 cpu,cpuacct
drwxr-xr-x. 2 root root  0 61 16:22 cpuset
drwxr-xr-x. 4 root root  0 61 16:22 devices
drwxr-xr-x. 2 root root  0 61 16:22 freezer
drwxr-xr-x. 2 root root  0 61 16:22 hugetlb
drwxr-xr-x. 4 root root  0 61 16:22 memory
lrwxrwxrwx. 1 root root 16 61 16:22 net_cls -> net_cls,net_prio
drwxr-xr-x. 2 root root  0 61 16:22 net_cls,net_prio
lrwxrwxrwx. 1 root root 16 61 16:22 net_prio -> net_cls,net_prio
drwxr-xr-x. 2 root root  0 61 16:22 perf_event
drwxr-xr-x. 4 root root  0 61 16:22 pids
drwxr-xr-x. 4 root root  0 61 16:22 systemd

可以看见,默认的情况下,Cgroups 会为 Subsystems 创建好它们各自的 Cgroup Filesytem,内含了用于设定资源配额、以及用于关联到若干个 Tasks 所需要的文件。如下所示。

$ ll /sys/fs/cgroup/memory/
总用量 0
-rw-r--r--.  1 root root 0 61 16:22 cgroup.clone_children
--w--w--w-.  1 root root 0 61 16:22 cgroup.event_control
-rw-r--r--.  1 root root 0 61 16:22 cgroup.procs
-r--r--r--.  1 root root 0 61 16:22 cgroup.sane_behavior
-rw-r--r--.  1 root root 0 61 16:22 memory.failcnt
--w-------.  1 root root 0 61 16:22 memory.force_empty
-rw-r--r--.  1 root root 0 61 16:22 memory.kmem.failcnt
-rw-r--r--.  1 root root 0 61 16:22 memory.kmem.limit_in_bytes
-rw-r--r--.  1 root root 0 61 16:22 memory.kmem.max_usage_in_bytes
-r--r--r--.  1 root root 0 61 16:22 memory.kmem.slabinfo
-rw-r--r--.  1 root root 0 61 16:22 memory.kmem.tcp.failcnt
-rw-r--r--.  1 root root 0 61 16:22 memory.kmem.tcp.limit_in_bytes
-rw-r--r--.  1 root root 0 61 16:22 memory.kmem.tcp.max_usage_in_bytes
-r--r--r--.  1 root root 0 61 16:22 memory.kmem.tcp.usage_in_bytes
-r--r--r--.  1 root root 0 61 16:22 memory.kmem.usage_in_bytes
-rw-r--r--.  1 root root 0 61 16:22 memory.limit_in_bytes
-rw-r--r--.  1 root root 0 61 16:22 memory.max_usage_in_bytes
-rw-r--r--.  1 root root 0 61 16:22 memory.memsw.failcnt
-rw-r--r--.  1 root root 0 61 16:22 memory.memsw.limit_in_bytes
-rw-r--r--.  1 root root 0 61 16:22 memory.memsw.max_usage_in_bytes
-r--r--r--.  1 root root 0 61 16:22 memory.memsw.usage_in_bytes
-rw-r--r--.  1 root root 0 61 16:22 memory.move_charge_at_immigrate
-r--r--r--.  1 root root 0 61 16:22 memory.numa_stat
-rw-r--r--.  1 root root 0 61 16:22 memory.oom_control
----------.  1 root root 0 61 16:22 memory.pressure_level
-rw-r--r--.  1 root root 0 61 16:22 memory.soft_limit_in_bytes
-r--r--r--.  1 root root 0 61 16:22 memory.stat
-rw-r--r--.  1 root root 0 61 16:22 memory.swappiness
-r--r--r--.  1 root root 0 61 16:22 memory.usage_in_bytes
-rw-r--r--.  1 root root 0 61 16:22 memory.use_hierarchy
-rw-r--r--.  1 root root 0 61 16:22 notify_on_release
-rw-r--r--.  1 root root 0 61 16:22 release_agent
drwxr-xr-x. 55 root root 0 61 16:23 system.slice
-rw-r--r--.  1 root root 0 61 16:22 tasks
drwxr-xr-x.  2 root root 0 61 16:23 user.slice

其中 cgroup 的 Core 接口文件以 cgroup 为前缀:

  • cgroup.clone_children:标识 Child cgroup 是否会继承 Parent cgroup。默认值为 0,表示不继承。
  • cgroup.procs:当是 Root cgroup 时,会记录该 Hierarchy 中所有的 PIDs。
  • 等等。

而 memory 为前缀的则为 Controller 接口文件,是 Cgroups 资源分配模型设计的控制器,包括:

  1. 权重(weight):按照权重比率来分配资源。
  2. 限制(max):限制资源被过度使用。
  3. 保护:可以是硬保护,也可能是软保护。
  4. 分配:资源分配参数。

其余还有一个管理接口文件,例如:

  • notify_on_release:标识当这个 cgroup 最后一个 Task 退出的时候是否执行 release_agent。
  • release_agent:是一个路径,用作 Task 退出后自动清理掉不再使用的 cgroup。
  • tasks:记录了关联到这个 cgroup 的 Tasks 列表。

Cgroup Hierarchy

Cgroups 使用 Filesystem 的方式来提供操作入口,所带来的另一个好处就是支持 Cgroup Hierarchy(层次化)组织形式,表现为一棵树状的结构。

当用户在 Parent cgroup 中创建了一个 Child cgroup 之后,Child cgroup 同样会自动创建所需要的配置文件,并可以配置是否继承 Parent cgroup 的相关配置。如下图所示。

$ mkdir /sys/fs/cgroup/memory/cgrp1/$ ls /sys/fs/cgroup/memory/cgrp1/
cgroup.clone_children  memory.kmem.limit_in_bytes          memory.kmem.tcp.usage_in_bytes  memory.memsw.max_usage_in_bytes  memory.soft_limit_in_bytes  tasks
cgroup.event_control   memory.kmem.max_usage_in_bytes      memory.kmem.usage_in_bytes      memory.memsw.usage_in_bytes      memory.stat
cgroup.procs           memory.kmem.slabinfo                memory.limit_in_bytes           memory.move_charge_at_immigrate  memory.swappiness
memory.failcnt         memory.kmem.tcp.failcnt             memory.max_usage_in_bytes       memory.numa_stat                 memory.usage_in_bytes
memory.force_empty     memory.kmem.tcp.limit_in_bytes      memory.memsw.failcnt            memory.oom_control               memory.use_hierarchy
memory.kmem.failcnt    memory.kmem.tcp.max_usage_in_bytes  memory.memsw.limit_in_bytes     memory.pressure_level            notify_on_release

在这里插入图片描述

最后,在每个 cgroup 的 Filesystem 中都包含了一个 tasks 文件,用于保存关联到当前 cgroup 的 Tasks 列表。如果我们想向某个 cgroup 添加一个 User Process 时,就可以把它的 PID 写入到 tasks 文件中。如下:

$ cd /sys/fs/cgroup/memory/cgrp1    # 进入 cgrp1
$ echo 1029 > tasks                 # 将 PID 1029 的 Process 添加到 cgrp1 的 tasks 列表

Cgroups 的操作规则

用户在使用 Cgroups 时,必须按照一定的操作规则,否则会出现错误。操作规则的目的是为了避免资源配额的配置存在冲突。

  1. 一个 Hierarchy 可以 Attach 多个 Subsystems,如下图将 cpu 和 memory Subsystems Attached 到了同一个 Hierarchy。
    在这里插入图片描述

  2. 一个已经被 Attached 的 Subsystem 只能被再次 Attach 在一个空的 Hierarchy 上,不能 Attach 到一个已经 Attached 了其他 Subsystems 的 Hierarchy 上,如下图 cpu Subsystem 已经 Attached 到了 Hierarchy A,并且 memory Subsystem 已经附加到了 Hierarchy B。因此 cpu Subsystem 不能再 Attach 到 Hierarchy B,只能 Attach 另一个空的 Hierarchy C 上。
    在这里插入图片描述

  3. 每个 Task 只能在同一个 Hierarchy 的唯一一个 cgroup tasks 里,并且可以在多个不同 Hierarchy 的 cgroup tasks 里。如下图,这样保证了对于一个 Task 的同一种 cgroup 配额是唯一的。
    在这里插入图片描述

  4. 子进程在被 fork 出时自动继承父进程所在 cgroups,但是 fork 之后就可以按需调整到其他 cgroup,如下图:
    在这里插入图片描述

Cgroups 的代码实现

现在回头再看 Kernel 中对 cgroup 的声明定义,它被设计成了一个树数据结构:

struct cgroup {...// 下面 3 个字段把 cgroup 设计成了一个树数据结构struct list_head sibling;   // 兄弟节点struct list_head children;  // 子节点struct cgroup *parent;      // 父节点struct dentry *dentry;      // cgroup 对应的目录对象// cgroup 关联的 subsystems 对象struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT]; ...
};

在这里插入图片描述

默认情况下,在 Kernel 启动过程中,会自动实例化一个 rootnode(根节点),并将所有 Subsystems 的 cgroup FS 关联到此 rootnode。

static struct cgroupfs_root rootnode;struct cgroupfs_root {struct super_block *sb;            // Root cgroup FS 的挂载点(VFS 使用)...struct list_head subsys_list;      // Root cgroup 绑定的 Subsystems 列表struct cgroup top_cgroup;          // Root cgroup 对象int number_of_cgroups;             // Root cgroup 拥有的 cgroups 的数量...
};

如果用户想手动的把 Subsystems 挂在到其他 cgroup FS,也可以使用 mount 命令来进行挂载,如下命令所示:

$ mount -t cgroup -o memory memory /sys/fs/cgroup/memory1

另外,cgroup 中的 cgroup_subsys_state 字段用于关联到一个 Subsystems State(子系统资源统计结构体)列表,再关联到若干个具体的 Subsystems。

struct cgroup_subsys_state {struct cgroup *cgroup; // 指向 cgroup 对象atomic_t refcnt;       // 引用计数器unsigned long flags;   // 标志位
};struct mem_cgroup {// 资源统计对象通用部分struct cgroup_subsys_state css;// 资源统计对象私有部分struct res_counter res;  // 用于统计 tasks 的内存使用情况struct mem_cgroup_lru_info info;int prev_priority;struct mem_cgroup_stat stat;
};

在这里插入图片描述

可见 cgroup 和 Subsystems 是一对多的关系,如下图所示。
在这里插入图片描述

同时又由于一个 Task 可以关联到多个 cgroups 中,最终实现了 Tasks 和 Subsystems 之间的多对多关系。如下图所示:

  • ProcessA 属于 /sys/fs/cgroup/memory/cgrp1/cgrp3 和 /sys/fs/cgroup/cpu/cgrp2/cgrp3,所以 ProcessA 就关联了 mem_groupA 和 task_groupA 这两个 Cgroup Subsystems State。
  • ProcessB 属于 /sys/fs/cgroup/memory/cgrp1/cgrp4 和 /sys/fs/cgroup/cpu/cgrp2/cgrp3,所以 ProcessB 就关联了 mem_groupB 和 task_groupA 这两个 Cgroup Subsystems State。

在这里插入图片描述

在 Task 的 task_struct 中通过 css_set 字段来记录自己所关联的 Cgroup Subsystems State 列表,如下:

struct task_struct {...struct css_set *cgroups;...
};struct css_set {...// 用于收集不同 cgroup 的资源统计对象struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];
};

最终 User Process 和 Cgroups 之间就构成了一个 M×N Linkage 的映射关系结构。如下图所示。

在这里插入图片描述

Namespaces

Linux Namespaces(命名空间)是一种操作系统层级的资源视图隔离技术,能够将 Linux 的全局资源,划分为 Namespace 范围内可见的资源。

Namespaces 具有多种类型,基本上涵盖了构成一个操作系统所需要的基本元素:

  1. UTS namespace(系统主机名)
  2. Time namespace(系统时间)
  3. PID namespace(系统进程号)
  4. IPC namespace(系统进程间通信)
  5. Mount namespace(系统文件系统)
  6. Network namespace(系统网络)
  7. User namespace(系统用户权限)
  8. Cgroup namespace(系统 Cgroup)

User Process 是 Namespace 的主要服务对象,与之相关的 SCI 主要有 3 个:

  1. clone():创建一个 Process,同时设置 Namespace Instance 的类型参数。
  2. setns():把一个 Process 加入到指定的 Namespace Instance。
  3. unshare():把一个 Process 脱离指定的 Namespace Instance。

如下图所示,每种 Namespaces 都拥有各自的 clone 类型参数:
在这里插入图片描述

通过 /proc/{pid}/ns 文件可以查看指定 Process 运行在哪些 Namespaces Instance 中,并且每个 Namespace Instance 都具有一个唯一的标识。

$ ls -l --time-style='+' /proc/$$/ns
总用量 0
lrwxrwxrwx. 1 root root 0  ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 root root 0  mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0  net -> net:[4026531956]
lrwxrwxrwx. 1 root root 0  pid -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0  user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0  uts -> uts:[4026531838]

最终,用户可以通过创建多种不同类型的 Namespaces Instance 来提供的操作系统资源的隔离,再结合创建多种不同类型的 cgroups 来提供操作系统的资源配额,就构成了一个最基本的操作系统容器,即:Process Container。

UTS namespace

UTS namespace 为 Container 提供了 Hostname 和 Domain Name 的隔离。

Container 中的 Process 可以根据需要调用 sethostname 和 setdomainname 指令来进行配置,让每个 Container 都可以被视为网络中的一个独立的节点。

PID namespace

PID namespace 为 Container 提供了进程号的隔离。

每个 Containers 都拥有自己的进程环境,Container 的 init Process 都是 PID 1 号进程,它作为所有子进程的父进程。要想做到进程的隔离,首先需要创建出 PID 1 号进程,它具有以下特性:

  • 如果某个子进程脱离了父进程(父进程没有 wait 它),那么 init Process 就会负责回收资源并结束这个子进程。
  • 如果 init Process 被终止,那么 Kernel 就会调用 SIGKILL 终止此 PID namespace 中的所有进程。

IPC namespace

IPC namespace 为 Container 提供了 IPC(进程间)通信机制的隔离,包括信号量、消息队列、共享内存等机制。

每个 Containers 都拥有以下 /proc 文件接口:

  • /proc/sys/fs/mqueue:POSIX Message Queues 接口类型;
  • /proc/sys/kernel:System V IPC 接口类型;
  • /proc/sysvipc:System V IPC 接口类型。

Mount namespace

Mount namespace 为 Container 提供了 Filesystem 挂载点的隔离,继而实现了 VFS 的隔离。

每个 Containers 都拥有以下 /proc 文件接口,可以构成一个独立的 rootfs(Root 文件系统):

  • /proc/[pid]/mounts
  • /proc/[pid]/mountinfo
  • /proc/[pid]/mountstats

实际上,Mount namespace 是基于 Chroot 的不断改良而开发出来的。为 Container 创建的 rootfs 只是一个操作系统发行版所包含的文件、目录和配置,并不包括 Kernel 的文件。

Network namespace

Network namespace 为 Container 提供了网络资源的隔离,包括:

  • Network devices(网络设备)
  • IPv4 and IPv6 protocol stacks(IPv4、IPv6 的协议栈)
  • IP routing tables(IP 路由表)
  • Firewall rules(防火墙规则)
  • Sockets 套接字
  • /proc/[pid]/net
  • /sys/class/net
  • /proc/sys/net

需要注意的是,同一个 Network device 只能存在于一个 Namespace Instance 中,所以常常结合虚拟网络设备来使用。

在这里插入图片描述

User namespace

User namespace 为 Container 提供了用户权限和安全属性相关的隔离,包括:User ID、User Group ID、Root 目录以及特殊的权限。

每个 Containers 都拥有以下 /proc 文件接口:

  • /proc/[pid]/uid_map
  • /proc/[pid]/gid_map

Docker 对 Cgroups 和 Namespaces 的应用

当我们创建了一个 Docker Container 之后就可以查看这个 Container 所具有的 cgroups 和 namespaces 了。

  1. 查看 Container 的 ID(cfca1212d140)和 PID(2240)配置。
$ docker ps
CONTAINER ID   IMAGE                   COMMAND   CREATED         STATUS       PORTS     NAMES
cfca1212d140   centos:centos7.9.2009   "bash"    18 months ago   Up 2 hours             vim-ide$ docker inspect --format='{{.State.Pid}}' cfca1212d140
2240
  1. 查看 Container 的 cgroups 配置。
$ ll /sys/fs/cgroup/memory/docker/
总用量 0
drwxr-xr-x. 2 root root 0 62 03:40 cfca1212d1407a89632a439e974e246d1f6edd0bbef9079f06addf2613e1d46f$ cat /sys/fs/cgroup/memory/docker/cfca1212d1407a89632a439e974e246d1f6edd0bbef9079f06addf2613e1d46f/cgroup.procs 
2240$ cat /sys/fs/cgroup/memory/docker/cfca1212d1407a89632a439e974e246d1f6edd0bbef9079f06addf2613e1d46f/memory.limit_in_bytes
9223372036854771712
  1. 查看 Container 的 namespaces 配置。
$ ls -l --time-style='+' /proc/2240/ns
总用量 0
lrwxrwxrwx. 1 root root 0  ipc -> ipc:[4026532433]
lrwxrwxrwx. 1 root root 0  mnt -> mnt:[4026532431]
lrwxrwxrwx. 1 root root 0  net -> net:[4026531956]
lrwxrwxrwx. 1 root root 0  pid -> pid:[4026532434]
lrwxrwxrwx. 1 root root 0  user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0  uts -> uts:[4026532432]

参考文档

  • https://mp.weixin.qq.com/s/EdRVEJ0i5j9eHwd8QK-cDg
  • https://juejin.cn/post/6921299245685276686
  • https://zhuanlan.zhihu.com/p/388101355

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/353581.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

使用geoserver发布shp和tiff数据

一、安装并启动geoserver服务 1.1 下载geoserver 进入官网下载 由于geoserver是使用Java语言开发的&#xff0c;所以运行需要java的环境&#xff0c;不同geoserver的版本号对java的版本要求不同&#xff0c;所以选择版本时需注意对应java的版本要求&#xff0c;由于我本地安…

javascript回到顶部

如图&#xff1a; 数字从1到100&#xff0c;滚动后点击章鱼哥便可以回到顶部。 HTML内容不多只有一个a标签内容有一个图片和100个h1标签&#xff0c;但要看清楚是给它设置好了id的&#xff0c;当然不设置id也行但有时候对小白不太友好。 为了使点击图片更加贴合整个页面所以需要…

一段简单的网页返回顶部和返回底部代码(html+css+jquery)

最近用到了返回顶部和底部功能&#xff0c;找了找&#xff0c;查了查&#xff0c;改了改&#xff0c;最终实现效果大概这样子。 以下是相关代码&#xff1a; html部分&#xff1a; <div id"backtotop" class"backtotop"><div class"bt-box…

网页回到顶部的js代码实现

背景 还是给老板写他的个人主页&#xff0c;用到了一个小功能&#xff0c;随手记录下 代码 控制样式的 css 代码如下 /* 回到顶部实现代码的css */ .back-to-top {display: none;/* 默认是隐藏的&#xff0c;这样在第一屏才不显示 */position: fixed;/* 位置是固定的 */bott…

HTML5 回到顶部

图片&#xff1a; html <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>回到顶部</title><link rel"stylesheet" type"text/css" href"css/try.css" /><script src"j…

JS返回顶部代码

2019独角兽企业重金招聘Python工程师标准>>> <!-- 回到顶部 --> <div><span style"font-size:14px"><p id"back-to-top"><a href"#top"><span><img src"public/images/top.jpg" ti…

URL 地址栏能玩出什么新花样?这位歪果程序员小哥给你开开脑洞!

前言 在现在一些互联网营销号的传播素材中&#xff0c;程序员往往会被塑造成一个邋遢、木讷、秃头、低情商的形象&#xff0c;以借此博得普通群众的眼球&#xff0c;吸引他人注意力。 前阵子&#xff0c;暴走漫画发布的一个视频《创造1024》&#xff0c;里面便有多个情节涉及到…

用Python让蔡徐坤在我的命令行里打篮球!| 附完整代码

作者自称是一个经常逛 B 站的肥宅。最近B站上流行的视频素材除了“换脸”&#xff0c;其次就要属“蔡xx打球”视频了。有模仿的、对比的、手绘的... ...更过分的是&#xff0c;竟然有人在命令行输出了他的打球视频。不过&#xff0c;视频中的动画好像是用某个软件生成的 txt 文…

一行代码“黑”掉任意网站

文章目录 只需一行代码&#xff0c;轻轻一点就可以把任意网站变成暗黑模式。 首先我们先做一个实验&#xff0c;在任意网站中&#xff0c;打开浏览器开发者工具(F12)&#xff0c;在 C1onsole 控制台输入如下代码并回车&#xff1a; document.documentElement.style.filterinv…

用Python让蔡徐坤在我的命令行里打篮球!|附完整代码

点击上方↑↑↑蓝字关注我们~ 「2019 Python开发者日」全日程揭晓&#xff0c;请扫码咨询 ↑↑↑ 来源 | 01二进制&#xff08;ID:gh_d1999add1857&#xff09; 编辑 | Jane 【导语】作者自称是一个经常逛 B 站的肥宅。最近 B 站上流行的视频素材除了“换脸”&#xff0c;其次就…

自动化测试,B、C接口要将A接口返回的参数作为入参,有哪些方法?

在进行自动化测试时&#xff0c;如果需要将A接口返回的参数作为B、C接口的入参&#xff0c;可以通过以下几种方法实现&#xff1a; 如果你想学习自动化测试&#xff0c;我这边给你推荐一套视频&#xff0c;这个视频可以说是B站百万播放全网第一的自动化测试教程&#xff0c;同…

直播带货APP小程序系统开发功能有哪些?

直播带货APP小程序系统开发功能有哪些&#xff1f; 1、直播带货&#xff1a;主播一边带货一边直播间活跃气氛&#xff0c;直观地了解产品&#xff0c;下单的概率会更高。还集有观看、打赏、购物、分享于一体。 2、短视频带货&#xff1a;短视频种草&#xff0c;利…

KCNScrew for Mac(mac序列号工具)

KCNScrew for Mac是一款序列号工具&#xff0c;是一款十分简单好用的序列号查找工具。该软件的数据每个月都会更新&#xff0c;包含将近上千款软件的序列号提供给大家。如果你还在苦恼于软件因为没有序列号而不能正常使用到话&#xff0c;想要查看软件序列号的朋友欢迎&#xf…

CuteFTP,8uftp cuteftp

CuteFTP&#xff0c;FTP工具之一&#xff0c;其传输速度比较快&#xff0c;但有时对于一些教育FTP 站点却无法连接。我在进行ftp工具查找时&#xff0c;还发现了这么一款软件&#xff1a;IIS7服务器管理工具。 作为IIS7服务器管理工具&#xff0c;它可以对ftp站点进行批量管理…

Win10下SCP命令免密码上传、下载工程师服务器上的文件

【1】本地win10操作步骤 1&#xff09;在本地win10系统打开命令行工具&#xff08;winr&#xff09; 2&#xff09;执行命令&#xff1a;ssh-keygen -t rsa 3&#xff09;提示输入保存路径&#xff0c;直接回车&#xff0c;默认即可。 4&#xff09;提示输入密码&#xff0c;因…

CAN-TP帧类型(SF/FF/CF/FC)解析

简介 CAN-TP帧类型可分为 单帧和多帧。 单帧(SF) &#xff1a; Single Frame,数据长度小于等于7个Byte&#xff08;标准CAN是7Byte,CAN-Fd是63Byte&#xff09;时&#xff0c;使用单帧进行传输 多帧可分为 首帧&#xff08;FF&#xff09;/流控帧&#xff08;FC&#xff09;…

tcp 序列号

父 tcp,状态. from异常流程_个人渣记录仅为自己搜索用的博客-CSDN博客 转载请注明出处&#xff1a;6-TCP6-TCP 协议&#xff08;序号和确认号&#xff09;_tcp 最终确认序号_--Allen--的博客-CSDN博客6-TCP 接下来的内容是学习后续内容的基础&#xff0c;必须先讲清楚。为了…

手把手带你YOLOv5/v7 添加注意力机制,30多种模块分析①,SE模块,SK模块

目录 一、注意力机制介绍1、什么是注意力机制&#xff1f;2、注意力机制的分类3、注意力机制的核心 二、SE模块1、SE模块的原理2、代码实例3、实验结果4、应用示例&#xff08;1&#xff09;在 models/yolo.py 文件中定义 SEModule 类&#xff0c;用于实现SE模块。&#xff08;…

安装使用cuteFTP注意事项

花絮&#xff1a; 一直以来都使用红帽的共享文件来让windows和linux之间进行传输文件&#xff0c;今天头脑一发热&#xff0c;想使用windows下的cuteFTP软件来代替前面的方法。可谁想到&#xff0c;一是cuteFTP在网上根本找不到序列号&#xff0c;找了N久没找到&#xff0c;后…

CuteFTP安装

CuteFTP 9破解版&#xff0c;百度网盘链接&#xff1a;https://pan.baidu.com/s/16SDjxyQF2WtiPKpZHjcueQ 密码&#xff1a;xblr CuteFTP 9破解版是一款非常实用的商业FTP软件&#xff0c;也就是一个FTP客户端。可能很多人不知道FTP是什么&#xff0c;就是一个文件传输系统&…