标签: Linux

  • 排查 duckdb-node-neo 并发查询时的随机段错误

    问题的说明跟复现请看这里,这篇文章主要记录一下如何排查 node addon 代码引发的问题。

    保存进程崩溃的翻车现场

    在 Linux/KDE 环境下,当应用进程崩溃的时候,systemd 会记录下该进程的翻车现场(aka,coredump)。我们可以使用 coredumpctl 这个工具查看当前机器上发生过的 coredump。

      List all captured core dumps:
    
          coredumpctl
    
      List captured core dumps for a program:
    
          coredumpctl list program
    
      Show information about the core dumps matching a program with `PID`:
    
          coredumpctl info PID
    
      Invoke debugger using the last core dump:
    
          coredumpctl debug
    
      Invoke debugger using the last core dump of a program:
    
          coredumpctl debug program
    
      Extract the last core dump of a program to a file:
    
          coredumpctl --output path/to/file dump program
    
      Skip debuginfod and pagination prompts and then print the backtrace when using `gdb`:
    
          coredumpctl debug --debugger-arguments "-iex 'set debuginfod enabled on' -iex 'set pagination off' -ex bt"

    还原翻车现场

    通过上面的步骤获取到 coredump 后,可以搭配 debugger 工具跟 symbol file 定位出问题的代码。我选择的 debugger 工具是 gdb 搭配 CLion 作 UI。这样只要将 node addon 项目用 CLion 打开,并导入 coredump 后,就可以获得一个功能齐全的 GUI Debugger。

    用 CLion 打开 node addon 项目

    以 duckdb-node-neo 为例,这个项目使用 node-gyp 作为构建工具,而 CLion 只支持 CMAKE 工程,所以需要手动创建一份 CMakefile 文件,让 IDE 的智能提示正常工具,方便在 debug 的过程中查看定义、搜索引用等。

    下面的这份 CMake 文件是我跟 Claude 一起编写的,不一定能够通过 IDE 编译 node addon,但是可以提供智能补全。

    cmake_minimum_required(VERSION 3.10)
    project(duckdb_node_neo)
    
    set(CMAKE_CXX_STANDARD 17)
    
    # include node headers
    include_directories(/usr/include/node)
    
    # Node.js addon API headers
    include_directories(${CMAKE_SOURCE_DIR}/bindings/node_modules/node-addon-api)
    
    # DuckDB headers
    include_directories(${CMAKE_SOURCE_DIR}/bindings/libduckdb)
    
    # Add source files
    file(GLOB SOURCE_FILES
        "bindings/src/*.cpp"
    )
    
    # This is just for CLion's code intelligence - not for actual building
    add_library(duckdb_node_bindings SHARED ${SOURCE_FILES})
    
    # Define preprocessor macros used in the code
    add_definitions(-DNODE_ADDON_API_DISABLE_DEPRECATED)
    add_definitions(-DNODE_ADDON_API_REQUIRE_BASIC_FINALIZERS)
    add_definitions(-DNODE_API_NO_EXTERNAL_BUFFERS_ALLOWED)
    
    # Add Node.js specific definitions to help CLion's IntelliSense
    add_definitions(-DNAPI_VERSION=8)
    
    # Tell CLion to treat this as a Node.js addon project
    set_target_properties(duckdb_node_bindings PROPERTIES
        CXX_STANDARD 17
        PREFIX ""
        SUFFIX ".node"
    )

    生成带有 symbol 的二进制

    设置后 debugger 后,还需要提供一个 symbol 文件,帮助 debugger 定位代码位置。对于 node-gyp 而言,只需要在编译时设置 --debug 参数即可生成带有 symbol 的二进制。

    cross-replace node-gyp configure --verbose --arch=$TARGET_ARCH --debug && node-gyp build --verbose --arch=$TARGET_ARCH --debug

    接下来我们只要用带有 symbol 的二进制复现进程崩溃的问题,就可以得到一个方便 debug 的 coredump。

    定位问题

    通过 debug coredump 文件,可以很容易找到抛出错误的地方,但这次很不走运的是, error 变量是 NULL,没有提供任何有价值的信息。另外,可以发现 result_ptr_ 似乎处于一个没有被初始化的状态。

    查看 duckdb 的源码后发现,虽然 duckdb 对外提供的是 C API,但是其本身是用 C++ 开发的,在将 C++ 操作以 C API 的形式暴露时,duckdb 会捕获内部的异常,然后将其中的错误信息写入到 duckdb_result 中。

    目前 coredump 表明 result_ptr_ 未被 duckdb 正常初始化,而异常对象很可能已经在跳出 catch 作用域时被销毁,现在只能在运行时 debug 才能捕获到具体的错误信息了。

    附加到进程上 debug

    CLion 提供了非常好用的附加到未启动进程上 debug 的功能,只需要向 CLion 提供进程的 commandline,它就可以自动将 debugger 附加到该进程上。

    在开始 debug 之前,还需要设置一下 C++ 异常断点:

    当启动问题进程,并复现崩溃后,就可以在 debugger 视图查看到实时的异常对象信息:

    这里的 obj 就是异常对象,但是由于 CLion 没有识别出异常的类型,我们得手动用 gdb 指令查看 obj 的信息。

    通过 gdb 的输出,可以看到异常对象的类型是 duckdb::InvalidInputException

    这时在变量页面输入下面的指令就行:

    *(duckdb::InvalidInputException*)obj

    终于,更加具体的错误信息被挖掘出来了~

  • 早知道 zswap 这么配置就好了

    swap 对于 Linux Server 来说可能是一剂毒药,但对于我这种经常要打开几十个 Tab 的 Linux Desktop 用户来说则可能是救命稻草。网上关于 swap 的调优众说纷纭,我决定给这个充满争议的话题再添一些乱。

    简单来说,我发现使用完全不写入硬盘的 zswap 搭配一个 swap 文件(或分区)要比单纯只用 swap + zswap 要更加适合日常办公场景。

    zswap 是一种 swap 跟 RAM 中间的一个压缩池。当一块内存将被送进 swap 的时候,zswap 会将其先压缩然后放进一个单独的内存池中,而不是直接放进 swap 设备。当 zswap 内存池快满或者这块内存不适合压缩(比如压缩后体积变大了)的时候,zswap 则会将其直接放进 swap 设备。

    通过安装 aur/zswap-disable-writeback ,可以几乎禁用掉 zswap 将内存解压写入 swap 的行为,这时 zswap 就会像 zram/swap 一样工作——仅压缩内存到内存池,不写入到更慢的 swap 设备,例如,硬盘分区或者 swap 文件。

    这样做的好处就是 swap 设备可以空出来用于系统休眠,而且 zswap 仍可以给操作系统提供更多的内存空间,更高效的使用内存。

    比如,在一个有 32GB RAM 的设备上,默认预留 20% 的空间来创建 zswap 的内存池,假设压缩比在 4.0 左右,那么这台设备在理论上可以给应用程序分配的内存将高达 51.2GB ,立省一条 16GB 内存

    在上面的配置下,还可以将 swappiness 设置到 100 以上,让操作系统更加激进地将内存交换出 RAM,给当前正在运行的活跃操作空出更多的空间,以应对突发性的内存需求(比如,打开新的浏览器 Tab)。

    相较于只使用硬盘作为 swap 设备,禁用 writeback 的 zswap 还有经济性上的优势,只要我们的内存需求在大多数情况下能够被 zswap 的内存池满足(在上面的例子中大约是 51GB),就不会发生由 swap 引起的硬盘 IO,有助于延长硬盘的寿命。

  • 在机械革命无界 15XPro 暴风雪上运行 Linux

    最近把工作电脑换成了机械革命无界 15XPro 暴风雪,在收货后小小折腾了一两天后,终于让他大部分的功能工作正常了。

    键盘背光跟散热风扇的驱动

    可以参考这篇文章中的介绍安装来自德国一家公司维护的驱动。驱动安装好后,还可以安装类似鸡哥控制面板的 TUXEDO 控制面板,切换性能模式跟调整键盘背光 RGB:

    内置屏幕掉帧

    笔记本内置屏幕在 60Hz 时会频繁随机掉帧,这个现象跟 VRR 有关,需要强制启用 VRR。

    除此之外我还遇到了使用 Spectacle 截图时,外置显示器闪烁的问题,需要强制关闭外置显示器的 VRR。

    睡眠(suspend)后被立即唤醒

    当通过睡眠按键(Fn + F1)或者 systemctl suspend 进入睡眠状态时,系统会被立即唤醒,需要第二次尝试才能成功睡眠。

    反复阅读 https://wiki.archlinux.org/title/Power_management 系列文章后,我在 deepseek-r1 的帮助下成功地找出了这个问题的根因。

    TLDR

    创建一个 udev rule,禁用 PS/2 键盘的 Wakeup Trigger。

    # sudoedit /etc/udev/rules.d/99-disable-keyboard-wakeup.rules
    # Disable wakeup for PS/2 keyboard controller
    ACTION=="add", SUBSYSTEM=="serio", KERNEL=="serio0", ATTR{power/wakeup}="disabled"

    然后重载 udev rules:

    sudo udevadm control --reload-rules
    sudo udevadm trigger

    重启系统后验证是否生效:

    $ cat /sys/devices/platform/i8042/serio0/power/wakeup
    disabled

    现在尝试睡眠,就不会被立即唤醒了。

    排查过程

    首先安装 amd-debug-tools,运行 amd-s2idle test ,查看当前系统是否满足 s2idle 的要求:

    $ amd-s2idle test
    💻 AMD Ryzen AI 9 H 365 w/ Radeon 880M (family 1a model 24)
    💻 MECHREVO WUJIE Series (STX\KRK)
    🐧 Arch Linux
    🐧 Kernel 6.12.32-1-lts
    🔋 Battery BAT0 (OEM standard) is operating at 100.00% of design
    ✅ ASPM policy set to 'default'
    ✅ GPIO driver `pinctrl_amd` available
    ✅ PMC driver `amd_pmc` loaded (Program 11 Firmware 93.4.0)
    ✅ USB3 driver `xhci_hcd` bound to 0000:65:00.4, 0000:67:00.0, 0000:67:00.3, 0000:67:00.4
    ✅ USB4 driver `thunderbolt` bound to 0000:67:00.6
    ✅ System is configured for s2idle
    ✅ GPU driver `amdgpu` bound to 0000:65:00.0
    ✅ PC6 and CC6 enabled
    ✅ SMT enabled
    ✅ IOMMU properly configured
    ✅ ACPI FADT supports Low-power S0 idle
    🚦 Logs are provided via dmesg, timestamps may not be accurate over multiple cycles
    ✅ LPS0 _DSM enabled
    ✅ WLAN driver `mt7921e` bound to 0000:62:00.0
    ❌ Kernel is tainted: 12288
    💯 Your system does not meet s2idle prerequisites!
    🗣️ Explanations for your system
    🚦 Kernel is tainted
    A tainted kernel may exhibit unpredictable bugs that are difficult for this script to characterize. If this is intended behavior run the tool with --force. 
    For more information on this failure see:https://gitlab.freedesktop.org/drm/amd/-/issues/3089

    可以看到这里提示我的 kernel 被 tainted 了,这是安装 mechrevo-drivers-dkms 引起的,可以忽略。

    既然系统符合 s2idle 的要求,说明可能是某个设备在唤醒系统,deepseek 给出了接下来的排查思路,启用 PM 日志:

    # Enable verbose PM debugging
    echo 1 | sudo tee /sys/power/pm_print_times<br>echo 1 | sudo tee /sys/power/pm_debug_messages
    # Now try to suspend again
    systemctl suspend
    # Check dmesg after resume
    dmesg | grep -i "wake\|resume\|acpi"

    再次执行 systemctl suspend 复现问题后,可以在 dmesg 中看到下面的日志:

    [ 4636.873132] xhci_hcd 0000:67:00.4: PM: pci_pm_suspend_noirq returned 0 after 42252 usecs  
    [ 4636.873146] thunderbolt 0000:67:00.6: PM: pci_pm_suspend_noirq returned 0 after 42269 usecs  
    [ 4636.873169] pcieport 0000:00:08.3: PM: calling pci_pm_suspend_noirq @ 29723, parent: pci0000:00  
    [ 4636.878632] pcieport 0000:00:02.1: PM: pci_pm_suspend_noirq returned 0 after 10561 usecs  
    [ 4636.878653] pcieport 0000:00:03.2: PM: pci_pm_suspend_noirq returned 0 after 11025 usecs  
    [ 4636.885192] pcieport 0000:00:08.1: PM: pci_pm_suspend_noirq returned 0 after 12279 usecs  
    [ 4636.885300] pcieport 0000:00:08.3: PM: pci_pm_suspend_noirq returned 0 after 12119 usecs  
    [ 4636.885359] PM: noirq suspend of devices complete after 54.690 msecs  
    [ 4636.885376] ACPI: _SB_.PCI0.GPP5: LPI: Constraint not met; min power state:D1 current power state:D0  
    [ 4636.885381] ACPI: _SB_.PCI0.GPP6: LPI: Constraint not met; min power state:D1 current power state:D0  
    [ 4636.885388] ACPI: _SB_.PCI0.GPP4.SDCR: LPI: Constraint not met; min power state:D3hot current power state:D0  
    [ 4636.886012] PM: Triggering wakeup from IRQ 9  
    [ 4636.886504] ACPI: _SB_.PEP_: Successfully transitioned to state screen off  
    [ 4636.887420] ACPI: _SB_.PEP_: Successfully transitioned to state lps0 ms entry  
    [ 4636.887625] ACPI: _SB_.PEP_: Successfully transitioned to state lps0 entry  
    [ 4636.888555] PM: suspend-to-idle  
    [ 4636.888592] ACPI: EC: ACPI EC GPE status set  
    [ 4636.888623] ACPI: PM: Rearming ACPI SCI for wakeup  
    [ 4636.891279] PM: Triggering wakeup from IRQ 1  
    [ 4639.593124] amd_pmc: SMU idlemask s0i3: 0xffff1abd  
    [ 4639.593188] ACPI: PM: Wakeup unrelated to ACPI SCI  
    [ 4639.593189] PM: resume from suspend-to-idle  
    [ 4639.594699] amd_pmc AMDI000A:00: Last suspend didn't reach deepest state  
    [ 4639.595185] ACPI: _SB_.PEP_: Successfully transitioned to state lps0 exit  
    [ 4639.595760] PM: Triggering wakeup from IRQ 9  
    [ 4639.596746] ACPI: _SB_.PEP_: Successfully transitioned to state lps0 ms exit  
    [ 4639.597719] ACPI BIOS Error (bug): Could not resolve symbol [_SB.ACDC.RTAC], AE_NOT_FOUND (20240827/psargs-332)  
    [ 4639.597729] ACPI Error: Aborting method _SB.PEP._DSM due to previous error (AE_NOT_FOUND) (20240827/psparse-529)  
    [ 4639.597737] ACPI: _SB_.PEP_: Failed to transitioned to state screen on

    Deepseek 大胆推断是 IRQ 1 (typically the i8042 keyboard controller) 造成的问题,可以通过下面的方法来验证:

    echo "disabled" | sudo tee /sys/devices/platform/i8042/serio0/power/wakeup

    重试睡眠后,系统果然安然入睡了~

    进入 s2idle 睡眠后,一晚上大概会消耗 10% 左右的电量。

    Firefox 播放视频时会触发 GPU 图形崩溃

    当启用 Firefox 的硬件加速后,播放视频会让 GPU 崩溃,丢失全部的显存内容。还好这个问题已经被 AMD 修复,升级到最新的内核即可。

    色彩管理

    机械革命提供了官方的色彩文件,但必须要在 Windows 下安装机械革命控制台才能下载(官方客服并不受理此类咨询)。加载了官方的色彩配置文件后,可以比较明显地缓解低亮度下对比度过低的问题。为了方面读到这篇文章的你,点击下面的链接就可以下载到这份色彩配置文件~

    https://smartshare.zeeko.dev/share.cgi?ssid=b6dc9a3f850d4ba9b8e97f3a1018786c

    访问密码:NJIVkJR

  • Zerotier: No route to host

    搭建自己的 homelab ,在局域网尽情冲浪~

    3 / 4 of homelab 历险记

    一觉醒来,突然发现家里的 OpenWRT 软路由无法通过 Zerotier 网络访问了,虽然面板上显示设备在线,但不管是 Ping 还是 curl ,都会报错:No route to host。

    09:54:21.055775 [0-0] * [HTTPS-CONNECT] adjust_pollset -> 1 socks
    09:54:22.048545 [0-0] * [HTTPS-CONNECT] connect, check h21
    09:54:22.049187 [0-0] * connect to 192.168.100.22 port 443 from 10.1.1.111 port 59496 failed: No route to host

    首先,我们需要判断这个问题是否能在 Zerotier 网络中其他节点复现,通过登录不同节点执行 ping 跟 curl,发现只有 OpenWRT 在 Zerotier 网络中不可达。

    接下来登录 OpenWRT 上查看 Zerotier 客户端的状态:

    $ zerotier-cli info -j
    {
     "address": "....",
     "clock": 1748052290108,
     "config": {
      "settings": {
       "allowTcpFallbackRelay": true,
       "forceTcpRelay": false,
       "homeDir": "/var/lib/zerotier-one",
       "listeningOn": [
        "192.168.100.1/9993",
        "192.168.1.2/9993"
       ],
       "portMappingEnabled": true,
       "primaryPort": 9993,
       "secondaryPort": 44537,
       "softwareUpdate": "disable",
       "softwareUpdateChannel": "release",
       "surfaceAddresses": [
        "家宽公网 IP"
       ],
       "tertiaryPort": 44486
      }
     },
     "online": true,
     "planetWorldId": 149604618,
     "planetWorldTimestamp": 1738848951118,
     "publicIdentity": "....",
     "tcpFallbackActive": false,
     "version": "1.14.1",
     "versionBuild": 0,
     "versionMajor": 1,
     "versionMinor": 14,
     "versionRev": 1
    }
    
    $ zerotier-cli peers
    200 peers
    <ztaddr>   <ver>  <role> <lat> <link>   <lastTX> <lastRX> <path>
    778cde7190 -      PLANET   225 DIRECT   44578    134480   103.195.103.66/9993
    cafe04eba9 -      PLANET   318 DIRECT   44578    134387   84.17.53.155/9993
    cafe80ed74 -      PLANET   165 DIRECT   4519     129532   185.152.67.145/9993
    cafefd6717 -      PLANET   263 DIRECT   44578    129434   79.127.159.187/9993

    可以看到 OpenWRT 上的客户端并没有显示任何异常信息,这也对应了控制面板上看到的设备在线状态。但值得注意的是,在 surfaceAddresses 字段上显示的是我的家庭宽带公网出口地址,这也对应了控制面板上显示的该设备的物理地址。考虑到 Zerotier 可能已经被中国联通给 Ban 了,所以接下来可以尝试让 Zerotier 客户端通过代理连接 Planet。

    这时,有意思的事情出现了,尽管我在代理软件中正确地设置了 Zerotier 的协议转发规则(转发目标端口为 9993 的 TCP/UDP 流量),但我在代理软件的日志中却无法查看到 Zerotier 相关的流量信息。

    我的规则设置有误还是代理软件在这个场景下存在 Bug ?这可以通过一个实验来验证,使用 ncat -vuz 84.17.53.155 9993 就可以向 Zerotier 的 Planet 发送一个用来测试连接性的 UDP 包。

    Ncat: Version 7.93 ( https://nmap.org/ncat )
    Ncat: Connected to 84.17.53.155:9993.
    Ncat: UDP packet sent successfully
    Ncat: 1 bytes sent, 0 bytes received in 2.00 seconds.

    可以看到这个 UDP 包被成功发送了,而且在代理软件的日志中也能找到相关的转发记录。这说明我的代理软件规则配置是正确的,而且代理软件也没有任何问题。那为什么在代理软件的日志中看不到 Zerotier 的流量呢,难道 Zerotier 客户端压根没有去连接 Planet ?这就需要使用 Wireshark 来分析 OpenWRT 的流量了。

    # on my laptop
    $ ssh openwrt 'tcpdump  -s 0 -U -n -w - -i eth0' | sudo wireshark -k -i -<br>

    通过分析 OpenWRT 各个网络设备上的流量,可以发现 Zerotier 的 UDP 包确实被发送了,但它是通过 OpenWRT 的 WAN 地址发送的,而我的代理软件只会通过 nft 拦截来自 localhost 跟 LAN 的流量,这就解释了为什么代理软件无法转发 Zerotier 的流量。

    确实,在网络编程中,当机器被绑定了多个 IP 地址时,我们可以指定某个特定的 IP 作为源 IP,对于 VPN 软件 Zerotier 来说,这样做更是基操。所以,在 Zerotier 的配置文件中肯定存在某个配置项,用来设置 Zerotier 流量的出口网络。

    通过翻阅它的文档,可以看到有两个选项会影响 Zerotier 流量的发送地址:

    {
      "settings": {
        "interfacePrefixBlacklist": [ "XXX",... ], /* Array of interface name prefixes (e.g. eth for eth#) to blacklist for ZT traffic */
        "bind": [ "ip",... ], /* If present and non-null, bind to these IPs instead of to each interface (wildcard IP allowed) */
      }
    }

    在我的测试下,需要指定 bind 为 LAN 地址,才能真正解决这个问题。这样设置后,Zerotier 就会通过代理服务跟 Planet 沟通,在管理面板上,OpenWRT 的物理地址也会被展示成代理服务的 IP。

    实测下来,从外网访问 OpenWRT 的速度还是能达到家庭宽带的上限,网络延迟也还不错,问题终于解决了~

  • 如何让 MACvLan 网络下的容器与宿主机互联互通

    我在 NAS 上部署了一个 NginxProxyManager 方便管理其他 Self Host 服务的 SSL 证书,为了避免占用宿主机的 80/443 端口,我用了 macvlan 为 NPM 容器分配了一个独立的局域网 IP。

    networks:
      macvlan_network:
        driver: macvlan
        driver_opts:
          parent: <your-network-interface>
        ipam:
          config:
            - subnet: 192.168.100.0/24
              gateway: 192.168.100.1
    
    services:
      npm:
        image: 'jc21/nginx-proxy-manager:latest'
        restart: unless-stopped
        networks:
          macvlan_network:
            ipv4_address: 192.168.100.22
        environment:
          DISABLE_IPV6: 'true'
        volumes:
          - ./data:/data
          - ./letsencrypt:/etc/letsencrypt

    这时,局域网其他的设备就可以通过 192.168.100.22 访问 NPM,但是对于宿主机以及宿主机上其他的容器来说,这个地址是无法访问的

    在咨询了 GPT 之后,它建议了下面的方案:

    [Unit]
    Description=Set up specific macvlan route for a Docker container
    After=network.target
    
    [Service]
    Type=oneshot
    RemainAfterExit=yes
    # Replace <your-network-interface> with the actual interface name
    ExecStart=/usr/bin/ip link add mvlan0 link <your-network-interface> type macvlan mode bridge
    ExecStart=/usr/bin/ip addr add <unused-ip>/24 dev mvlan0
    ExecStart=/usr/bin/ip link set mvlan0 up
    ExecStart=/usr/bin/ip route del 192.168.100.0/24 dev mvlan0
    ExecStart=/usr/bin/ip route add 192.168.100.22 dev mvlan0
    ExecStop=/usr/bin/ip link delete mvlan0
    
    [Install]
    WantedBy=multi-user.target

    默认情况下的问题:

      • 当使用 macvlan/ipvlan 时,Docker 容器会直接连接到物理网络接口
      • 但是宿主机无法通过物理接口直接与这些容器通信,因为物理接口不会处理宿主机到 macvlan 接口的流量

      创建 mvlan0 的作用:

      sudo ip link add mvlan0 link <your-network-interface> type macvlan mode bridge
      sudo ip addr add <unused-ip>/24 dev mvlan0
      sudo ip link set mvlan0 up
      • 这实际上是在宿主机创建了一个新的网络接口 mvlan0
      • 这个接口也是一个 macvlan 接口,与 Docker 容器使用相同的技术
      • mvlan0 接口可以看作是宿主机在 192.168.100.0/24 网段的一个”入口”

      通信流程:

      宿主机 -> mvlan0 (unused-ip) -> 物理网卡 -> Docker 容器 (192.168.100.22)
      • 当宿主机要访问 192.168.100.22 时
      • 数据包会通过 mvlan0 接口发出
      • 由于 mvlan0 和容器的网络接口都是 macvlan 类型,它们可以在同一层次通信

      类比解释:

        • 可以把这个设置想象成宿主机也”加入”到了容器所在的网络
        • mvlan0 就像是宿主机的一个”虚拟网卡”,使它能够与容器在同一个网络层面通信

        为什么需要这样:

          • 这种设置避免了常规的网络栈限制
          • 提供了一个直接的通信路径
          • 保持了网络隔离性的同时又允许必要的通信

          这就是为什么添加这个 macvlan 接口可以让宿主机访问容器的原理。这种方法既保持了容器网络的独立性,又提供了必要的访问路径。

        1. 轻松备份,安心生活:个人电脑全盘备份指南

          有没有想过,如果电脑因为一些意外原因突然罢工,所有的宝贵记忆和重要文件就这么不翼而飞?这可不是危言耸听,但好消息是,有了全盘备份,这种噩梦就可以避免了。

          为什么要备份?

          想象一下,你的笔记本电脑突然不工作了,所有的工作文档、个人照片以及定制的系统设置都无法访问。这种情况下,如果你有一个最新的系统全盘备份,那么你可以快速地在新电脑上恢复工作环境。这就像是给数据做了一个时间胶囊,无论发生什么,都能回到过去,将你的数字生活恢复到最后一次备份的状态。

          全盘备份是什么?

          系统全盘备份与仅备份用户数据不同,它是对电脑上所有内容的完整克隆,包括操作系统、应用程序、设置和所有文件。这意味着在恢复过程中,你无需担心哪些隐藏的配置文件没被备份或者某些应用程序无法正常工作,因为一切都会被还原到备份时的状态。

          怎么选备份软件?

          作为一个对价格敏感的用户,我的选择标准很实在:便宜、好用、快速、还得能省空间。我找到了两款不错的备份软件,一款适合 Windows,一款适合 Linux:

          要求Dism++ (Windows)restic(Linux)
          成本免费软件,备份会被压缩,硬盘占用较小开源软件,备份会被压缩,硬盘占用较小
          备份格式WIM,标准的 Windows 部署文件格式开源的专有备份格式
          备份速度不比直接复制文件慢多少 🚗速度超快 🚀
          增量备份不支持增量备份 😥支持增量备份 😃
          其他优势WePE 内置工具支持加密备份文件;支持查看备份系统中的文件内容

          我选择 Dism++ 和 restic 的原因是它们都能够提供简洁且高效的备份解决方案。Dism++ 的备份文件格式是 WIM,这是 Windows 的标准部署格式,意味着即使未来 Dism++ 不再更新,我也能找到方法来恢复我的数据。Restic 虽然使用的是开源的专有备份格式,但它的速度和增量备份功能使得它成为 Linux 环境下的理想选择。这两款工具都解决了我对备份速度和便捷性的需求。

          备份计划

          我的备份策略是定期且频繁的备份。对于 Windows 系统,我每季度备份一次 C 盘;而 Linux 系统则更频繁,每周都要备份一次,因为这是我的主力办公电脑,这样的策略确保了在任何紧急情况下我都能迅速恢复工作环境。

          数据恢复测试与体验

          案例 1:备份并恢复 Windows 系统

          • 准备:使用带有 WePE 的 U 盘进行离线备份,将备份文件存储在一块旧的 1TB 移动硬盘上。
          • 结果:整个系统原本有 250GB ,被压缩成大概 80GB WIM 镜像,恢复后一切正常,就连原系统 Surface Book 的特殊驱动都在。所以还需要手动清理一些不必要的驱动程序。手动清理 Windows 驱动有风险,如果旧驱动没有清理干净可能导致新驱动不生效,可以参考这篇文章尝试修复。

          案例 2:备份并恢复 Linux 系统

          • 准备:使用 systemrescue Live CD 的 U 盘恢复备份,备份文件存储在运行 restic server 的 NAS 上。
          • 结果:原系统大概 300 GB,备份文件总体积约为 40GB ,恢复速度快。尽管需要手动修复 bootloader,但对于有经验的用户来说,这不是难题。

          安全与隐私

          备份时,我确保不通过公网传输数据。而且,为了防止家里 NAS 遭遇不测,我还会把加密后的备份放到云盘,实现异地存储。不过得提醒一下,Dism++ 不支持加密,所以要先用别的工具加密再上传,确保隐私安全。

          结论

          全盘备份就像是给电脑的生命买了份保险,无论遇到什么灾难,都能让你的数字生活重回正轨。备份或许听起来有点技术性,但实际操作起来并不复杂,而且它给你带来的安心,是任何金钱都买不到的。

        2. 网上冲浪指南 – 001

          网上冲浪指南 – 001

          AstroNvim https://astronvim.com

          一个全功能的 NVim 发行版,开箱即可获得非常完整的 IDE 体验,预设的快捷键很符合我这个前 SpaceVim 用户的使用直觉。它跟 SpaceVim 有很多不同的地方:

          • AstroNvim 使用 lua 作为配置语言,官方提供了一个用户配置模板帮助用户自定义 AstroNvim。
          • 正如它的名称所示,AstroNvim 只支持 neovim 。

          这是我的 AstroNvim 配置 https://github.com/ZeekoZhu/AstroNvim

          Burning Vocabulary https://burningvocabulary.com

          这是一款简单好用的背单词浏览器插件,网上冲浪遇到不认识的单词,只需要用鼠标选中它,插件就会自动查询翻译并展示翻译结果,并且在之后的浏览中,会自动在网页上标注查过的生词。我个人认为,类似的浏览器插件有很多,但上手难度比它低、使用的顺滑度比它高不多:

          我现在会把 Burning Vocabulary 翻译的目标语言设置成英文(就是用英文解释英文),跟沙拉词典搭配使用,因为沙拉词典支持的词典源更加丰富,让我暂时还不能放弃。

          System Rescue https://www.system-rescue.org

          这是一个 Linux 系统急救工具箱,基于 Arch Linux 开发,一共 700M 的镜像大小,可以使用 Ventoy 制作成 Live CD 使用。

          最近我在扩容工作用笔记本 /root 分区时发现了它,自带图形界面跟 GParted,比单纯的 Arch Install ISO 要方便很多。

        3. 不花钱提升工作笔记本的电池续航

          我的工作电脑是一台安装 Arch Linux 的轻薄笔记本,虽说 Linux 比 Windows 更加节省资源,但是我的工作笔记本续航比较尿崩,出门必须带充电器,进店必找有插座的位置。看到同事的 mbp 的超长续航不可能不羡慕,但我也不奢望过多,只期望出门后,笔记本能够支撑一下午的正常办公就足够了。

          amd_pstate

          刚好最近注意到 Linux 6.1 内核中集成了 amd_pstate 模块,这是用来实现动态调节 Ryzen CPU 频率的驱动模块,相较于原先的 acpi_cpufreq 驱动, amd_pstate 支持 CPPC(Collaborative Processor Performance Control) 功能,简单来说,CPPC 可以根据系统对性能、功耗的需求,调节 CPU 的工作频率。

          支持 CPPC 的 CPU driver 需要跟合适的 CPU scaling governor 配合使用。后者是 Linux 系统中用来调节 CPU 频率的软件,不同的 gorvernor 实现了不同的 CPU 频率调整算法,跟 CPPC 最搭配的是下面两个 gorvernor:

          • ondemand:基于当前工作负载动态调节 CPU 频率
          • schedutil:基于调度器的反馈动态调节 CPU 频率

          这两种 gorvernor 都能根据操作系统的需要动态降低或提升 CPU 的工作频率,从而实现比较理想节能效果。

          对于运行 6.1 版本或者更新内核的 Linux ,可以参考这个链接启用 amd_pstate 驱动。

          检测 amd_pstate 生效的方式也比较简单,推荐安装 cpupower

          analyzing CPU 14:
            driver: amd-pstate
            CPUs which run at the same hardware frequency: 14
            CPUs which need to have their frequency coordinated by software: 14
            maximum transition latency: 20.0 us
            hardware limits: 400 MHz - 1.90 GHz
            available cpufreq governors: conservative ondemand userspace powersave performance schedutil
            current policy: frequency should be within 400 MHz and 1.90 GHz.
                            The governor "ondemand" may decide which speed to use
                            within this range.
            current CPU frequency: Unable to call hardware
            current CPU frequency: 1.11 GHz (asserted by call to kernel)
            boost state support:
              Supported: yes
              Active: no
              AMD PSTATE Highest Performance: 166. Maximum Frequency: 4.51 GHz.
              AMD PSTATE Nominal Performance: 70. Nominal Frequency: 1.90 GHz.
              AMD PSTATE Lowest Non-linear Performance: 41. Lowest Non-linear Frequency: 1.11 GHz.
              AMD PSTATE Lowest Performance: 15. Lowest Frequency: 400 MHz.
          

          对于笔记本来说,更常见的需求是根据电源状态自动设置性能偏好,这里推荐使用 TLP,为了方便使用,我还安装了 tlpui。TLP 允许我们配置电脑在不同电源状态的 CPU 调节参数:

          • gorvernor
          • 最低频率、最高频率
          • 自动超频开关

          不过 TLP 的自动超频开关有些死板,即使是出门在外使用电池的场景,我也希望偶尔开启 Boost 提高一下 IDE 分析代码的速度。解决这个问题很简单,有两个方法:

          1. 手动按需执行开关 CPU Boost 的命令
          2. 安装 auto-cpureq,这个软件会在后台监听当前的工作负载,会在必要的时候开关 Boost

          经过一段时间的观察与调试,为了尽量减少风扇噪音、提高电池续航,我的笔记本(HP EliteBook 845 G8 / Ryzen 5800U)配置如下:

          • 外部电源:schedutil gorvernor,启用自动超频,限定最低频率在标称频率的 68%(大概 1.1GHz)
          • 内置电池:ondemand gorvernor, 配合 auto-cpufreq 动态调整超频开关

          实际续航测试

          目前的电池状况,标称容量 45wh,电池健康度 76%,屏幕亮度 30% ~ 35%。工作时会使用的软件包括:

          • Webpack Dev Server
          • WebStorm(180kloc TypeScript 项目)
          • Vivaldi 浏览器(通常控制在 30 个标签以内)
          • Firefox 浏览器(通常控制在 3 个标签以内)
          • 飞书 Linux 客户端
          • Telegram

          acpi_cpufreq

          在 acpi_cprfreq 驱动下,我的笔记本电脑的最低运行频率是 1.9 GHz,最高可以达到 4.1 GHz,下面是我记录的两次续航时长。

          roundstart timeend timetime to 10%
          12023-02-06 13:322023-02-06 16:413:10
          22023-02-13 14:112023-02-13 17:353:24

          amd_pstate

          在 amd_pstate 驱动下,最低运行频率可以到 400 MHz,通常会在 900 MHz 徘徊,最高可以达到 4.1 GHz。

          roundstart timeend timetime to 10%
          12023-02-19 10:1514:474:32
          22023-02-20 9:3714:304:53
          32023-03-06 13:3117:314:00

          在 IDE 的使用体验上没有明显区别,小项目依然很流畅,巨型项目该卡的操作还是卡,但是续航的提升非常明显,至少出门重度办公一下午没有续航焦虑了。

          如何进一步改善续航?

          关于这点,我建议你看看 极客湾的观点

          • 尽量减少 Boost 时长
          • 减少 CPU 以外硬件的功耗

          普通消费者几乎没法做硬件上的改进(最多禁用独显以提高续航),只能从减少 Boost 时长上入手,毕竟是否开启 Boost、需要多长时间的 Boost,这些参数是可以通过软件调节的,上面提到的 auto-cpufreq 就是干这件事的,这个软件是用 python 编写,除了可以通过配置文件来设置之外,自己也可以根据实际体验和偏好来调节相关算法。

        4. 2022 年,我的 Wayland 工作电脑设置

          Wayland 比 Xorg 要快很多,在没有独立显卡的笔记本电脑上对桌面动画性能提升还是很明显的,忍受不了卡顿掉帧的桌面动画,我决定切换到 Wayland 。

          DE

          试用了各种 DE、WM 后,我的最终选择是 KDE,KDE 支持为不同分辨率的屏幕设置不同的 DPI,并且能够清晰地在不同的屏幕上缩放 XWayland 应用,使之适配多个分辨率不同的显示器。

          只有真正去折腾的人才知道,让 Linux 桌面适配多个分辨率差异很大的屏幕有多痛苦,KDE Wayland 能够以最小的代价实现让人满意的效果。

          在 XOrg 下,为了实现为多块屏幕设置不同分数缩放,我使用的是全局设置两倍缩放,然后通过 xrandr 压缩到合适分辨率的做法,这种方式比较消耗性能,但还在可以勉强忍受的范围内。

          Wayland 下的 GNOME 虽然支持为不同显示器设置不用的缩放系数,但是分数缩放的支持比较差,使用 XWayland 渲染的软件会先按照 1 倍渲染,然后被系统强制放大到指定的倍率,其效果可想而知,满脸的马赛克锯齿。

          Wayland 下的 KDE 就厉害多了,可以直接在系统设置中针对不同的屏幕设置分数缩放,而且性能还很不错,最重要的是,KDE 会自动管理那些不支持 Wayland 的软件的缩放系数,最终可以让我们得到非常 Nice 的显示效果。

          左侧是笔记本内置屏幕(1920×1080, 14″,150%)上的 KDE 系统设置,使用原生 Wayland 渲染,右边是外接显示器上(3840×2160, 24″,200%)基于 Chromium 的 Obsidian,使用 XWayland 渲染。

          打工人必备

          飞书 XWayland

          Wayland 下的飞书跟 XOrg 下的使用体验没有太大区别,主要是 Wayland 下共享桌面需要一些额外的设置,可以参考下面的做法:

          1. 启用 Chromium 的一个 flag: enable-webrtc-pipewire-capturer
          2. 安装 xdg-desktop-portal-kde

          这种做法对于其他的基于 Chromium 的软件应该同样使用,其中的原理可以查看这篇文章

          截图与录屏

          截图工具用的是 Flameshot,虽然对多显示器的支持比较早期,但是用的顺手。KDE 自带的 spectacle 能用,但在我的电脑连接外置显示器的时候比较卡,而且不太符合我的使用习惯。

          录屏比较麻烦,我之前用的 SimpeScreenRecorder 并不支持 Wayland,找了一圈发现很多带图形界面的录屏软件在 KDE Wayland 下要么卡顿,要么功能上有 Bug。还有 Sway 用户推荐的各种命令行工具,感觉用起来会很不方便。最终我还是决定用 OBS 来录屏,属于是用牛刀来杀鸡了,但是至少稳定高效。

          输入法

          对于 KDE 用户来说,直接安装 fcitx5 应该就可以开箱即用了,如果发现输入法候选框没有在有些应用中出现,可以参考这里的说明试试。

          开发必备

          JetBrains 系列

          JetBrains 所有基于 Idea 的软件以及 Fleet 目前都只能在 XWayland 下运行,不过就我目前的使用体验来看,没有遇到任何阻断性的问题。

          WebStorm 2022
          Fleet

          KDE 会为 Fleet 设置 1.5 倍缩放(跟我设置的全局缩放一致),但实际上 Fleet 的 UI 库似乎会跟随全局缩放自动调整,所以得手动在 Fleet 中把缩放系数改为 75%,把 KDE 设置的缩放抵消掉。

          终端模拟器

          虽然用的 KDE,但是我没有选择使用 Konsole 作为终端模拟器,在我看来,响应更加迅速、跟 Shell 融和地更好的 kitty 更有吸引力。如果你发现无法在 kitty 窗口中调用 fcitx5,可以参考这里

          设置全局唤起 kitty 的快捷键需要一点 tricks,KDE Wayland 把设置自定义快捷键的设置面板换掉了,需要通过执行 QT_QPA_PLATFORM=xcb systemsettings 找回旧的快捷键设置面板。