Hash
觉得什么笔记软件都不好用,恰好疲于选择博客主题,因此使用 mdBook 记下一点东西。
才疏学浅,应该有很多不对的地方,欢迎和我一起讨论 ^^
关于我
网名“一粒”、常用提交 ID qaqland,是 Alpine Linux && deepin Linux 桌面用户。
我的计算机知识和代码水平比较肤浅:
- 会一点 C 但是既没编译过内核也没搞过驱动
- 会一点 Rust 不过日常看不懂生命周期
- 会一点 Linux 但总是听不懂群友在讨论什么
说要做很多项目,但是至今 3000 行以上的没写出来几个。目前还在关注的有:
- Wless:定位上高于 cage 约等于 dwl 的窗管,基于 wlroots
- BuShi:目标是取代 cgit 成为 gitweb 的首选,基于 Rust 和 SQLite
以上两个项目进行了初步尝试,位于 30% 阶段。
2025-10
Alpine Linux
- https://gitlab.alpinelinux.org/alpine/aports/-/issues/17499
- https://gitlab.alpinelinux.org/alpine/aports/-/issues/17605
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/90725
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/91364
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/91610
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/91611
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/91612
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/91614
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/91664
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/91692
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/91928
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/91929
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/92020
原计划在国庆假期把 Python 相关的包努力做一做,setup.py install 相关函数将要在
月底废弃,但是经过努力决定开摆,原因有下:
- 当前在不测试的情况下完全无法分辨是否依赖有缺失
- 很多上游不着急,构建系统的切换还是上游做合适
- 缺少项目经验,不明白 nox、pytest、uv 等的工作原理
- 使用的 py3-gpep517 脚本协议错误,提了修改没人管
这个月 pmos 的人引入了 systemd 子包功能到 abuild,也算是对社区的又一次冲击(摊手), 不知道 WHLUG 能不能做个闪电分享讲讲这些事:
- https://gitlab.alpinelinux.org/alpine/abuild/-/merge_requests/272
- https://gitlab.alpinelinux.org/alpine/abuild/-/merge_requests/433
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/91516
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/84539
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/85504
- https://github.com/systemd/mkosi/pull/3781
Wless & Wayland
隔了一整个 8 月和 9 月没写,之前写的代码有点看不懂了,于是也重新看。
- 在 sway 中 layer-shell 是挂在 output 下,渲染时 reparent 到 server
- 在 dwl 中 border 是挂在 client 下,对于我的场景应该适合挂在 output 里
wlr_scene_node_set_size需要考虑到 pending 状态(tinywl 的 resize 没考虑)
之前拆多文件是为了使用 AI 写代码理解方便,但是体验了 VSCode + cline + DeepSeek, 小几千行的情况下,单文件对 AI 和对我的原生 Vim 来说都更方便,我就改回去了。
协议 wlr output management 是为了客户端 Read 或者 Write 服务端的显示器设置
-
zwlr_output_manager_v1- request
create_configuration>>zwlr_output_configuration_v1 - request
stop - event
head>>zwlr_output_head_v1只读的广播,没什么逻辑 - event
done - event
finished
- request
-
zwlr_output_configuration_v1- request
enable_head>>zwlr_output_configuration_head_v1+zwlr_output_head_v1 - request
disable_head>>zwlr_output_head_v1 - request
apply - request
test - request
destroy
- request
-
zwlr_output_configuration_head_v1- request
set_{mode,position,etc.}
- request
-
https://wayland.app/protocols/wlr-output-management-unstable-v1
10 月用两个周末的时间完全重写了之前的配置文件(主要是快捷键)的逻辑,
大幅度依赖 getopt、getsubopt、wordexp 这三个标准库函数。
修改之后实现了两个目标:
- 决定把部分复杂功能从 WM 本体中丢出去,降低配置的复杂度
- 命令行与配置文件使用同一套解析函数
认真学习了 wlr_output_layout_output 相关内容(并不认真):
显示器的 enable 在非原生后端时可能会修改,但是如果 output 不在整个 layout 中,
我们也应该认为这个显示器是 disable 的。应用到具体场景就是用户的笔记本显示器熄屏,
此时物理上显示器连接,但是显示器没有真正投入使用,用户可能扣着盖子用外接屏幕。
Bushi & Git
重新审视了这个项目的整体架构,思考下一步真的要做什么。 项目已有的“祖先跳表”设计令人兴奋,但是内部数据的生成还是依赖 git-fast-export, 这不优雅:
- 很难控制 git-cli 本体的内存占用,据测算需要 800 Mb(使用 aports.git 仓库)
- 命令行输出的解析是用的手抠字符串,虽然能跑但相当原始
- 初始化更新和后续更新可能逻辑不一致,写两套函数无法接受
所以最好这里再细节一点。
先尝试的是字符串解析,尝试使用 nom 但阅读文档后发现,这个库必须先把字符串读取为
&str,这就很不合适,因为管道重定向是 Stream 流:数据量大、输出时间长、
不保证字符编码。目前也没有什么其他库做这件事很合适,所以搁置。
用原版 git-cli 和 libgit2 各做 1000 次提交差异对比,发现原版速度就是快一个数量级
export GIT_PAGER=cat
git diff --no-color --name-only "$commit_old" "$commit_new"
看了会儿 libgit2 的源码思考为什么如此慢。发现它读取 object-tree 这里可以改一下,
用 data 的 size 除以 oid_t + mode + 6,然后拿这个数值去初始化数组,
避免很多次 realloc。修改后对比测试,略有提升,效果微乎其微,平均百万次读取超大
object-tree 调用提升 10s(口算)。
- https://github.com/libgit2/libgit2/blob/58d9363f02f1fa39e46d49b604f27008e75b72f2/src/libgit2/tree.c#L405
- https://linux.die.net/man/3/realloc
- https://en.cppreference.com/w/c/memory/realloc
读取有 object 后的 diff 运算没有可以优化的地方,就是简单的双指针单向遍历。
所以还没什么其他加速思路。
从 git-sizer 程序的解析结果看,把仓库中所有的 object-tree 事先解析并塞到 SQLite 的方法会导致程序体积占用很大,预计有 20 倍的膨胀,所以不可取。理想预期:
- 【内存占用小】全程不超过 200 Mb,而且缓存大小可控
- 【硬盘占用小】因为必须保留原生 git-objects 做兼容,所以尽可能产物小于同期 fossil
- 【CPU 消耗低】初始化不作要求,查询时单线程可运行
- 【解析时间短】初始化时间低于 git-fast-export 耗时的三倍,查询时间低于 git-cli
下一步计划:当前的性能瓶颈有两个,object-tree 的 read 和 diff,它们被广泛压缩在 packfile 中,不仅 IO 紧张而且 CPU 紧张。但是逆转过来想,压缩本身也是一种信息? 可能需要写一点优化后的读取方法,在读取时抓取更多信息。
备用计划:手动封装 git
- 找个支持流式解析库,优化现在的 fast-import
- 魔改 fast-export 输出 json,然后接 serde::json
- 封装原版 git-diff-stat 用 nom
QT
发现一个 Bug,右键弹出菜单中,分隔线上的点击事件无效但会导致弹出菜单消失。 使用 AI 写了个复现小 demo,提交问题到了 QT 上游。
# 感谢子冲帮忙修复 LSP 爆红
sudo apt install libstdc++-13-dev
不知道下一步会不会学习 QT 做点小工具。
论文
导师喊我改论文,这让我非常难过,尽管我知道这件事情只要完成就再也不会有烦恼了。 但我还是很难过,无法诉说的压力和恐惧,让我没有失去了对生活 60% 的注意力和快乐。 有以下几个方面需要改:
- 标点符号、文字、句子之间的连贯性
- 图表、公式、大小写
好像没了。我尽量去做吧。祝我开心。
Vim
学到了 Vim 自带的 grep 命令,非常好用,再也不需要终端里查询复制到 Vim 中再打开了。
现在是接入了 ripgrep 日常使用,配置文件略微修改:
if executable("rg")
set grepprg=rg\ --vimgrep\ --smart-case
set grepformat+=%f:%l:%c:%m
endif
把之前在 Vim 上修的小补丁 cherry-pick 到了 NeoVim 上
- https://github.com/vim/vim/commit/ce4f9d2a1016ade19fa07c5b66e58eb084719192
- https://github.com/neovim/neovim/pull/36221
Misc
博客 or 主页
修好了个人主页,去掉万年不更新的 Hugo 放了个简单的 Html 单文件,CSS 是从之前的 hugo-rss-only 仓库薅来的,设计风格来自 Nginx 默认页面。
计划在以后的 hash 按照月为单位更新日志,有其他长短文字按照内容分类,也放这里。
FetchSrc
做了一个用于下载、缓存、解压源码的 rust 小工具 fetchsrc,我的 Option 和错误处理 经验还是太匮乏了,这里写的很吃力(和 shell 差不多吃力了!)。 后面有空找个开源项目,跟踪一段时间学习错误处理。
LSP
clangd 原来默认也会搜索 build/compile_commands.json,再也不抱怨 LSP 不干活了,
之前全靠软链接和 .clangd 配置文件苟活。
SSH
终于(又一次)理解了端口转发的方向
ssh [-L|-R] [FROM_IP:]FROM_PORT:DESTINATION:DESTINATION_PORT [USER@]SSH_SERVER
对于 -L 本地端口转发来说,流量从本地端口进入,转发到 DESTINATION,所以是 LOCAL
1024
本来想搭配“麒麟”这首歌做个混剪小视频,但是和 xingji 交流后发现代码提交截图 不适合做素材,远远不如画画、徒步等,所以还是算了。
Donations
这次选择了 asciinema 项目,恰好新版本发布。
捐款上也有一些小想法:
- 参与代码贡献的项目不捐:出力不出钱,比如 Alpine Linux
- 账面还有很多钱的不捐:钱不能解决这种维护问题,比如 libgit2
- 没用过 or 不好用的不捐:特指希望倒闭的 matrix
没事可以去 https://opencollective.com/search 逛逛,想想以后自己的项目怎么搞钱
如果 11 月没想好给谁就去订阅一下 LWN,或者给服务器加点钱什么的。
2025-11 Draft
wless
本月主要关注client的resize和output的frame事件之间的关系,相关参考内容有:
- https://github.com/DreamMaoMao/mangowc/issues/293
- https://github.com/swaywm/sway/blob/055be4ec35eec4eaaf066a18ccbf5132ebed0694/sway/desktop/output.c#L374
- https://github.com/swaywm/sway/pull/2072
- https://wayland.app/protocols/xdg-shell#xdg_surface:request:ack_configure
问题的矛盾在于窗口的大小和窗口的位置改变不同步,如果我想让窗口进入全屏:
- WM立即将窗口的左上顶点移动对齐到左上角
- WM请求窗口改变大小,此时窗口占据了1/4屏幕(举例)
- 窗口响应WM,变为全屏,此时窗口全面覆盖
窗口的位置是WM没有延迟随意操作的,而窗口的大小需要客户端自己去响应,两者几乎不会在同一帧完成。 在这个时间裂缝里,窗口从1/4到全屏不可避免的闪了一次。 还没想好怎么处理,可能需要卡住显示器,或者等窗口响应的pre-commit里再去改变位置。
bushi
nom似乎可以对stream进行解析,之前可能没注意到?
从i5换到了n5105,耗时差不多是两倍:
git-log--stat 5m16s
git log --oneline --stat > /dev/null
git-fast-export 3m45s
git fast-export --no-data --fake-missing-tagger --signed-tags=strip --all > /dev/null
据研究packfile包含的压缩信息确实可用(好像废话,不然git是怎么把20G压缩到600M的)
git verfy-pack -v .git/objects/pack/pack-*.idx
但是没有稳定性保证,压缩的基准与提交顺序有关系但不保证, 高概率出现ABCD等多个tree都基于同一个base差值压缩的情况。 因此当前决定还是不做这个了, 我们还是愉快的攀附在git二进制文件上,对输出进行解析吧!
- 扫描所有refs,存储到数据库
- 从refs中的一个开始,遍历commit到存在停止,索引存数据库
- 基于git-log拿到changed-path,文件修改存数据库
- 重复2直到完成所有
- 从上到下构建祖先跳表,存另一个表
firefox & dde
火狐在dde上一直有缩放问题,主要症状是系统设置缩放(非1)后,火狐界面异常大。 在论坛上有很多反馈:
- Firefox火狐浏览器界面异常变大原因-论坛-深度科技
- V25和火狐浏览器有点冲突-论坛-深度科技
- deepin 25 火狐浏览器莫名的分辨率变大-论坛-深度科技
- 火狐浏览器firefox高分屏设置无效-论坛-深度科技
- 火狐浏览器问题-论坛-深度科技
- Firefox 窗体整体缩放变大问题-论坛-深度科技
- 火狐浏览器缩放-论坛-深度科技
- 火狐 mozilla 中文版标题栏宽大-论坛-深度科技
- 火狐浏览器分辨率问题。-论坛-深度科技
- 火狐浏览器缩放过大在浏览器中禁用注销后自动复原-论坛-深度科技
- 火狐浏览器的显示比例有问题-论坛-深度科技
- 火狐浏览器界面放大-论坛-深度科技
- 火狐的界面变得好大,怎么办-论坛-深度科技 2022-08-12
从症状上看,缩放不正常时,打开 about:config 配置页面,layout.css.devPixelsPerPx
的默认值 -1 被修改为了当前系统缩放值,如果恢复默认就缩放表现正常。
重启火狐,这个值又被改了,所以肯定是系统干的。 在GitHub搜索发现是dde-daemon仓库的逻辑。关掉相关服务,一切正常:
systemctl --user stop org.dde.session.Daemon1.service
从代码看,dde-daemon扫描用户的 .mozilla/firefox/*/prefs.js 文件并设置上述项,
删掉这块的逻辑就好了,测试下来确是如此。
这部分的代码是从startdde搬过来的,而在startdde项目当年的提交是在2017年:
xsettings: Add dpi supported for firefox · linuxdeepin/startdde@2636914
https://github.com/linuxdeepin/startdde/commit/263691490fb4e1ce36859b606361c1b718bfef30
回到论坛,用户有提出一个规避方案,设置browser.display.os-zoom-behavior的值为0。 这个选项的作用是设置火狐如何响应操作系统的缩放,在2022年7月26日发布的103版本中新增
Bug 1773633 - Allow configuring OS zoom behavior. r=tnikkel · mozilla-firefox/firefox@c7106fc
https://github.com/mozilla-firefox/firefox/commit/c7106fcb8111c4139094f444b78c5ed1632883ec
到这里就一目了然了,2022年103版本开始,火狐新增了一个默认开启的随系统缩放选项, 而我们的系统在2017年添加了手动修改缩放的逻辑,与此处的随系统缩放叠加,造成了界面异常大。 删掉2017年的缩放逻辑就好了。
TODO poweron no sound & dde
- https://bbs.deepin.org/post/293046
- https://bbs.deepin.org/post/292279
- https://bbs.deepin.org/post/292101
- https://bbs.deepin.org/post/292006
论坛有用户说开机重启后音量很小,我自己使用两个sink都没复现。 目前希望用户多给一点日志,包括关机前后的音量:
pactl get-sink-volume @DEFAULT_SINK@
pactl list sinks
有点怀疑是WirePlumber的音量保存与DDE内部的逻辑冲突,因为他们的音量计算方法不同, 我们系统上的立方音量改为了1.8
import math
# percent to dB volume
20 * math.log10(X ** 3)
# dB to percent volume
(10 ** (-Y / 20)) ** (1/3)
alpine
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/92432
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/92719
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/92722
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/92723
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/92730
- https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/92781
- https://gitlab.alpinelinux.org/alpine/alpine-conf/-/merge_requests/272
双十一购入的电子产品是32G的U盘,在Windows里成功装上了「Linux To Go」
- 下载官网的ISO镜像,标准版就行,虚拟机里网络一般都很好
- 创建vbox虚拟机并挂载盘片,默认512M内存,不需要本地硬盘
- 虚拟机以USB3.0格式挂载U盘设备,
lsblk应该能看到设备是/dev/sdX - 设置环境变量
USE_EFI=1SWAP_SIZE=0并执行setup-alpine
虚拟机一般不开启UEFI所以需要手动指定一下脚本中的EFI不然实体机不识别。 SWAP对U盘要比较高,关掉提高寿命。其他的优化方法暂未发现后续再看。
misc
alsa-lib
alsa居然合并了,上周本来想关掉这个PR来着。 原本的逻辑是先判断大小,再进行线性缩放,可能因为整数除法掉出最开始的判断范围, 所以我改成了先计算最后判断。
snd_tlv_convert_to_dB: Fix mute handling forMINMAX_MUTEtype by qaqland · Pull Request #478 · alsa-project/alsa-lib
aosc dde port
A: 我觉得这个事情既然不是一锤子买卖,就真得有人持续做
B: 是的,所以现阶段只能drop,不然搞个人临时处理两天意义也不大,不能持续搞的话对大家来讲都是个负面的
后续如果有人做aosc的port,也许能从这里再捡回来
deepin Desktop Environment: drop, orphaned by MingcongBai · Pull Request #13548 · AOSC-Dev/aosc-os-abbs
donations
一开始希望去OpenBSD,但是只有paypal捐款途径,我的国区账号不支持,所以换了家
Alpine Linux 软件包仓库一致性检查
2025 年 6 月 6 日
问题描述
镜像中共有两种文件,APKINDEX 索引文件和具体的 pkgname-pkgver-rpkgrel.apk 安装包。当用户执行安装软件操作时,会进行以下过程:
- 下载最新的 APKINDEX 到本地
- 解析索引,拿到对应的具体包名
- 下载对应的软件包文件
- 执行安装、脚本等
如果索引文件的版本与具体软件包不匹配,就会造成安装时找不到软件包,报错信息等同于软件名输入错误,示例如下:
$ doas apk add cage
ERROR: unable to select packages:
cage (no such package):
required by: world[cage]
当前进展
- 联系上游制作基于本地的一致性检查工具
- 本地测试
- 联系镜像站测试 TODO
- 发现问题后如何解决?
使用方法
Go 项目很好编译,调用参数为 APKINDEX.tar.gz 所在的目录路径:
$ ./repo-tools/repo-tools local check test-repo/
# 如果缺失
Package zfs-scripts-2.3.2-r0.apk mentioned in the index not found
# 如果成功无显示 TODO 可能要进程返回值设置为 1
个人猜想
可以做成两步同步:先单独拉索引文件,生成 rsync 同步 apk 文件列表,再用这个列表做同步,最后检查没问题时,使用新索引文件覆盖掉旧的。
引用网址
- https://gitlab.alpinelinux.org/alpine/infra/repo-tools/-/merge_requests/1
- https://salsa.debian.org/mirror-team/archvsync/
TinyWL 源码学习记录
2025 年 5 月 22 日 后面补充上了一点 dwl 的东西
Why are there "key" and "modifiers" two events at the same time?
根据协议,key 表示物理按键序列,而 modifiers 用来同步当前修饰键的逻辑状态
- 客户端 enter 之后会收到一次 modifiers 同步
- 修饰键 key 触发后也会收到 modifiers 状态激活
在 wlroots/types/seat/wlr_seat_keyboard.c 中可以看到调用顺序:
wlr_seat_keyboard_notify_enterwlr_seat_keyboard_enterwlr_seat_keyboard_send_modifiers
第二种情况暂未发现 // TODO
对窗口的移动
客户端主动发出请求 xdg_toplevel::move,服务端在 xdg_toplevel_request_move 中处理
server->grabbed_toplevel = toplevel;
server->cursor_mode = TINYWL_CURSOR_MOVE;
server->grab_x = server->cursor->x - toplevel->scene_tree->node.x;
server->grab_y = server->cursor->y - toplevel->scene_tree->node.y;
处理该请求主要为记录状态,包括希望移动的窗口、触发时的鼠标位置等。后续具体移动逻辑由服务端在鼠标事件中负责:
*toplevel = server->grabbed_toplevel;
wlr_scene_node_set_position(
&toplevel->scene_tree->node,
server->cursor->x - server->grab_x,
server->cursor->y - server->grab_y
);
使用“鼠标的实时位置”与请求触发时“窗口顶点与鼠标的相对距离”设置移动过程与最终的窗口位置。
对窗口的尺寸调整
与移动相似,但窗口需要更多调整,涉及到窗口的大小、位置改变。
- 记录光标位置与窗口边缘坐标
- 注意窗口最小尺寸(当调整左上边框时)
- 计算得到新窗口大小并设置
- 计算得到新窗口坐标并设置
对于坐标来说,wlr_xdg_toplevel 的坐标不等于 wlr_surface 的坐标,因此需要考虑两者之间的相对值。
键盘状态-焦点 surface
struct wlr_surface* previous_surface = seat->keyboard_state.focused_surface;
// tinywl.c @focuse_toplevel
在 wlroots 的 seat 中这个状态鼠标键盘各有一个:
struct wlr_surface *pointer_focus = wlr_seat->pointer_state.focused_surface;
if (pointer_focus != NULL &&
wl_resource_get_client(pointer_focus->resource) == client) {
wlr_seat->pointer_state.focused_client = seat_client;
}
struct wlr_surface *keyboard_focus = wlr_seat->keyboard_state.focused_surface;
if (keyboard_focus != NULL &&
wl_resource_get_client(keyboard_focus->resource) == client) {
wlr_seat->keyboard_state.focused_client = seat_client;
}
// types/seat/wlr_seat.c @static struct wlr_seat_client *seat_client_create()
当 wlr_seat 没有资源时,持有的焦点也要销毁(不过一般一个 WM 只有一个实例)
if ((capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) {
if (focused_client != NULL && focused_surface != NULL) {
seat_client_send_pointer_leave_raw(focused_client, focused_surface);
}
}
// types/seat/wlr_seat.c @void wlr_seat_set_capabilities()
所以这个变量可以为空,仅表示当前 seat 相关的焦点状态。常规修改发生在一些 enter 事件上:
wlr_seat_keyboard_enterwlr_seat_pointer_enter
不过注释说更应该使用 wlr_seat_*_notify_enter,内部会自行调整。
focus_toplevel
主要在以下三个位置调用:
handle_keybinding做窗口轮换时重新获得焦点server_cursor_button鼠标点击事件xdg_toplevel_map新窗口生成时拿到焦点
对于这三种情况基本可以满足 focuse_output 的等效替换,不过要考虑不可最大化的窗口处理
// TODO
wlr_scene_tree 的创建
在 dwl.c 的 mapnotify 函数中,有以下创建:
c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
c->scene_surface = c->type == XDGShell
? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg)
: wlr_scene_subsurface_tree_create(c->scene, client_surface(c));
c->scene->node.data = c->scene_surface->node.data = c;
这里的 scene scene_tree 类型均为 wlr_scene_tree 指针。对比两种函数:
/**
* Add a node displaying an xdg_surface and all of its sub-surfaces to the
* scene-graph.
*
* The origin of the returned scene-graph node will match the top-left corner
* of the xdg_surface window geometry.
*/
struct wlr_scene_tree *wlr_scene_xdg_surface_create(
struct wlr_scene_tree *parent, struct wlr_xdg_surface *xdg_surface);
/**
* Add a node displaying a surface and all of its sub-surfaces to the
* scene-graph.
*/
struct wlr_scene_tree *wlr_scene_subsurface_tree_create(
struct wlr_scene_tree *parent, struct wlr_surface *surface);
// 没有太大差别,XDG 和 XWAYLAND 不适配
进程与环境变量
2025年5月18日 在想办法用环境变量做程序的配置系统
数据结构
理论上讲应该采用 HashMap 的数据格式保存,以实现 O(1) 的访问速度。
#include <unistd.h>
char **__environ = 0;
weak_alias(__environ, ___environ);
weak_alias(__environ, _environ);
weak_alias(__environ, environ);
// https://git.musl-libc.org/cgit/musl/tree/src/env/__environ.c
但实际使用形如 KEY=VALUE 的字符串数组。
系统调用
对环境变量的读和写均发生在用户态:
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
char *getenv(const char *name)
{
size_t l = __strchrnul(name, '=') - name;
if (l && !name[l] && __environ)
for (char **e = __environ; *e; e++)
if (!strncmp(name, *e, l) && l[*e] == '=')
return *e + l+1;
return 0;
}
// https://git.musl-libc.org/cgit/musl/tree/src/env/getenv.c
仅初始化与系统调用有关:
#include <unistd.h>
#include "syscall.h"
int execve(const char *path, char *const argv[], char *const envp[])
{
/* do we need to use environ if envp is null? */
return syscall(SYS_execve, path, argv, envp);
}
// https://git.musl-libc.org/cgit/musl/tree/src/process/execve.c
Bash
在 Bash 中很坏,“环境变量”只是普通变量的一个属性,一般用 export 关键字标识
#define att_exported 0x0000001 /* export to environment */
...
#define att_local 0x0000020 /* variable is local to a function */
// bash/variables.h
/* An array which is passed to commands as their environment. It is
manufactured from the union of the initial environment and the
shell variables that are marked for export. */
char **export_env = (char **)NULL;
// bash/variables.c
所以会出现以下情况,环境变量随变量修改而改变:
export foo=bar
foo=barbar
sh -c 'echo $foo'
# output: barbar
其它对环境变量的构建和修改与普通函数无异
static inline char *
mk_env_string (name, value, attributes)
const char *name, *value;
int attributes;
{
...
{
p = (char *)xmalloc (2 + name_len + value_len);
memcpy (p, name, name_len);
q = p + name_len;
}
q[0] = '=';
memcpy (q + 1, value, value_len + 1);
}
// bash/variables.c
int
shell_execve (command, args, env)
char *command;
char **args, **env;
{
...
execve (command, args, env);
}
// bash/execute_cmd.c
JuDou 句读
2025 年 5 月 14 日 记录一下项目想法
概述
JuDou 是一个基于 LSP 的代码审阅辅助工具,通过在本地建立预缓存,实现在网页端的语义高亮和引用跳转。
- 服务端:资源消耗极低
- 客户端:无需环境配置
- 流水线:自动集成构建
名称
“常买旧书的人,有时会遇到一部书,开首加过句读,夹些破句,中途却停了笔:他点不下去了。”
技术栈
- Golang
- Vue
- Json-RPC 2.0
- Json
盈利
前期贴皮广告,中期 SAAS,后期私有化部署。
直接竞争对手为 GitHub 和 Sourcegraph,潜在包括各类 LLM 分析工具。
参考
- https://github.com/netmute/ctags-lsp
- https://github.com/sourcegraph/jsonrpc2
- https://microsoft.github.io/language-server-protocol/
Git 祖先跳表
2025年5月4日 介绍一下 Bushi 所使用的祖先跳表
目标
快速判断 Commit A 是否是 Commit B 的祖先
- git-ref-contains-commit
- git-log-path-filter
限制
本项目仅考虑提交的第一个 Parent Commit
原理
判断
验证
选择 Aports 仓库中 Commit-ID 分别为 100 和 20000 的两个提交,对应的 Commit-OID 如下
sqlite> SELECT * FROM commits WHERE commit_id = 100 OR commit_id = 20000;
commit_id commit_hash commit_mark parent_id depth repo_id
--------- ---------------------------------------- ----------- --------- ----- -------
100 44a369d15ac69464584099d339a0e1ec1ec7fa66 100 99 99 1
20000 89d3e577e9a10d8f75b87d5ec44d801efbb7f3c7 20000 19999 13920 1
查看网页确实在同一分支中,两者 depth 之差为 13821,转为二进制数得到 2 的 N 次幂
>>> bin(13821)
'0b11010111111101'
即为 13、12、10、8、7、6、5、4、3、2、0 这些序列,下面开始在祖先表中查找
sqlite> SELECT * FROM ancestors WHERE commit_id = 20000 AND level = 13;
commit_id level ancestor_id
--------- ----- -----------
20000 13 7618
sqlite> SELECT * FROM ancestors WHERE commit_id = 7618 AND level = 12;
commit_id level ancestor_id
--------- ----- -----------
7618 12 2133
sqlite> SELECT * FROM ancestors WHERE commit_id = 2133 AND level = 10;
commit_id level ancestor_id
--------- ----- -----------
2133 10 777
sqlite> SELECT * FROM ancestors WHERE commit_id = 777 AND level = 8;
commit_id level ancestor_id
--------- ----- -----------
777 8 372
sqlite> SELECT * FROM ancestors WHERE commit_id = 372 AND level = 7;
commit_id level ancestor_id
--------- ----- -----------
372 7 244
sqlite> SELECT * FROM ancestors WHERE commit_id = 244 AND level = 6;
commit_id level ancestor_id
--------- ----- -----------
244 6 161
sqlite> SELECT * FROM ancestors WHERE commit_id = 161 AND level = 5;
commit_id level ancestor_id
--------- ----- -----------
161 5 129
sqlite> SELECT * FROM ancestors WHERE commit_id = 129 AND level = 4;
commit_id level ancestor_id
--------- ----- -----------
129 4 113
sqlite> SELECT * FROM ancestors WHERE commit_id = 113 AND level = 3;
commit_id level ancestor_id
--------- ----- -----------
113 3 105
sqlite> SELECT * FROM ancestors WHERE commit_id = 105 AND level = 2;
commit_id level ancestor_id
--------- ----- -----------
105 2 101
sqlite> SELECT * FROM ancestors WHERE commit_id = 101 AND level = 0;
commit_id level ancestor_id
--------- ----- -----------
101 0 100
最终得到 Commit-ID 与目标 ID 相同,两者位于同一分支中。
GitWeb Collect
2025 年 4 月 12 日 cosplay 一下产品经理,对潜在精品进行汇总
汇总有遗漏,请额外查看:
- https://forge.runxiyu.org/stuff/-/repos/code-forge-list/
- https://en.wikipedia.org/wiki/Comparison_of_source-code-hosting_facilities
- https://en.wikipedia.org/wiki/Forge_(software)#Examples
Library
- libgit2 (c)
- git2-rs (c-rust)
- pygit2 (c-python)
- rugged (c-ruby)
- nodegit (c-javascript)
- go-git (golang)
- gitoxide (rust)
- ungit (javascript)
- Gitaly (git-golang)
严格来说 Gitaly 不算库,它是 GitLab 在替换掉 Rugged 之后的 RPC 远程调用,内部是对 Git 原版命令的封装
Simple
Perl
- GitWeb(Perl, Git builtin)
- https://github.com/yuki-kimoto/gitweblite
- https://github.com/cantonios/gitwebhub
- https://github.com/broquaint/Gitalist
Rust
- https://github.com/w4/rgit
- https://github.com/alexwennerberg/mygit
- https://github.com/qaqland/bushi Here is mine
Golang
- https://github.com/icyphox/legit
- https://github.com/emersion/matcha
- https://github.com/esote/gitweb
- SoftServe
C
- https://git.zx2c4.com/cgit/about
- https://github.com/oxalorg/stagit
- https://github.com/Ernest1338/GitWebSee
Java
Lua
Full
VSCode Remote SSH 连接 Alpine Linux
2025 年 4 月 11 日
远程连接时的步骤大致如下:
- 安装 remote-cli 到服务端
- remote-cli 下载并启动 vscode-server
- 本地客户端与 vscode-server 连接
基础
$ apk add bash curl git libstdc++ procps-ng
如果还是在启动时报错,可以查看 vscode 仓库对应脚本 resources/server/bin/helpers/check-requirements-linux.sh
插件
// TODO
References
Git 很慢
//
2025 年 1 月 23 日 抱怨一下 Git 的数据结构
2025 年 4 月 13 日 后续:ROGit 改名为 Bushi 卜筮,基本解决了这个问题,其余部分还在做
//
一年半之前,我有一个 ROGit 项目计划,旨在构建一个 cgit 同生态位的现代替代品
- ReadOnly:网页所有操作对 Git 仓库来说都是只读的,区别于 GitHub 或者 Gitea
- Rust:没有学习过 Rust 所以借此机会尝试一点新东西
- Online:以后可能除了 http 还会做 gemini 协议网站,所以很在线
得益于技术栈发展,cgit 所使用的 CGI 协议已相当少见。ROGit 相比 cgit 有以下几个目标:
- 现代 CSS 界面及交互设计
- 避免 Git 源码依赖
- 灵活的 API 接口及扩展
当前正在做后端的数据初始化部分,希望在启动时对所有的提交及其变动修改预缓存到 SQLite,但是遇到了严重的性能问题:解析 aports 仓库需要 3 个小时(对于初始化而言 3 分钟是可以接受的)。

注:使用 libgit2 作为解析库,火焰图时关闭缓存、关闭 Hash 验证,在开启自带缓存后性能略有提高
Git Object
理论上讲 Git 中有三个大类别的对象:Commit、Tree、Blob,具体到解析时还有 Commit 的同类 Tag 及 Note。这些对象以各自 Object 的 hash 作为索引,经 zlib 压缩后保存在 .git/objects 下的文件中,纯正文件系统驱动,并在需要时被 Git 解析。
Git 的数据结构为写多读少设计,因此其他程序应避免将 Git 作为数据库使用。具体保存格式有 Loose Objects 和 Packfiles 两类,参考阅读:
- https://git-scm.com/book/en/v2/Git-Internals-Packfiles
- https://github.blog/open-source/git/gits-database-internals-i-packed-object-store/
Git Commit
提交 Commit 指向自己的历史来源,有 0 个、1 个、2 个或者更多个 Parent Commit。不同的数量表示了这次提交的不同种类,是 root 节点还是 fast-forward 这种单线链表,或者是合并分支的 Merge 节点,超过 2 的情况不多见,是特殊的合并节点,比如内核的提交中出现的:
https://github.com/torvalds/linux/commit/9b25d604182169a08b206306b312d2df26b5f502
当然像提交信息、提交人、提交时间、以及 Committer 与 Author 之间的区别这里不再赘述。Commit 还保存了提交时的文件(树)快照,指向当前 Commit 相随的 Tree。
- Tree Hash
- Parent Commit Hash
- Author
- Committer
- Commit Message
这就是一个 Commit 对象信息的全部,当一个普通线性提交发生时,Git 会扫描当前 WorkTree 生成 TreeHash,底层保存的数据中并不关心此次提交的修改。
Git Log
Git 并没有在 Commit 中描述这次提交修改了哪些文件,所以若要知晓给定文件的最后修改日期,过程较为艰难:
- 获取 Commit 的 TreeHash 与 Parent Commit 的 TreeHash
- 在两个 Tree Object 中遍历,查找修改的文件
- 检查给定文件是否在本次 Diff 中修改
- 重复上述操作,直到两次提交的树之间存在期望差异
Git 内部在第二步和第三步之间应该有查询优化,仅对比给定的路径,但整体在没有对应数据结构的情况下进行类似 SQL 的结构化查询还是相当消耗性能,面对稍微大一点的仓库,时间来到秒级
$ /usr/bin/time git -c core.commitGraph=false log --oneline -n 10 community/xmake/
81380060446 community/xmake: upgrade to 2.9.9
7c21bea5624 */*: replace non-POSIX find -not option
a92fe0ba060 community/xmake: upgrade to 2.9.7
12374870a8f community/xmake: upgrade to 2.9.6
12c188ec967 community/xmake: upgrade to 2.9.5
0096aeef07c community/xmake: upgrade to 2.9.4
4b130eb7f5a community/xmake: upgrade to 2.9.3
d516ffbb476 community/xmake: upgrade to 2.9.2
d74b311e776 community/xmake: move from testing
real 0m 51.11s
user 0m 41.44s
sys 0m 9.56s
Git CommitGraph
Git 在 2.18 版本后引入了提交图的概念,使用单独的提交图文件对提交关系做缓存,所以在理想情况下,最快的查询条件为:
$ git gc --aggressive
$ git commit-graph write --changed-paths
- 全量 pack 保存,避免 Git Object 保存在 loose 时反复文件 IO
- 开启提交图缓存,包括提交修改记录
$ /usr/bin/time git -c core.commitGraph=true log --oneline -n 10 community/xmake/
81380060446 community/xmake: upgrade to 2.9.9
7c21bea5624 */*: replace non-POSIX find -not option
a92fe0ba060 community/xmake: upgrade to 2.9.7
12374870a8f community/xmake: upgrade to 2.9.6
12c188ec967 community/xmake: upgrade to 2.9.5
0096aeef07c community/xmake: upgrade to 2.9.4
4b130eb7f5a community/xmake: upgrade to 2.9.3
d516ffbb476 community/xmake: upgrade to 2.9.2
d74b311e776 community/xmake: move from testing
real 0m 1.99s
user 0m 1.17s
sys 0m 0.81s
提升很明显(但是其它第三方 Git 实现都没类似功能)
Git Blame
Blame 是 Log 的细节版本,不仅找到相关的提交,而且按照行定位每行的最后修改提交,类似糖豆人的蜂巢关卡,直到完成对目标文件当前所有行的修改提交定位,终止遍历。
所以这里有个 ddos 的小技巧,请求一个不存在的文件,Git 不得不遍历所有提交定位最终修改(但是似乎大家普遍缓存了每个文件的最终修改提交,所以可能没用)
References
- https://github.com/libgit2/libgit2-backends
- https://github.com/go-git/go-git/issues/811
- https://github.com/rust-lang/git2-rs/issues/222
- https://github.com/libgit2/libgit2/issues/3027
- https://github.com/eafcc/eafcc-core/blob/master/src/storage_backends/git/p2p/custom_backend_demo.rs
- https://git-scm.com/docs/git-commit-graph
- https://devblogs.microsoft.com/devops/supercharging-the-git-commit-graph/
- https://devblogs.microsoft.com/devops/updates-to-the-git-commit-graph-feature/
Alpine Linux 软件打包
2024 年 1 月 14 日
软件仓库的总部叫 aports,目前是官方 GitLab 实例上的一个超大 repository,所有与官方有关的软件包提交修改都基于仓库的流水线,有什么问题可以查阅 Wiki,也可以前往 oftc.net 上的 #alpine-devel 频道(IRC)寻求帮助。
$ git clone --depth=1 ...
据观察基于邮箱的协作好像不太能用,只能注册 GitLab 使用。找到官方的仓库 fork 到自己名下(就像 GitHub 那样),接下来的大致流程:
- 绑定密钥、设置用户名与用户邮箱
- 克隆自己的 fork 到本地,耐心等待
- 为自己需要修改的地方创建分支
- 修改(测试)并提交,推送新分支到仓库
- 在网页里自己的仓库页面提交合并请求
Alpine Linux 软件包的配置文件 APKBUILD 与 Arch Linux 的 PKGBUILD 非常相似,可以偷偷去他们的网站学习打包经验,但是不要抄袭——发行版不同、工具链不通用、分包策略也不同。同时也非常推荐 Fedora 家的包,他们相比 Arch 更加严格规范,易于参考。
网址
- 包管理器使用 https://wiki.alpinelinux.org/wiki/Alpine_Package_Keeper
- 新建软件包 https://wiki.alpinelinux.org/wiki/Creating_an_Alpine_package
- 配置文件说明 https://wiki.alpinelinux.org/wiki/APKBUILD_Reference
- 打包相关工具 https://wiki.alpinelinux.org/wiki/Abuild_and_Helpers
- 与 aports 仓库相关的 aports/README.md
- 代码与提交格式要求 CODINGSTYLE.md COMMITSTYLE.md
- Arch Linux 的包 https://archlinux.org/packages/
- Fedora 的包 http://src.fedoraproject.org/
FAQ
由于 musl 引起的编译错误如何解决?
应该都有人遇到过,aports 搜一搜自己也做个补丁
如何知道软件需要的依赖?
构建依赖:abuild rootbld 一个一个添加 dev 包测试
运行依赖:打包时会自动识别大部分动态库,手动加上额外的运行时依赖
编译需要的 gcc 是依赖吗?
build-base 已有,不需要写,apk info -r build-base 查看类似基础组件
网络问题拉不下来构建需要的源码
使用 http 代理,例如:https_proxy=http://localhost:7890/ abuild checksum
软件包依赖另外一个自己打的本地包
abuild rootbld 时添加 ~/packages/testing/ 到 ~/aports/testing/.rootbld-repositories
怎么知道安装包里包含什么文件
新版 tar 会自动识别压缩格式,执行tar -vtf file.apk获得文件列表
每次下载软件包耗时很久
① 开启 setup-apkcache ② $ mirror=http://mirrors.tuna.tsinghua.edu.cn/alpine abuild rootbld
最后如果有其他问题可以联系我
Apktools
2025 年 4 月 13 日 Alpine Linux 相关的基础使用,主要是系统包管理器
mirror
所有软件包的镜像信息保存在 /etc/apk/repositories,系统默认自带有 vi 编辑器,可以尝试修改。
有三种选择推荐:指定版本号、最新稳定版、滚动版,不推荐混合使用可能造成依赖冲突。
# 国内推荐使用:南阳理工学院开源软件镜像站
# https://mirror.nyist.edu.cn/
# 指定版本号
http://mirror.nyist.edu.cn/alpine/v3.21/main
http://mirror.nyist.edu.cn/alpine/v3.21/community
# 最新稳定版
http://mirror.nyist.edu.cn/alpine/latest-stable/main
http://mirror.nyist.edu.cn/alpine/latest-stable/community
# 滚动版
http://mirror.nyist.edu.cn/alpine/edge/main
http://mirror.nyist.edu.cn/alpine/edge/community
http://mirror.nyist.edu.cn/alpine/edge/testing
修改后刷新生效(不刷新也没事,默认间隔后使用会自动刷新)
$ apk update
$ apk upgrade
cli
已安装软件包信息保存在 /etc/apk/world 中,查看此文件了解系统中显式安装的软件包。
搜索比较弱鸡,推荐用谷歌、官方搜包站、和本人自建服务辅助进行
# 默认
$ apk search helix
# 搜索终端命令
$ apk search cmd:hx
# 搜动态链接库
$ apk search so:libgit2.so
安装和卸载软件包
$ apk add xxx
$ apk del xxx
查看此系统中哪些包依赖 xxx
$ apk info -r xxx
查看已安装的给定包 xxx 内部包含哪些文件
$ apk info -L xxx
example
docker
world
/etc/apk/world
$ apk fix
Setup Aports
2025年4月30日 设置 Aports 本地环境
Abuild
配置本地 Abuild,遵照官方 Wiki
apk add abuild abuild-rootbld alpine-sdk
addgroup xxx abuild
abuild-keygen -a -i
在 ~/.abuild/abuild.conf 可以配置部分设置选项,如颜色、镜像等
Ssh
官方的 GitLab 注册并配置 Ssh 免密,与常见操作相同
cd .ssh
ssh-keygen # 生成 key
cat xxx.pub # 复制到网页里
vim config
ssh -T [email protected]
Git
推荐 pull 时从镜像拉,因为官方的仓库又大又慢,以下是本人初始化过程
Clone
从镜像进行首次 Clone,默认只包括 master 分支
git config --global user.name qaqland
git config --global user.email "[email protected]"
git clone https://mirror.nyist.edu.cn/git/aports
cd aports/
git remote -v
Remote
将镜像上游改名到 mirror,设置官方上游到 origin
git remote add mirror https://mirror.nyist.edu.cn/git/aports
git remote set-url origin [email protected]:qaqland/aports.git
最终效果如下
$ git remote -v
mirror https://mirror.nyist.edu.cn/git/aports (fetch)
mirror https://mirror.nyist.edu.cn/git/aports (push)
origin [email protected]:qaqland/aports.git (fetch)
origin [email protected]:qaqland/aports.git (push)
Branch
更新 remote 并把 master 绑定到 mirror,因为 master 只用来同步 Pull
git fetch origin
git fetch mirror
git branch --set-upstream-to=mirror/master
git branch -vv
每当自己有新修改时,推送自己的新分支到 origin
git checkout -b xxx
git commit
git push -u origin xxx
Abuild & Rust(Cargo)
rustup
setup mirror
keep offline
options="net" # cargo fetch
prepare() {
default_prepare
cargo fetch --target="$CTARGET" --locked
}
build() {
cargo auditable build --frozen --release
:
}
clap(completions)
subpackages="
$pkgname-bash-completion
$pkgname-fish-completion
$pkgname-zsh-completion
"
build() {
:
./target/release/mdbook completions bash > $pkgname.bash
./target/release/mdbook completions fish > $pkgname.fish
./target/release/mdbook completions zsh > $pkgname.zsh
}
package() {
:
install -Dm644 $pkgname.bash "$pkgdir"/usr/share/bash-completion/completions/$pkgname
install -Dm644 $pkgname.fish "$pkgdir"/usr/share/fish/vendor_completions.d/$pkgname.fish
install -Dm644 $pkgname.zsh "$pkgdir"/usr/share/zsh/site-functions/_$pkgname
}
make patches
$ git init .
$ git add Cargo.toml Cargo.lock
$ git diff > ../../01-update-lock.patch
download patch from GitHub and GitLab
tests
options="!check" # upstream has no test
check() {
cargo test --frozen
}
$ cargo test -- --skip xxx
patches
use system library
- env
- config.toml
- patch