这学期开学也差不多一个月了,这一个月还真是过的毫无感觉,基本上一星期就一两节课,考完了科三犹豫了一下交管系统就炸了,现在还没报上科四。不过一个好事是上学期我写给校长信箱提的建议很多都生效了,比如教学楼前斑马线对面要踩了草坪才能走上人行道的弱智设计已经改了,比如臭了两三年的河道也终于开始治理了,虽然不知道我的邮件到底有没有起什么作用,但结果上来看终归是好的。

说起垃圾治理,就让人想到电脑和手机里一些难以忍受的垃圾软件(唐突一转),刚好最近做了一点微小的工作,就来简单讲讲我的经验好了。

Linux 版 telegram 的内存泄漏问题1

不可否认 telegram 确实是一个做的非常好的 im 软件了,光是官方支持全平台甚至包括 Linux 和 Windows Phone 就比微信 qq 之流不知道高到哪去,更不用说微信 qq (现在)自己不做 Linux 客户端还要各种打击第三方客户端了。但是 telegram 的 Linux 版却有着万年不修的内存泄漏问题(至于说这是正常行为不算是泄漏的只要对比一下在其他平台上的表现就能知道了),在 systemd 240 以后总算能靠 cgroup v2 来解决。

要用事先设定好的内存配额来运行一个程序,一个简单的方法是直接用 systemd-run, 例如可以:

# 用 -p 来将本来写在 [Unit] 里的属性用在这个临时起来的服务上
$ sudo systemd-run --scope -p MemoryMax=512M caddy

但是这里有一个问题是如果不启用 cgroup v2 的话,这个命令是不能加 --user 的,也就是不能以普通用户运行,我们当然也不可能用 root 用户来启动 telegram 了;而如果启用 cgroup v2 的话,就无法使用 docker/lxc 了。不过对桌面用来说这就没那么多所谓啦,没有 docker 也还有 systemd-nspawn,而且也没什么桌面服务非要 docker 不可的。

开启 cgroup v2 需要在开机的时候传递一个内核参数 systemd.unified_cgroup_hierarchy,以 systemd-boot 为例的话:

$ cat /boot/loader/entries/arch-cgroupv2.conf
title   Arch Linux (with cgroup v2 enabled)
linux   /vmlinuz-linux-zen
initrd  /initramfs-linux-zen.img
options root=PARTUUID=8dc5ce26-1566-4315-bcec-2135898641a9 rw quiet vga=current systemd.unified_cgroup_hierarchy=1

这样开机以后就可以写一个服务了:

# 用户服务要放在用户目录里,也就是 ~/.config/systemd/user/ 下
$ cat ~/.config/systemd/user/tg.service
[Unit]
Description=Telegram

[Service]
Type=simple
ExecStart=/usr/bin/telegram-desktop -- %u
KillMode=process
Restart=on-abnormal
RestartSec=2
#MemoryHigh=512M
MemoryMax=512M
MemorySwapMax=256M

[Install]
WantedBy=multi-user.target

# 然后启动 tg
$ systemctl --user start tg

其中 MemoryHigh 是较为温和的限制,不会限制死内存使用上限,而 MemoryMax 则会在程序使用超过上限以后按需杀掉进程,然后再由对应设置决定需不需要重启。但是这个过程里有一个我不懂的地方,MemoryMax 做的似乎不仅仅只是在超过内存上限后发送 SIGKILL,以 telegram 为例的话通过这种方式启动的 tg 会在接近 512M 上限的时候开始正常地回收内存,最高停留在 511.9M 绝不超过 512M,简直就像是求生欲一样,放着不管一段时间以后甚至会慢慢降到 500 以下。不管实际原理怎么样,作为用户我们只要真香就够了,对吧?

图形软件毕竟还是需要一个图形化启动的方式不能总往终端跑,所以我们还要修改一下 application 设置。以 KDE 为例的话,在开始菜单里右键 tg 的图标,选择编辑应用程序,然后修改启动命令为 systemctl --user start tg 就行了,或者直接去改 /home/kiri/.local/share/applications/telegramdesktop.desktop 文件。

Linux 版网易云音乐的缓存爆炸问题

前面骂了腾讯作为国内首屈二指的大厂在 Linux 市场上的不作为,这边倒是要夸一下猪场网易与 deepin 合作的网易云音乐客户端的。尽管 bug 一堆也不怎么更新,但网易云音乐好歹是有一个确实能看也能用的客户端的,相比之下,某些自称最好的网易云音乐客户端的开源拖拉机实际上却难堪一用。但就算我们忽略他的 dpi 问题、下载不了 .ncm 文件的问题、经常在网络良好的时候也放不出某些歌的问题,他完全不清理缓存的问题也是无法忍受的。在我的电脑上只要打开网易云音乐用一两天, .cache 目录就会唐突增大一两个 G,群里的 rebuild 甚至到了发现的时候一看已经有 29G 了,这可不能忍。这个问题的根本原因在于傻逼网易云设置里的缓存大小形同虚设,这玩意在线放甚至没放到的一些歌都会直接把整首歌缓存到 ~/.cache/netease-cloud-music/CachedSongs 里去然后就不管了,于是你的 .cache 目录占用就一发不可收拾,当你开启了无损试听的设置的话这个增长速度还会成倍的增长,本地直接多了一大坨的 .flac 文件。

解决的办法就是 disk quota 了,如果想直接在文件系统启用 quota 的话参考 ArchWiki 就可以了,但是我不想冒风险在根目录分区启用 quota(并没有把 /home 单独分出来),而且考虑到这货的大量频繁读写也并不想让它再摧残我的 nvme 了,刚好手上还有一块使用 zfs 的移动固态硬盘,就是你啦!下面用的是 zfs dataset quota + bindmount 的方案来进行垃圾治理。

# 我的移动硬盘挂载在 /mnt/darjeeling, 已有一个叫 darjeeling 的 pool, 现在新建一个叫 ncmcache 的 dataset
$ sudo zfs create darjeeling/ncmcache
# 启用 quota
$ sudo zfs set quota=512M darjeeling/ncmcache
# 把所有权交给普通用户
$ sudo chown -R kiri /mnt/darjeeling/ncmcache
# bind mount 到 ~/.cache/netease-cloud-music/CachedSongs
$ sudo mount --bind /mnt/darjeeling/ncmcache /home/kiri/.cache/netease-cloud-music/CachedSongs

成功 mount 了之后网易云就老实了,塞满了属于自己的 512M 以后就会自己在下一首歌播放的时候把前面的歌删掉,程序本体的表现也没什么不正常的。

然后要把这个 bind mount 设置成开机自动挂载,既然已经是底裤用户当然也还是把这个交给 systemd 了。一种办法是写一行 fstab,但这样有一个风险是开机启动读取 fstab 以后自动挂载的时候移动硬盘上的 zfs 还没起来,这样就会导致挂载失败;另一个办法是写一个 .mount 单元。

这里有一个非常大的槽不吐不行,systemd-mount 的单元名必须与挂载点的绝对路径相对应,比如 /home/kiri/test 需要对应为 home-kiri-test.mount,连在 [Install] 里写 Alias 都不行。但我们现在的挂载点可是 /home/kiri/.cache/netease-cloud-music/CachedSongs 啊?里面本身就带 '-',还有个 '.', 这玩意要怎么写?不幸中的万幸是 systemd 专门提供了一个 systemd-escape 工具 0w0.

$ systemd-escape -p --suffix=mount /home/kiri/.cache/netease-cloud-music/CachedSongs
home-kiri-.cache-netease\x2dcloud\x2dmusic-CachedSongs.mount
# 然后创建 home-kiri-.cache-netease\x2dcloud\x2dmusic-CachedSongs.mount,记得要用引号裹起来不然会先被 shell 转义一次!
$ sudo vim "/etc/systemd/system/home-kiri-.cache-netease\x2dcloud\x2dmusic-CachedSongs.mount"
[Mount]
Description="傻逼网易云治理工程"
What=/mnt/darjeeling/ncmcache
Where=/home/kiri/.cache/netease-cloud-music/CachedSongs
Type=none
Options=bind

[Unit]
DefaultDependencies=no
Conflicts=umount.target
Before=local-fs.target umount.target
After=zfs-mount.service
Requires=zfs-mount.service
ConditionPathIsDirectory=/mnt/darjeeling/ncmcache

[Install]
WantedBy=local-fs.target


# 然后 enable
$ sudo systemctl enable --now "home-kiri-.cache-netease\x2dcloud\x2dmusic-CachedSongs.mount"

spotify 也有类似的问题(他压根就没有设置缓存大小的选项),用一样的方法炮制就行了。

上古/不清真软件的编译和使用

本学期要用到一个非常古老的软件,需要的依赖一大半在 arch 这种滚动系统上压根找不着了,aur 里也没有。一开始挣扎了一下想自己写一下 pkgbuild,想了一想吔屎啦麻烦死了。这个软件更可怕的是直接搜索到的中文教程的安装方法全都乱七八糟,老师发在群里的教程甚至有先自己编译一个 gcc4 然后用 gcc4 把所有依赖挨个编译回来这种 ??? 级别的窒息操作。好在还是能搜到非常正常的英文教程,用的是 debian8 和包管理安装依赖来编译(也提到了 debian9),所以直接 debootstrap 一个干净的 debian8 来用就行了。

# 我把 debootstrap 出来 rootfs 放在 ~/machines/
$ cd ~/machines/
# --include 表示要直接额外装上的包,如果是 debian9+ 的话建议加上 systemd-container,但是 debian8 这包在 backports 源里,先不管他
$ sudo debootstrap --include=build-essential jessie jessie http://mirrors.ustc.edu.cn/debian/
# chroot 或者 systemd-nspawn
$ chroot jessie
# 然后 apt 装包编译,该干啥干啥

整个过程不过十几分钟就编译完了,这样等这门课完了直接删掉整个 rootfs 也不痛不痒的,而为辣鸡教程所苦的第一次接触 Linux 的我的同学们浪费了大量生命在这破事上面。另外 chroot/systemd-nspawn 也可以用来运行 GUI 程序,只需要 bindmount 一下 /tmp/.X11-unix 共用 socket 就行了,但是我 debootstrap 出来的 deepin 却不知道为什么不能用 deepin-wine 来用 qq 之类的 pmp。

Android 软件的权限、存储等一大坨问题

在有点了解的 Android 用户中,国产软件=流氓软件几乎已经是共识,随便什么破软件都要电话相册存储相机权限一个不落全要才能打开不然就闪退,要了存储权限就开始在文件系统里到处拉屎,p 大点功能也要带上一大堆的国产 sdk 准备好跟其他软件互相唤醒,真可谓是人间之屑。这些问题也并不是换个系统就能解决的,ios 上权限一样是一个值得商榷的问题,而文件管理?ios 有文件管理?总之对我来说, ios 并不是一个能满足日常需求的系统。但是用个手机就非要刷机 root 折腾来折腾去我也不觉得是什么好事,就安利一下自己在用的朋友写的 app 吧,也当是在打广告了:

App Ops(ROOT/ADB)

appops 是 Rikka 写的给应用返回一个空权限的管理软件,只要在 appops 这边点了拒绝就可以放心在目标应用点同意权限,可以治疗大部分的国产软件乱要权限的毛病,不过使用需要 ROOT 或者每次重启之后 ADB 来授予权限。比 appops 更加直接彻底的权限管理软件有 XPrivacyLua, 不过这玩意需要 xposed 框架才能用。

shelter

shelter 是彼得写的某炼妖壶的清真版竞品,从发布开始就开源了,同时在 F-Droid 上也能安装。shelter 是利用 Android 7.0 开始自带的 Work Profile 功能新建一个工作用户然后在里面安装需要隔离的应用,被隔离的应用就不能直接读到主用户的文件系统、相册、短信等,而且也能在不用的时候自动冻结,并且还不需要 ROOT/ADB,可以说很清真了。比起炼妖壶还可以顺利地在星星星系统上安装,虽然使用起来有一些 bug,不过瑕不掩瑜了。

Storage Redirect(ROOT)

存储重定向也是 Rikka 的作品了,用途是将那些在内置存储到处拉屎乱写一通的软件留下的脏东西用硬链接的方式重定向到他该去的位置,对于有精神洁癖的人可以说是救赎了。遗憾的是必须要 ROOT 了才能用,所以在我换了新手机以后也没能再用了。

冰箱(ROOT/ADB)

这个的作者我就不认识了,不过软件本身还是很好用,功能就是顾名思义的把不用的流氓软件冻结起来不让他们占用后台,这个方法简单粗暴,在省电等方面效果显著,不过当然会导致你收不到被冻结的 qq 微信等的推送消息,有利有弊吧。冰箱可以跟 Rikka 全家桶一样通过 Shizuku Manager 来授权,也就是每次重启只要对 Shizuku Manager 用 adb 就行了,不用一个一个来。

Windows 上的流氓软件

最好的垃圾软件治理办法就是不装垃圾软件,我的 Windows 上除了几个 UWP 应用以外已经完全没有国产屑软件了。

暂时也就想到这么多,有什么要补充的以后再写啦。