0%

Veth

定义

Veth 是 Linux Kernel 中提供的一套虚拟网络设备接口,顾名思义为虚拟网络设备,通过在 Kennel 中注册虚拟网络设备并交付给其它应用使用,能够用来向其它服务(如容器)提供解耦式的网络代理功能。目前的 Docker 及 K8s 网络实现中均使用此方式来为容器提供网络服务。

使用方式

Veth 在申请时为成对出现,申请的应用程序会同时获得一对网络设备,拥有不同的设备名称,两个设备默认关联。可以向其中一个设备发送网络数据包,此时数据包会经过 Kernel 的转发从另一端的 Veth 网络设备中接收,反向通信同理。

进阶用法可以将 Veth 设备的一端绑定至物理网口或其它设备上进行使用。

评价

对于容器中采用此特性的实现,能够很好地为容器模拟真实主机的运行环境,内核能够通过标准网络通信接口为容器提供服务,而 runtime 可以接管容器的网络通信并且在其上做各种协议封装过滤等功能,效果很好。

此特性由内核提供,当内核本身成为瓶颈时这种通信方式则会遭遇瓶颈。由于内核转发需要在内核态与用户态间切换,存在一定的开销,需要进一步获得更高性能时可以考虑内核旁路模式执行,如 [[OVN]]、DPDK等实现。

参考资料

Obsidian插件使用记录

开端

之前配置Obsidian的时候搜罗了很多各种各样的插件,不得不说Obsidian的插件生态真的好到起飞,这里列出自己使用或遇到过比较有用的插件,一方面是做一个记录方面以后查找,另一方面也回顾一下自己都安了哪些乱七八糟的插件(笑)。

插件

下面每个插件的标题均为Obsidian市场中的插件名,可以直接通过名称搜索到对应的插件。

Admonition

富文本扩展插件,能够在markdown中嵌入块样式的附加文字,从而与正文分开,可以用于填写增加一些评论、旁注、警告或追加醒目信息等功能。

平时用得不是很多,因为开始要自己配置admonition的名字,比较不开箱即用,可能还要探索一阵。

Advanced Table

表格功能增强插件,支持快速调整表格格式样式居中居右等功能,插件启动后会在左侧按钮列表中增加一个入口,点击后会在右侧打开控制panal使用。

实际上现在用表格比较少,所以暂时没用上。

增强URL功能插件,在粘贴URL到markdown中的时候自动切换成URL语法并且访问原地址获取网页标题填写到语句中,很方便。

Calendar

日历插件,能够在右侧日历面板(不确定是不是插件提供的)上为每个日子创建一个markdown文件,支持从模板创建并且展示一些信息到日历上快速索引。

是一个比较核心的插件,编辑模板以后可以做很多事情,比如说当每天的事件总结和起始事件,这样每天都等于有一个工作总结或者开端,不过有点尴尬就是这两个作用混在一起的话只有一个模板文件会难受一些,可能起始页这件事应该拆分到其它地方去。还有点麻烦的事情就是这个模板也不是开箱即用的,所以模板上需要自己花费很多精力去慢慢调整,如果能直接毛些别人的模板就好了hhh。

Checklist

Todo功能的扩展插件,功能是在右侧面板中提供一个Todo List面板,把各个markdown文件中的Todo事项全部列在一起,顺便还是彩色展示。

之前完全没意识到安装了这个插件,效果上Todo展示的效果非常直观,可以直接完成和开始任务,不过不确定这个完成时间能不能触发其它插件的时间记录,可能要测试一番,如果可以的话联动效果就非常好了。

Cycle Through Panes

让你可以使用ctrl + tab 的方式在多个文档间切换,ctrl + shift + tab是反向切换。

Dataview

可以说是Obsidian的杀手级插件之一了,赋予了Obsidian数据检索能力,能够把Markdown文件当作数据库来进行检索,支持多种SQL语法,并且也支持使用Javascript语法来编写查询逻辑,拥有极强的可玩性。

DataView本身还会为每个Markdown文件追加诸多元数据和追踪信息用于检索,从而提供更多可操作信息。

配合模板可以做到快速扫描Vault展示各种各样的信息,非常好用,强力推荐。

Day Planer

扩展插件,可以将Todo按时间线形式列出,因此能够用来安排日常规划,拥有一个面板来展示信息。

Editor Syntax Highlight

给markdown中代码块提供语法高亮功能。

Find Unlinked Files

查找vault中没有link或被link的文档,方便生成知识网络管理,触发方式是在command中使用find unlinked file

暂时没有用过,可以用来偶尔查漏补缺一下以前拉下的笔记。

Incremental Writing

笔记管理插件,可以配置把笔记分成多个工作队列,从而完成多层流的笔记处理。

常见的可以把笔记放在未完成队列中这样每次可以补全笔记,更高深的用法可以是把笔记分成初次记录,后期文档化和标签化等多个队列,多个工作流一步步吸收归纳知识。

高级用法暂时没有使用过,目前暂时还是当作未完成队列在使用,因为这个模式是开箱即用的。

Kanban

将Markdown文件转化为看板,可以设置不同的工作条,但是具体的展示效果暂时没有测试过。

拿类似的独立产品Trello来说,Trello的进度条功能和拖动是非常好用的功能,如果没有这两个功能的话它对我的吸引力就没有那么大了,虽然做到了多合一但是牺牲了直观性。

Obsidian Linter

Markdown格式化工具,默认配置和Vscode类似都是在保存时自动进行格式化,有很多具体格式细节能够调整,并且支持自动插入一些YAML Tag,配合文档索引等功能非常方便。

Media Extended

让Markdown支持播放音频及视频,并且支持定位到指定的时间轴位置。

具体效果因为未使用过暂时未知,目前预想到的场景为看网课视频时做笔记能够快速索引到原文,或是在看视频作评论时能够使用。

扩展插件:Media Extended for Bilibili,大概是这样的名字,能够在插件市场中搜索到,顾名思义是为哔哩哔哩视频提供了引用支持。

Mind Map

将Markdown文档按标题顺序生成对应的脑图,某种程度上能够成为脑图绘制工具,或是在编写一份笔记时用来纵览思路,使用场景很多。

使用时通过command调出对应panel即可,可以放置在一旁作为速览。

使用上插件有时候会因为部分皮肤和主题之类的原因无法显示,兼容性相对比较差,使用时可能要和主题作一番取舍。

Natural Language Dates

通过@符号加today、tomorrow等关键词快速插入当前日期,属于快捷语法一类的插件,但是某些高级功能插件需要依赖这个插件功能运作,因此也属于必备插件之一。

Obsidian-Charts

在Markdown中插入好看的图表,使用方式可以通过Chart代码块创建或command呼叫图形化图表生成。

这个插件暂时没有使用过,不知道能不能配合dataview的数据检索进行操作,如果可以的话也将是一个非常好用的功能插件。

Obsidian Outliner

为markdown的list提供额外的快捷键控制,类似其它的markdown软件中快捷键,便于list的操控。

Pandoc Plugn

为Obsidian提供Pandoc支持,本身不带Pandoc组件需要自行安装,主要功能是提供word、html、latex、markdown等格式的导出,PDF导出功能已经由Obsidian官方支持并且兼容的功能更多,Pandoc主要是为其它场景使用。

安装后在command中调用即可。

Reminder

为markdown的Todo提供时间提醒功能,在Todo上标记时间即可触发,有一个独立面板按时间展示Todo,同时兼容Kanban插件和其它Task插件的语法,组合以后Obsidian在Todo追踪这一块功能就非常完整了。

目前暂时没有在使用,Todo功能虽然很全但是跨平台方面我还没有配置好,所以没有迁移过来,并且快速添加Todo会相对麻烦一些。

Self-hosted LiveSync

为Obsidian开启第三方兼容的实时同步功能,让Obsidian能够完全本地化运行,目前官方表示插件已经接近稳定但是可能还有数据损坏可能,此外它仅能用于同步而不是一个备份方案。

这个插件是刚发现不久的,并没有实际使用过,从功能目的上来说非常未来可期。

Sliding Panes

将Obsidian在多个文档间跳转的行为变成叠加多个panel,方便阅读时在多个文档间快速切换,非常方便。

Tasks

Obsidian的Todo管理增强插件,为Todo增加了日期时间、循环定时、完成时间记录等功能,让Obsidian可以作为一个比较完整的Tasks管理软件运作。

目前使用上还好,编辑时要配置Todo需要用command进行控制稍微有点麻烦,可能这里还有其它优化方式我没有发现,另外它对完成时间的标记能够被DataView识别,从而做到联动效果,这点非常方便,但是似乎DataView自身也能够对Todo的数据进行追踪,这部分可能需要再作尝试。

Templater

Obsidian的模板增强插件,支持众多插件语法和快速创建命令,支持按目录匹配模板等,此处功能介绍可以参考其官方的介绍,是Obsidian的核心扩展插件之一,很多管理技巧都可以通过它的模板功能实现。

Text Expand

Markdown语法扩展插件,可以在Markdown中对Vault内数据进行搜索并展示,只要预先设定搜索条件和规则即可。

这个插件之前安装后没有意识到它的存在,没有使用过,但是这种动态能力应该具有非常高的可玩性。

Vault Statistics

为Obsidian增加词数统计等功能,增强统计效果,算是基础插件之一,统计信息也是文档类编辑工具的基础能力了。

Workbench

这个插件在安装后也没有使用过,其功能描述为能够将当前UI中显示的信息(Todos、Block、段落等)生成链接插入到另一个已打开的Markdown文档之中,生成链接后在该文档里会直接显示成正文并且能够编辑和操作。

这个的作用似乎局限于已经打开的文档中?引用功能的话类似的插件还挺多的,不知道它的作用如何。

Youglsih Plugin

一个词典插件,能够快速给出某个词的定义和用例,甚至能够定位到相关网络视频(主要是油管)的发音用例,值得一提的是这个插件并不局限于英语。

词典功能很完善,有点奇怪的是我觉得Obsidian这个编辑器的功能有点差异,作为一个输入软件能够被查询的词可能用户不太能够直接输入到文档中,而能够输入到文档中的词通常也不需要进行查询,除非用户主动把未知的词汇粘贴进Obsidian来使用这个功能。

更理想的场景应该是放置到浏览器之类的输出软件因为那里用户能够接触到不了解的词汇从而进行词典查询,要在Obsidian里进行未知词汇查询就比较奇怪。这个插件也没有使用过。

Mellanox ASAP2

定义

Accelerated Switching and Packet Processing

加速交换与数据报处理技术,原由Mellanox提出但在Nvidia收购后更名为 Nvidia ASAP2

应对场景

主要针对高速网络下的加速网络数据包场景,传统网络数据包处理需要将网络通信数据包全部提交至CPU进行处理,这些处理过程占用了CPU资源并且开销日益增大

应对思路

让网卡接管网络IO的处理,并且在硬件上完成原本CPU需要进行的网络数据解析任务,并且携带网络数据分流的特性,支持Single Root Virtualization等方式模拟出多个网络设备,将不同的网络流量分别呈递,能够接入[[OpenVirtualSwitch]] 虚拟交换或其它虚拟网络,使得这一过程对CPU的开销降低到非常低的程度,从而达到硬件加速的效果

性能

1
Benchmarks show that on a server with a 25G interface, OVS accelerated by ASAP2 Direct achieves 33 million packets per second (mpps) for a single flow with near-zero CPU cores consumed by the OVS data plane, and about 18 mpps with 60,000 flows performing VXLAN encap/decap. These performance results are three to ten times higher than DPDK-accelerated OVS. ASAP2 offers the best of both worlds, software-defined flexible network programmability, and high network I/O performance for the state-of-art speeds from 10G to 25/40/50/100/200G.

25G 端口下,通过ASAP2 Direct 技术能够处理单条流33mpps数据包,且无CPU占用,在VXLAN解包生成包操作下能够达到最多6万条流一共18mpps的性能

目前此技术可应用在25/40/50/100/200G网络通信模式中

参考资料

WSL2占用53端口的解决方案

定义

[[WSL]]2在运行时由于为虚拟机架构,因此与宿主机的网络通信实际上为两块虚拟网卡之间相互通信,为了让WSL2子系统能够正常访问网络,其在初始化时会在宿主机53端口上开启一个DNS解析服务,并且由于虚拟网卡的IP端不固定,会随着每次系统的重启而变化,因此该解析服务监听了0.0.0.0:53,由此导致了在WSL2运行时,用户无法自行运行53端口的DNS解析服务。

解决方案

实际上该DNS解析服务范围为0.0.0.0:53,即IPv4空间,而IPv6空间下53端口为空闲状态,因此用户自行启用的53服务设定去监听IPv6即可,并且Windows主机支持只设定ipv6的DNS,且v6下的DNS也可以解析v4的IP

参考资料

DPDK环境搭建记录

定义

由Intel发起的开源网络数据处理框架及相关生态环境,本身面向更通用化的数据平面编程,并且在和Intel硬件的结合后能够有更好的运行性能(如DPDK在Intel CPU及Intel NIC环境中,数据包在到达NIC后能够直接上传至CPU的三级缓存中,相比其他PF_RING等框架为NIC通过DMA上传至系统内存,这种优化能够减少一次CPU读取内存的操作)

1
对大多数平台,使用基本DPDK功能无需对BIOS进行特殊设置。然而,对于HPET定时器和电源管理功能,以及为了获得40G网卡上小包处理的高性能,则可能需要更改BIOS设置。可以参阅章节  [Enabling Additional Functionality](https://doc.dpdk.org/guides/linux_gsg/enable_func.html#enabling-additional-functionality)以获取更为详细的信息。

环境配置

配置依赖

  • General development tools including a supported C compiler such as gcc (version 4.9+) or clang (version 3.4+), and pkg-config or pkgconf to be used when building end-user binaries against DPDK.

    • For RHEL/Fedora systems these can be installed using

      1
      dnf groupinstall "Development  Tools"
    • For Ubuntu/Debian systems these can be installed using

      1
      apt install build-essential
    • For Alpine Linux

      1
      apk add alpine-sdk bsd-compat-headers libexecinfo-dev
  • Python 3.5 or later.

  • Meson (version 0.49.2+) and ninja

    • meson & ninja-build packages in most Linux distributions
    • If the packaged version is below the minimum version, the latest versions can be installed from Python’s “pip” repository: pip3 install meson ninja
  • pyelftools (version 0.22+)

    • For Fedora systems it can be installed using dnf install python-pyelftools
    • For RHEL/CentOS systems it can be installed using pip3 install pyelftools
    • For Ubuntu/Debian it can be installed using apt install python3-pyelftools
    • For Alpine Linux, apk add py3-elftools
  • Library for handling NUMA (Non Uniform Memory Access).

    • numactl-devel in RHEL/Fedora;
    • libnuma-dev in Debian/Ubuntu;
    • numactl-dev in Alpine Linux

Optional Tools

  • Intel® C++ Compiler (icc). For installation, additional libraries may be required. See the icc Installation Guide found in the Documentation directory under the compiler installation.
  • IBM® Advance ToolChain for Powerlinux. This is a set of open source development tools and runtime libraries which allows users to take leading edge advantage of IBM’s latest POWER hardware features on Linux. To install it, see the IBM official installation document.

Additional Libraries

A number of DPDK components, such as libraries and poll-mode drivers (PMDs) have additional dependencies. For DPDK builds, the presence or absence of these dependencies will be automatically detected enabling or disabling the relevant components appropriately.

In each case, the relevant library development package ( -devel or -dev ) is needed to build the DPDK components.

For libraries the additional dependencies include:

  • libarchive: for some unit tests using tar to get their resources.
  • libelf: to compile and use the bpf library.

For poll-mode drivers, the additional dependencies for each driver can be found in that driver’s documentation in the relevant DPDK guide document, e.g. Network Interface Controller Drivers — Data Plane Development Kit 22.03.0 documentation

System Software

Required:

  • Kernel version >= 4.4

    The kernel version required is based on the oldest long term stable kernel available at kernel.org when the DPDK version is in development. Compatibility for recent distribution kernels will be kept, notably RHEL/CentOS 7.

    The kernel version in use can be checked using the command:

    uname -r

  • glibc >= 2.7 (for features related to cpuset)

    The version can be checked using the ldd --version command.

  • Kernel configuration

    In the Fedora OS and other common distributions, such as Ubuntu, or Red Hat Enterprise Linux, the vendor supplied kernel configurations can be used to run most DPDK applications.

    For other kernel builds, options which should be enabled for DPDK include:

编译

下载源代码并解压

1
2
tar xJf dpdk-<version>.tar.xz
cd dpdk-<version>

The DPDK is composed of several directories, including:

  • doc: DPDK Documentation
  • license: DPDK license information
  • lib: Source code of DPDK libraries
  • drivers: Source code of DPDK poll-mode drivers
  • app: Source code of DPDK applications (automatic tests)
  • examples: Source code of DPDK application examples
  • config, buildtools: Framework-related scripts and configuration
  • usertools: Utility scripts for end-users of DPDK applications
  • devtools: Scripts for use by DPDK developers
  • kernel: Kernel modules needed for some operating systems

DPDK Configuration

To configure a DPDK build use:

1
meson <options> build

where “build” is the desired output build directory, and <options> can be empty or one of a number of meson or DPDK-specific build options, described later in this section. The configuration process will finish with a summary of what DPDK libraries and drivers are to be built and installed, and for each item disabled, a reason why that is the case. This information can be used, for example, to identify any missing required packages for a driver.

Once configured, to build and then install DPDK system-wide use:

1
2
3
4
cd build
ninja
ninja install
ldconfig

The last two commands above generally need to be run as root, with the ninja install step copying the built objects to their final system-wide locations, and the last step causing the dynamic loader ld.so to update its cache to take account of the new objects.

配置网卡驱动

DPDK使用PMD驱动操作网卡,并且有多个可选项,默认使用 UIO or VFIO驱动,但是也能够基于Bifurcated Driver 运行

1
It is recommended that `vfio-pci` be used as the kernel module for DPDK-bound ports in all cases. If an IOMMU is unavailable, the `vfio-pci` can be used in [no-iommu](https://doc.dpdk.org/guides/linux_gsg/linux_drivers.html#vfio-noiommu) mode. If, for some reason, vfio is unavailable, then UIO-based modules, `igb_uio` and `uio_pci_generic` may be used. See section [UIO](https://doc.dpdk.org/guides/linux_gsg/linux_drivers.html#uio) for details.

大部分设备被DPDK使用时,需要将其脱离Linux Kernel的控制,这意味着Linux Kernel将无法看到该设备并管理,其它非DPDK应用程序在Kernel重新获得控制权之前也无法使用该设备。对于网络设备可以基于端口进行分割绑定

操作设备绑定与解绑,DPDK提供一个实用脚本 dpdk-devbind.py 可使用

  • 查看系统中网络端口状态

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    ./usertools/dpdk-devbind.py --status

    Network devices using DPDK-compatible driver
    ============================================
    0000:82:00.0 '82599EB 10-GbE NIC' drv=vfio-pci unused=ixgbe
    0000:82:00.1 '82599EB 10-GbE NIC' drv=vfio-pci unused=ixgbe

    Network devices using kernel driver
    ===================================
    0000:04:00.0 'I350 1-GbE NIC' if=em0 drv=igb unused=vfio-pci *Active*
    0000:04:00.1 'I350 1-GbE NIC' if=eth1 drv=igb unused=vfio-pci
    0000:04:00.2 'I350 1-GbE NIC' if=eth2 drv=igb unused=vfio-pci
    0000:04:00.3 'I350 1-GbE NIC' if=eth3 drv=igb unused=vfio-pci

    Other network devices
    =====================
    <none>
  • 绑定设备“04:00.1”到驱动vfio-pci

    1
    2
    3
    ./usertools/dpdk-devbind.py --bind=vfio-pci 04:00.1
    # or
    ./usertools/dpdk-devbind.py --bind=vfio-pci eth1
  • 释放设备到系统,即切换其驱动到标准驱动,支持通配符

    1
    ./usertools/dpdk-devbind.py --bind=ixgbe 82:00.*

VFIO 驱动相关

默认情况下,VFIO 驱动 通常已集成在系统中,能够直接启用运行

通过前述提到的 dpdk-devbind.py 能够将设备切换到 VFIO驱动

部分情况下使用脚本将设备绑定到 VFIO驱动

开发DPDK应用

编译环境配置

When installed system-wide, DPDK provides a pkg-config file libdpdk.pc for applications to query as part of their build. It’s recommended that the pkg-config file be used, rather than hard-coding the parameters (cflags/ldflags) for DPDK into the application build process.

An example of how to query and use the pkg-config file can be found in the Makefile of each of the example applications included with DPDK. A simplified example snippet is shown below, where the target binary name has been stored in the variable $(APP) and the sources for that build are stored in $(SRCS-y).

PKGCONF = pkg-config

CFLAGS += -O3 $(shell $(PKGCONF) –cflags libdpdk)

LDFLAGS += $(shell $(PKGCONF) –libs libdpdk)

$(APP): $(SRCS-y) Makefile
$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS)

1
Unlike with the make build system present in older DPDK releases, the meson system is not designed to be used directly from a build directory. Instead it is recommended that it be installed either system-wide or to a known location in the user’s home directory. The install location can be set using the –prefix meson option (default: /usr/local).

an equivalent build recipe for a simple DPDK application using meson as a build system is shown below:

project(‘dpdk-app’, ‘c’)

dpdk = dependency(‘libdpdk’)

sources = files(‘main.c’)

executable(‘dpdk-app’, sources, dependencies: dpdk)

参考资料

DataView 插件介绍

定义

[[Obsidian]]插件之一,能够将Obsidian内的文档视为数据表进行检索和数据处理。

数据可识别格式

YAML格式

在Markdown文件中嵌入YAML格式的字段,能够被识别为数据

1
2
3
4
5
6
7
#markdown content

---
#yaml content
name1: dataContent1
name2: dataContent2

Markdown格式

在Markdown文件中使用Name::Value的方式标记数据

1
2
3
4
5
#markdown content

name1::dataContent1
name2::dataContent2

插件追加信息

dataview插件本身也会为Markdown文件加入诸多标记属性,这里使用一个打印命令将本文件的属性全部输出

1
2
dv.paragraph(dv.current())

检索语法

块检索语法

使用语法标记为dataview的代码块进行检索,在其中输入检索语句进行查询

检索语法可参考SQL语法

1
2
3
4
5
SELECT <columns>
FROM <source>
WHERE <condition>
SORT <column> [desc|asc]
GROUP BY <column>

SELECT 对应的关键词

  • LIST 以列表的形式展示数据
1
2
LIST
FROM <source>
  • TABLE 以表格的形式展示数据,语法后接字段,表示定义
1
2
TABLE column1, column2, column3
FROM <source>
  • TASK 以任务形式展示数据,与LIST类似但是能够将抽出的数据作为任务生成

FROM 语法

FROM 可以从 #tag 标签获取、 从 folder 文件夹获取、从 [[link]] 链接获取,或者从链接了 link 的文件获取 outgoing([[link]])

行内代码语法

Dataview 目前支持的行内代码块主要是对于日期以及本页信息的显示,例如:

显示时间差

你可以用行内代码块计算出任意的时间差,如下:

1
`=(date(2021-12-31)-date(today))`

你就可以获得相关的时间差值,如下:

查看当前文件的信息

例如标签:

1
`= this.file.tags`

进入渲染模式后就会自动在对应的位置上显示你的当前文件的所有标签了。

DataviewJS 代码块

正如它后边附带的 JS 所言,DataviewJS 在扩展了本身的函数能力的基础上,获得了所有 API 相关的操作权限以及几乎完全的 Javascript 能力。接下来先介绍 DataviewJS 已经封装好的几个主要显示函数(目前仅能通过这些函数来显示对应的参数)。

注意,DataviewJS 代码块用 dataviewjs 而不是 dataview

DataviewJS语法也可在inline中使用,使用prefix $=即可触发,缺陷为目前无法在live preview模式下触发,需要切换到reading模式

检索

  • dv.pages(source)

根据标签或者文件夹返回页面参数,代码参考如下:

1
2
3
4
5
6
​```dataviewjs
dv.pages("#books") //返回所有带有标签 books 的页面
dv.pages('"folder"') //返回所有在 folder 文件夹的页面
dv.pages("#yes or -#no") //返回所有带有标签 yes 或者没有标签 no 的页面
dv.pages("") //返回所有页面
​```
  • dv.pagePaths(source)

和上边类似,但是这个会返回文件路径作为参数

1
2
3
4
5
​```dataviewjs
dv.pagePaths("#books") //返回所有带有标签 books 的页面路径
dv.pagePaths('"folder"') //返回所有在 folder 文件夹的页面路径
dv.pagePaths("#yes or -#no") //返回所有带有标签 yes 或者没有标签 no 的页面路径
​```
  • dv.page(path)

用于返回单个页面作为参数

1
2
3
4
​```dataviewjs
dv.page("Index") //返回名称为 Index 的页面
dv.page("books/The Raisin.md") //返回所有在 books 文件夹下的 The Raisin 文件的页面
​```

渲染

  • dv.header(level, text)

用于渲染特定的文本为标题,其中 level 为层级,text 为文本。

  • dv.paragraph(text)

用于渲染为段落,你可以理解成普通文本。

Dataview 封装函数

列表

1
2
3
4
5
6
7
​```dataviewjs
dv.list([1, 2, 3]) //生成一个1,2,3的列表
dv.list(dv.pages().file.name) //生成所有文件的文件名列表
dv.list(dv.pages().file.link) //生成所有文件的文件链接列表,即双链
dv.list(dv.pages("").file.tags.distinct()) //生成所有标签的列表
dv.list(dv.pages("#book").where(p => p.rating > 7)) //生成在标签 book 内所有评分大于 7 的书本列表
​```

任务列表

默认为 dv.taskList(tasks, groupByFile) 其中任务需要用上文获取 pages 后,再用 pages.file.tasks 来获取,例如 dv.pages("#project").file.tasks。 而当 groupByFile 为 True 或者 1 的时候,会按照文件名分组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
​```dataviewjs
// 从所有带有标签 project 的页面中获取所有的任务生成列表
dv.taskList(dv.pages("#project").file.tasks)

// 从所有带有标签 project 的页面中获取所有的未完成任务生成列表
dv.taskList(dv.pages("#project").file.tasks
.where(t => !t.completed))

// 从所有带有标签 project 的页面中获取所有的带有特定字符的任务生成列表
// 可以设置为特定日期
dv.taskList(dv.pages("#project").file.tasks
.where(t => t.text.includes("#tag")))

// 将所有未完成且带有字符串的任务列出
dv.taskList(
dv.pages("").file.tasks
.where(t => t.text.includes("#todo") && !t.completed),1)
​```

表格

默认为 dv.table(headers, elements) ,提供表头和元素内容即可生成对应的表格。

例如:

1
2
3
4
5
6
7
​```dataviewjs
// 根据标签 book 对应的页面的 YAML 生成一个简单的表格,其中 map 为对应的内容所对应的表头,按顺序填入即可。
// b 可以是任意值,只是代表当前传入的文件为 b
dv.table(["File", "Genre", "Time Read", "Rating"], dv.pages("#book")
.sort(b => b.rating)
.map(b => [b.file.link, b.genre, b["time-read"], b.rating]))
​```

看完以上的内容以后,如果你对表格或者获取的数据操作感兴趣的话,那你应该去查看官方提供的数据操作 API ,请查阅:Data Arrays | Dataview


DataviewJS 示例

以下的方案目前已经经过一部分人的使用,大呼已经满足,而如果你还有其它的需求的话,可以去请教对 Javascript 熟悉的人,或者去官方论坛请教。此处感谢提供脚本的 @tzhou 以及在官方社区的 @shabegom

标签聚合

简单版

1
2
3
4
​```dataviewjs
// 生成所有的标签且形成列表
dv.list(dv.pages("").file.tags.distinct())
​```

改进版

1
2
3
4
5
6
​```dataviewjs
// 生成所有的标签且以 | 分割,修改时只需要修改 join(" | ") 里面的内容。
dv.paragraph(
dv.pages("").file.tags.distinct().map(t => {return `[${t}](${t})`}).array().join(" | ")
)
​```

高级版

1
2
3
4
5
6
7
8
​```dataviewjs
// 基于文件夹聚类所有的标签。
for (let group of dv.pages("").filter(p => p.file.folder != "").groupBy(p => p.file.folder.split("/")[0])) {
dv.paragraph(`## ${group.key}`);
dv.paragraph(
dv.pages(`"${group.key}"`).file.tags.distinct().map(t => {return `[${t}](${t})`}).array().sort().join(" | "));
}
​```

效果如下:

输出内容

输出所有带有关键词的行

1
2
3
4
5
6
7
8
9
10
11
12
​```dataviewjs
//使用时修改关键词即可
const term = "关键词"
const files = app.vault.getMarkdownFiles()
const arr = files.map(async ( file) => {
const content = await app.vault.cachedRead(file)
const lines = content.split("\n").filter(line => line.contains(term))
return lines
})
Promise.all(arr).then(values =>
dv.list(values.flat()))
​```

如下:

输出所有带有标签的文件名以及对应行且形成表格

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
​```dataviewjs
// 修改标签
const tag = "#active"
// 获取 Obsidian 中的所有 Markdown 文件
const files = app.vault.getMarkdownFiles()
// 将带有标签的文件筛选出来
const taggedFiles = new Set(files.reduce((acc, file) => {
const tags = app.metadataCache.getFileCache(file).tags
if (tags) {
let filtered = tags.filter(t => t.tag === tag)
if (filtered) {
return [...acc, file]
}
}
return acc
}, []))

// 创建带有标签的行数组
const arr = Array.from(taggedFiles).map(async(file) => {
const content = await app.vault.cachedRead(file)
const lines = await content.split("\n").filter(line => line.includes(tag))
return [file.name, lines]
})

// 生成表格
Promise.all(arr).then(values => {
dv.table(["file", "lines"], values)
})
​```

输出所有带有标签的文件链接以及对应行且形成表格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
​```dataviewjs
// 获取 Obsidian 中的所有 Markdown 文件
const files = app.vault.getMarkdownFiles()

// 将带有标签的文件以及行筛选出来
let arr = files.map(async(file) => {
const content = await app.vault.cachedRead(file)
//turn all the content into an array
let lines = await content.split("\n").filter(line => line.includes("#tag"))
return ["[["+file.name.split(".")[0]+"]]", lines]
})

// 生成表格,如果要将当前的文件排除的话,请修改其中的排除文件
Promise.all(arr).then(values => {
console.log(values)
//filter out files without "Happy" and the note with the dataview script
const exists = values.filter(value => value[1][0] && value[0] != "[[排除文件]]")
dv.table(["file", "lines"], exists)
})
​```

如下:

输出任务

简单版

输出所有带有标签 todo 以及未完成的任务

1
2
3
4
5
6
​```dataviewjs
// 修改其中的标签 todo
dv.taskList(
dv.pages("").file.tasks
.where(t => t.text.includes("#todo") && !t.completed),1)
​```

改进版

输出所有带有标签 todo 以及未完成的任务,且不包含当前文件

1
2
3
4
5
6
​```dataviewjs
// 修改其中的标签 todo 以及修改 LOLOLO
dv.taskList(
dv.pages("").where(t => { return t.file.name != "LOLOLO" }).file.tasks
.where(t => t.text.includes("#todo") && !t.completed),1)
​```

倒计时

简单版

1
2
3
4
5
6
7
8
9
10
11
​```dataviewjs
// 修改其中的时间,可以输出当前离倒计时的时间差。
const setTime = new Date("2021/8/15 08:00:00");
const nowTime = new Date();
const restSec = setTime.getTime() - nowTime.getTime();
const day = parseInt(restSec / (60*60*24*1000));

const str = day + "天"

dv.paragraph(str);
​```

如下:

复杂版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
​```dataviewjs
// 只要在任务后边加上时间(格式为 2020-01-01 ,就会在显示所有的任务的同时对应生成对应的倒计时之差
dv.paragraph(
dv.pages("").file.tasks.array().map(t => {
const reg = /\d{4}\-\d{2}\-\d{2}/;
var d = t.text.match(reg);
if (d != null) {
var date = Date.parse(d);
return `- ${t.text} -- ${Math.round((date - Date.now()) / 86400000)}天`
};
return `- ${t.text}`;
}).join("\n")
)
​```

参考资料

Zinit 介绍

定义

一个为ZSH设计的插件管理框架,主要特性是轻量,支持延迟加载插件,避免ZSH启动时响应缓慢

安装

手动安装

  • 下载源代码仓库
1
2
3
ZINIT_HOME="${XDG_DATA_HOME:-${HOME}/.local/share}/zinit/zinit.git"
mkdir -p "$(dirname $ZINIT_HOME)"
git clone https://github.com/zdharma-continuum/zinit.git "$ZINIT_HOME"
  • 注册Zinit到.zshrc,添加这两行,完成基础配置
1
2
ZINIT_HOME="${XDG_DATA_HOME:-${HOME}/.local/share}/zinit/zinit.git"
source "${ZINIT_HOME}/zinit.zsh"
  • (可选)(执行 .zshrc,启动Zinit
1
source ~/.zshrc
  • (可选)(运行Zinit自动更新,让Zinit自行完成剩余配置
1
zinit self-update

添加插件

插件安装来源为github任意仓库,指定用户名/仓库名即可,将命令放入zshrc即可配置

1
2
3
zinit load  <repo/plugin> # Load with reporting/investigating.
zinit light <repo/plugin> # Load without reporting/investigating.
zinit snippet <URL> # Install direct script file, support local file

OhMyZSH插件

对于 [[OhMyZSH]] 和 Prezto,还可以使用缩写 OMZ::PZT::

1
2
zinit snippet OMZ::plugins/git/git.plugin.zsh
zinit snippet PZT::modules/helper/init.zsh

常用配置插件

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
# Theme
zinit ice depth"1" # git clone depth
zinit light romkatv/powerlevel10k

# 快速目录跳转
zinit ice lucid wait='1'
zinit light skywind3000/z.lua

# 语法高亮
zinit ice lucid wait='0' atinit='zpcompinit'
zinit light zdharma/fast-syntax-highlighting
# 自动建议
zinit ice lucid wait="0" atload='_zsh_autosuggest_start'
zinit light zsh-users/zsh-autosuggestions

zinit load zdharma-continuum/history-search-multi-word

# 加载 OMZ 框架及部分插件
zinit snippet OMZ::lib/completion.zsh
zinit snippet OMZ::lib/history.zsh
zinit snippet OMZ::lib/key-bindings.zsh
zinit snippet OMZ::lib/theme-and-appearance.zsh
zinit snippet OMZ::plugins/colored-man-pages/colored-man-pages.plugin.zsh
zinit snippet OMZ::plugins/sudo/sudo.plugin.zsh

zinit ice lucid wait='1'
zinit snippet OMZ::plugins/git/git.plugin.zsh


参考资料

C++ 使用带自定义比较函数的优先队列

优先队列 priority_queue

介绍

C++中自动保持有序的队列,位于 queue 头文件中,能够以 O(logn) 的复杂度取得 n 个元素中的最小元素,不需要自行实现堆排序。此队列默认为大顶堆,可以通过自定义来实现小顶堆。

基本用法

定义

1
2
3
4
5
queue<int> data; // 储存 int 型的从大到小优先队列

queue<int, vector<int>, less<int>> data; // 储存 int 型的从大到小优先队列的等价写法

queue<int, vector<int>, greater<int>> data; // 储存 int 型的从小到大优先队列

成员函数

参考 https://www.cplusplus.com/reference/queue/priority_queue/

自定义类型时的比较设定

由于 less<> 与 greater<> 仅支持系统自带的类型,这里有两种方法可以支持自定义类型的比较

  • 重载 struct 的 operator <(即重载小于号实现比较,因为内部的排序仅使用小于号比较),来为 less<> 与 greater<> 扩展比较能力
  • 传入自定义的 cmp 函数(更准确的说是重载了()操作符的数据结构,因为此处是使用泛型传入的),来完全自定义比较过程

struct 重载运算符

重载语法

重载小于号例子:

1
2
3
4
5
6
7
8
9
struct node{
int data;
int test;
bool operator < (const node &b) const {
return data < b.data;
}
};

priority_queue<node, vector<node>, greater<node>>

自定义 cmp 函数

当涉及到指针类型时只能通过这种方法

语法

1
2
3
4
5
6
7
struct cmp{
bool operator() (node* a, node* b){
return a->data < b->data;
}
};

priority_queue<node*, vector<node*>, cmp> 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <queue>
#include <vector>

using namespace std;
struct node{
int data;
int test;
bool operator < (const node &b) const {
return data < b.data;
}

node(int d){data = d;}
};

struct cmp{
bool operator() (node* a, node* b){
return *a < *b;
}
};

int main(){
priority_queue<node*, vector<node*>, cmp> data;
node* new_node;

int input, count;
cin >> count;
for(int i = 0; i < count; i++){
cin >> input;
new_node = new node(input);
data.push(new_node);
}

node* input1;
node* input2;
while(data.size() > 1){
input1 = data.top();
data.pop();
input2 = data.top();
data.pop();
input1->data = input1->data + input2->data;
delete input2;
data.push(input1);
}
cout << data.top()->data << endl;
new_node = data.top();
data.pop();
delete new_node;
return 0;
}

socks 协议是支持进行 DNS 解析的,但是很多使用该协议的程序并没有实现从 socks 协议来解析域名,仅仅是将流量转发到了 socks 端口上。

今天在使用 python 的 urllib 的时候意外地发现 urllib 是支持设定从 socks 协议进行解析的,具体的 PR 在 https://github.com/urllib3/urllib3/pull/1036

在设定 proxy 时将协议头设置为 socks5h 即可

WSL 是 Windows 下的 Linux 子系统,通过兼容层实现在 Windows 平台上模拟 Linux 环境,不像 WSL2 拥有完整的 Linux 内核。这使得很多依赖内核机制的应用都无法使用,比如说 system-init 和 docker。没有 system-init 支持之后常用的服务管理工具 systemctl 也就失效了,虽然可以通过 service 命令来操作,但是遇到需要自定义服务的时候,service 就显得有些简陋了:需要手写较长的脚本配置外加不支持自动重启等。此时就需要 supervisor 了,基于子进程的服务管理方式不需要内核支持,能够在 WSL 环境很好的工作

相关细节

supervisor 可以通过系统自带包管理器安装,又因为是 python 编写,支持通过 pip 安装。但是两者安装的版本上有一些区别。

  • 通过包管理器安装的 supervisor 会向 /etc/init.d 目录释放用于 service 命令管理的配置文件(其实就是个脚本文件,所以说有点麻烦写),但是它安装的 supervisor 所依赖的 python 版本会根据当前系统的初始版本变化。比如我这里使用的是 Ubuntu 系统,系统默认为 python2 ,虽然我已经手动切换到了 python 3,但是所安装的 supervisor 依然是基于 python2 版本(这个依赖应该是由 Ubuntu 的包所决定的,大概制作包的时候就是以 python2 为基准制作的吧,并没有根据环境自动选择),于是在安装 python2 版本的 supervisor 而运行环境为 python3 时,运行 supervisor 就会出现如下错误,找不到对应包:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    Traceback (most recent call last):
    File "/usr/bin/supervisord", line 6, in <module>
    from pkg_resources import load_entry_point
    File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 3088, in <module>
    @_call_aside
    File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 3072, in _call_aside
    f(*args, **kwargs)
    File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 3101, in _initialize_master_working_set
    working_set = WorkingSet._build_master()
    File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 574, in _build_master
    ws.require(__requires__)
    File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 892, in require
    needed = self.resolve(parse_requirements(requirements))
    File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 778, in resolve
    raise DistributionNotFound(req, requirers)
    pkg_resources.DistributionNotFound: The 'supervisor==3.3.1' distribution was not found and is required by the application
  • 通过 pip 安装的 supervisor 能够正常匹配所使用的 python 版本(废话),但是
    • 使用当前用户安装时程序会被放置到家目录的 .local/lib/python*/site-packages/ 目录下,虽然能够调用但是不是常规位置,不便于管理
    • 使用 sudo 或 root 权限安装时会安装到 /usr/local/lib/ 目录下,可执行文件在 /usr/local/bin/ 目录中,比较正常

最终方案

配置安装 supervisor 并使其支持通过 service 命令控制启停,然后在 Windows 下设置开机时启动 supervisor 进行,借此启动注册在 supervisor 下的各种服务

  • 使用默认 python 版本的话可以直接从包管理器安装 supervisor,之后通过以下命令来启动进程,相关配置文件在 /etc/supervisor/ 下
    1
    sudo service supervisor start

配置步骤

安装 supervisor 并配置

  • 使用 python3 但系统默认版本为 python2 的情况,使用 pip 进行全局安装,但配置文件使用包管理器自带版本,即 /etc/init.d/ 目录下相关服务管理脚本与 /etc/supervisor/ 配置文件目录。这里为了部署方便我会直接贴出从包管理器安装版本提取的服务管理脚本以及 supervisor 配置文件。以下为部署步骤:
    • 使用 pip 全局安装 supervisor
      1
      sudo pip install supervisor
    • 创建服务管理脚本
      1
      sudo vi /etc/init.d/supervisor
      以下为脚本内容,已修改程序路径
      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
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      #! /bin/sh
      #
      # skeleton example file to build /etc/init.d/ scripts.
      # This file should be used to construct scripts for /etc/init.d.
      #
      # Written by Miquel van Smoorenburg <[email protected]>.
      # Modified for Debian
      # by Ian Murdock <[email protected]>.
      # Further changes by Javier Fernandez-Sanguino <[email protected]>
      #
      # Version: @(#)skeleton 1.9 26-Feb-2001 [email protected]
      #
      ### BEGIN INIT INFO
      # Provides: supervisor
      # Required-Start: $remote_fs $network $named
      # Required-Stop: $remote_fs $network $named
      # Default-Start: 2 3 4 5
      # Default-Stop: 0 1 6
      # Short-Description: Start/stop supervisor
      # Description: Start/stop supervisor daemon and its configured
      # subprocesses.
      ### END INIT INFO

      . /lib/lsb/init-functions

      PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
      DAEMON=/usr/local/bin/supervisord
      NAME=supervisord
      DESC=supervisor

      test -x $DAEMON || exit 0

      RETRY=TERM/30/KILL/5
      LOGDIR=/var/log/supervisor
      PIDFILE=/var/run/$NAME.pid
      DODTIME=5 # Time to wait for the server to die, in seconds
      # If this value is set too low you might not
      # let some servers to die gracefully and
      # 'restart' will not work

      # Include supervisor defaults if available
      if [ -f /etc/default/supervisor ] ; then
      . /etc/default/supervisor
      fi
      DAEMON_OPTS="-c /etc/supervisor/supervisord.conf $DAEMON_OPTS"

      set -e

      running_pid()
      {
      # Check if a given process pid's cmdline matches a given name
      pid=$1
      name=$2
      [ -z "$pid" ] && return 1
      [ ! -d /proc/$pid ] && return 1
      (cat /proc/$pid/cmdline | tr "\000" "\n"|grep -q $name) || return 1
      return 0
      }

      running()
      {
      # Check if the process is running looking at /proc
      # (works for all users)

      # No pidfile, probably no daemon present
      [ ! -f "$PIDFILE" ] && return 1
      # Obtain the pid and check it against the binary name
      pid=`cat $PIDFILE`
      running_pid $pid $DAEMON || return 1
      return 0
      }

      case "$1" in
      start)
      echo -n "Starting $DESC: "
      start-stop-daemon --start --quiet --pidfile $PIDFILE \
      --startas $DAEMON -- $DAEMON_OPTS
      test -f $PIDFILE || sleep 1
      if running ; then
      echo "$NAME."
      else
      echo " ERROR."
      fi
      ;;
      stop)
      echo -n "Stopping $DESC: "
      # [ -f $PIDFILE ] && killproc $prog || success $"$prog shutdown"
      start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE
      echo "$NAME."
      ;;
      #reload)
      #
      # If the daemon can reload its config files on the fly
      # for example by sending it SIGHUP, do it here.
      #
      # If the daemon responds to changes in its config file
      # directly anyway, make this a do-nothing entry.
      #
      # echo "Reloading $DESC configuration files."
      # start-stop-daemon --stop --signal 1 --quiet --pidfile \
      # /var/run/$NAME.pid --exec $DAEMON
      #;;
      force-reload)
      #
      # If the "reload" option is implemented, move the "force-reload"
      # option to the "reload" entry above. If not, "force-reload" is
      # just the same as "restart" except that it does nothing if the
      # daemon isn't already running.
      # check wether $DAEMON is running. If so, restart
      start-stop-daemon --stop --test --quiet --pidfile $PIDFILE \
      --startas $DAEMON \
      && $0 restart \
      || exit 0
      ;;
      restart)
      echo -n "Restarting $DESC: "
      start-stop-daemon --stop --retry=$RETRY --quiet --oknodo --pidfile $PIDFILE
      echo "$NAME."
      ;;
      status)
      echo -n "$NAME is "
      if running ; then
      echo "running"
      else
      echo " not running."
      exit 1
      fi
      ;;
      *)
      N=/etc/init.d/$NAME
      # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
      echo "Usage: $N {start|stop|restart|force-reload|status}" >&2
      exit 1
      ;;
      esac

      exit 0
    • 创建配置目录
      1
      2
      3
      sudo mkdir /etc/supervisor
      sudo mkdir /etc/supervisor/conf.d
      sudo vi /etc/supervisor/supervisord.conf
      以下为脚本内容
      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
      ; supervisor config file

      [unix_http_server]
      file=/var/run/supervisor.sock ; (the path to the socket file)
      chmod=0700 ; sockef file mode (default 0700)

      [supervisord]
      logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)
      pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
      childlogdir=/var/log/supervisor ; ('AUTO' child log dir, default $TEMP)

      ; the below section must remain in the config file for RPC
      ; (supervisorctl/web interface) to work, additional interfaces may be
      ; added by defining them in separate rpcinterface: sections
      [rpcinterface:supervisor]
      supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

      [supervisorctl]
      serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket

      ; The [include] section can just contain the "files" setting. This
      ; setting can list multiple files (separated by whitespace or
      ; newlines). It can also contain wildcards. The filenames are
      ; interpreted as relative to this file. Included files *cannot*
      ; include files themselves.

      [include]
      files = /etc/supervisor/conf.d/*.conf
    • 至此配置结束,启动服务的命令为
      1
      sudo service supervisor start

设置自启动

  • 使用 Windows + R 快捷键调出运行窗口,输入
    1
    shell:startup
  • 在弹出的文件目录下创建一个快捷方式,名称任意,其执行命令设为
    1
    C:\Windows\System32\wsl.exe -u root service supervisor start

至此所有的操作就完成了,向 supervisor 配置的各种服务也能够正常运作,具体的配置方式可以查阅官方文档。