5
11
2020
14

Linux 的进程优先级与 nice 值

2024年04月20日更新:autogroup 仅对位于根 cgroup 中的进程有效。而在现在使用 systemd 的系统中,默认会使用 cgroup v2,没有进程位于根 cgroup,autogroup 也就不起效了。如果没有开启 cgroup 的 cpu 控制器的话,进程的 nice 值将起效。


假设有一个程序叫 use-cpu,它运行的时候会一直消耗一个 CPU 核心。试问,如果我开两个终端窗口,分别执行以下两个进程,其 CPU 会如何分配?

$ taskset 2 ./use-cpu
$ taskset 2 nice -n 10 ./use-cpu

两个进程都在1号CPU上运行,意味着它们必定争抢时间片。第二个进程使用 nice 命令设置其 nice 为 10,那么,是不是第二个进程会占用比较少的 CPU 呢?

很合情合理的推理,然而答案是否定的。

呃,也不一定。cat /proc/sys/kernel/sched_autogroup_enabled 看一下,这个开了的话,CPU 调度会多一个层级。默认是开的,所以这两个进程会均分 CPU 时间。

首先说好了呀,这里只讨论普通进程(SCHED_OTHER)的调度。实时进程啥的不考虑。当然啦,CPU 分配只发生在 R(Runnable)状态的进程之间,暂时用不到 CPU 的进程不管。

最上面的层级是 cgroups 了。按照 cgroup 层级,每一层的子组或者进程间按权重分配。对于 cgroups v1,权重文件是 cpu.shares;cgroups v2 则是 cpu.weight。不管用哪个版本的 cgroups,systemd 默认都没有做特别的设置的。对于 v1,大家全在根组里;对于 v2,CPU 控制器都没有启用。如果你去设置 systemd 单元的 CPUWeight 属性(或者旧的 CPUShares 属性),那么 systemd 就会开始用 cgroups 来分配 CPU 了。一个意外的状况是,使用 cgroups v2 时,systemd-run 不会自动启用上层组的 CPU 控制器,以至于如果上层未手动启用的话,设置不起作用。在使用 cgroups v1 时,用 systemd 设置 CPUWeight 或者 CPUShares 也不太好用,因为它并不会自动把进程挪到相应的层级里去……

其次就是这个默认会开的 autogroup。这个特性是为了提升交互式的桌面系统的性能而引入的,会把同一 session 的进程放一个组里。每个 autogroup 作为一个整体进行调度,每个 autogroup 也有个 nice 值,在 /proc/PID/autogroup 里可以看到,也可以 echo 一个想要的 nice 值进去。至于这个 session,不是 systemd 的那个 session,而是传统 UNIX 的那个 session,也是 setsid(2) 的那个 session。我一直以为它没多大作用,没想到在这里用上了。基本上,同一群进程会在同一个 session 里(比如一群火狐进程,或者一群 make 进程),同一个终端里跑的进程也都在一个 session 里(除非你 setsid 了)。

最后才是轮到进程。其实准确地讲是线程。同一个 autogroup 里的时间片如何分配,就看里边这些线程的 nice 值的大小了。所以其实,我系统里的那些高 nice 值的进程,由于 autogroup 的存在而它们又没有去设置 autogroup 的 nice 值,其实调度起来没什么差别的。

参考资料

  • man 7 sched
  • man 7 cgroups
Category: Linux | Tags: linux systemd cgroups
5
7
2020
10

Intel GVT-g 初体验

准备 GVT-g

把 kvmgt vfio-iommu-type1 vfio-mdev 这仨加到 /etc/mkinitcpio.confMODULES 数组里去。mkinitcpio -P 重新生成一下 initramfs。

添加内核参数 i915.enable_gvt=1。比如是 grub 引导就去改 /etc/default/grub 里的 GRUB_CMDLINE_LINUX 变量,然后 grub-mkconfig ...

去把 /etc/systemd/system.conf 里的 DefaultLimitMEMLOCK 给改了。比如 DefaultLimitMEMLOCK=65536:1073741824

重启。

这个时候应该已经有 /sys/devices/pciXXXX:XX/XXXX:XXXX.X/mdev_supported_types 这个目录了。里边有好几个选项呢。选择一下合适的(查看 description 文件),然后往里边的 create 文件里写一个 UUID 就创建了。

启动 KVM 虚拟机

呃,如果你还没有磁盘镜像就自己 qemu-img 创建一个,然后装机。如果你有别的虚拟机的,也可以用 qemu-img 去转格式。

另外准备一下网络。我早就有个网桥了,所以直接用它了。在 /etc/qemu/bridge.conf 里写一句 allow br0 不然不给用的,毕竟我是普通用户权限而网络接口是要 root 权限操作的,得明确允许一下。

我尽可能地使用了 virtio,据说性能好(VirtualBox 也支持一部分了呢)。如果用已有的虚拟机系统但以前没用过 virtio 的话,记得用 fallback 那个 initramfs 启动,然后进系统之后重新生成一个。

我给分配了四个逻辑 CPU 核,4G 内存。VGA 要关掉,不然两个显卡用起来麻烦。为了避免部分内容显示到别处去(如果关了 VGA 的话就看不到,否则能在默认的那个上看到),要加上 ramfb=on,driver=vfio-pci-nohotplug 选项。

声音当然是要的。添加个 PulseAudio 后端,一张 HDA 声卡。我不懂声卡型号所以找了个顺眼的,能用就好。

合起来是这样子的(那两个省略号,一个是磁盘镜像路径,一个是创建 vGPU 用的 UUID):

#!/bin/bash -e

ulimit -l 1024000

exec qemu-system-x86_64 -enable-kvm \
       -name "ArchKDE" \
       -cpu host -smp 4 \
       -m 4G \
       -drive file=/.../ArchLinuxKDE.qcow2,if=virtio \
       -netdev bridge,id=eth0,br=br0 \
       -device virtio-net,netdev=eth0 \
       -device vfio-pci,sysfsdev=/sys/bus/mdev/devices/...,display=on,x-igd-opregion=on,ramfb=on,driver=vfio-pci-nohotplug \
       -vga none \
       -display gtk,gl=on \
       -audiodev pa,id=pa0,server=/run/user/$UID/pulse/native -device intel-hda -device hda-output,audiodev=pa0 \
       "$@"

如果你使用 GVT-g 显卡的时候整个系统都卡卡卡的话,去看一下宿主的内核日志,是不是有 vfio_pin_page_external: Task qemu-system-x86 (257364) RLIMIT_MEMLOCK (104857600) exceeded 这样的提示,然后去把 RLIMIT_MEMLOCK 给调大,大到它不再报这个错为止。我最后给了1000M才终于不报错地把 KDE 给跑起来了(默认是64K)。

当然如果你没有 GVT-g 支持的话,去掉那行配置,然后 -vga virtio 也能用。

参考链接

Category: Linux | Tags: 虚拟机 linux kvm
11
26
2019
11

Python 3.8 升级记录

Python 3.8 发布有好多天了,Arch Linux 也早就重新打包了一千多个包(感谢辛勤的肥猫猫),隔天就从 [staging] 进入 [testing] 了,四天之后进入正式仓库([extra] 和 [community])。

Python 3.8 进入官方仓库的次日,我本地进行了更新。之所以要等一天,自然是等 [archlinuxcn] 的更新啦。然后那些需要人工干预而又暂时没人理的我本地重新打包了。使用 pacman -Qo /usr/lib/python3.7/site-packages 查询尚未更新的软件包,然后对着对应的 PKGBUILD 一顿改(基本上也就是 pkgrel 加 0.1 而已),makepkg -si 就好了。

但是这样还没完事哦。

sudo updatedb 更新一下 mlocate 的数据库。然后 locate -be python3.7 | grep -v /var/lib/lxc 找到一些残留的文件,主要是 ~/.local/lib 下的,以及散落在管理之外的 venv 里的。~/.local/lib 下的都是我自己的项目,删掉然后重新去项目里 python setup.py develop --user 就好了。venv 的话,直接删掉吧……

然后是 locate -be python-37 | grep -v /var/lib/lxc | grep -v /usr/lib/python3.7/site-packages。这个是为了查 Python 3.7 的 pyc 文件,所以这次也排除了 Python 3.7 的 site-packages,避免尚未更新的 Python 包的干扰(有些暂时用不到的包我就懒得自己 makepkg 了),等更新完之后整个目录删掉。有些软件包(比如 gdb-common)没有使用标准的 Python 安装流程(比如因为并不是标准的 Python 库),打包者(比如著名的 Allan McRae)没有或者拒绝在打包时编译 pyc 文件,造成 Python 自行创建不被管理的 pyc 文件,软件包卸载或者 Python 升级后就残留下来了。

确认没有问题之后(比如有些软件可能自带了个旧版本的 Python,或者有些并不是 pyc 的文件也包含这个字符串),执行 locate -be python-37 | grep -v /var/lib/lxc | grep -v /usr/lib/python3.7/site-packages | sudo xargs rm -v 删除这些文件。当然如果有需要保留的文件自行从文件列表中删掉先。

pyc 清理之后,接下来清理一下空的 __pycache__ 目录啦。locate -be __pycache__ | sudo xargs rmdir -v 2>/dev/null 就可以了,非空目录不会被删掉的。

哦对了,我现在在用 mypy 了,所以还要 locate -we .mypy_cache/3.7 一下。

我之所以现在记录这事儿,「现在」的原因是,我要在另一个系统上再测试一遍再发布出来,「记录」的原因是,下一次我就不用想要执行哪些命令了。

Category: Linux | Tags: linux python Arch Linux
9
3
2019
20

NVIDIA PRIME 配置笔记

这是由 NVIDIA 官方提供的新的双显卡配置方案(官方文档),需要最新的驱动及 Xorg 支持。其中 nvidia 驱动已经位于 Arch Linux 官方仓库中(版本号 435.21)。相关 Xorg 补丁还在 git 上,并没有新版本放出,所以需要自行编译包含补丁的版本。这里有现成的 PKGBUILD。我打包好的版本也提供下载

2019年11月25日更新:xorg-server 1.20.6 已经进入 Arch Linux 官方仓库,包含需要的补丁了。

2020年02月14日更新:现在也可以通过安装 nvidia-prime 直接配置好,就不用搞下边那些配置了。记得安装 nvidia(或者 nvidia-dkms 等)驱动包哦~

我的硬件是 ThinkPad T470p,Intel Corporation HD Graphics 630 核显和 GeForce 940MX 独显。核显的特点是:省电、支持视频编解码加速。独显的特点是:Minecraft 能开光影,FPS 也要高一点。用来跑火狐的话可以正确渲染 FishGL

配置方法如下。

首先把 bbswitch 啥的都卸载了吧。虽然注意点不卸载也没事,但是毕竟装着没意义还容易出问题。然后看看 /etc/modprobe.d 下有没有黑名单 nvidia 的驱动,给它取消了。我有一个 options nvidia_drm modeset=1 的配置,不知道有没有影响。

Xorg 这边要加一段配置。就保存在 /etc/X11/xorg.conf.d/nvidia.conf 好了。

Section "ServerLayout"
  Identifier "layout"
  Screen 0 "iGPU"
  Option "AllowNVIDIAGPUScreens"
EndSection

Section "Device"
  Identifier "iGPU"
  Driver "modesetting"
  BusID "PCI:0:2:0"
EndSection

Section "Screen"
  Identifier "iGPU"
  Device "iGPU"
EndSection

Section "Device"
  Identifier "dGPU"
  Driver "nvidia"
EndSection

那个 BusID 的值要自己看着 lspci | grep VGA 来改。

如果你之前是用N卡跑 Xorg 的,需要把 xrandr --setprovideroutputsource modesetting NVIDIA-0 之类的设置去掉。

然后重启系统就可以了。请做好通过 tty 或者 ssh 修复配置的准备。

默认情况下,Xorg 及其上的程序运行于i卡。使用 __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia 环境变量来指定使用N卡。为了方便起见,做一个别名好了:

alias nvrun="__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia"

然后就可以 nvrun minecraft-launcher 啦~

Minecraft BSL 光影效果

对了,要注意一下的是,这两个环境变量对视频硬解也是有效的。比如我如果给 mpv 使用这两个环境变量的话,mpv 就会黑屏(我的N卡不支持硬解……

Category: Linux | Tags: linux nvidia 显卡驱动
7
22
2019
6

fcitx 扩展:使用键盘粘贴选区(以及X选区原理科普)

之前的文章中介绍过,X Window 中有个很方便的名叫 PRIMARY 的「剪贴板」,选中即复制,中键即粘贴。

然而问题来了:我在输入的过程中需要之前选中的内容怎么办?又或者我的内容是通过 xsel、Vim 等程序以键盘的方式选中的,我还要继续打字,要怎么粘贴才比较流畅呢?

我之前的解决方案是 fcitx 自带的 fcitx-clipboard 扩展。启用之后可以按 Ctrl-; 来显示几条最近复制的内容。如果启用了的话,第一条、第三至最后,都来自常见的 CLIPBOARD 选区,而第二条(如果用了够久,第一条及本条有的话)是 PRIMARY 选区的内容。于是我只需要按 Ctrl-; 2 就可以粘贴了。

这么用了很久之后,我读到了一篇讲 X Window 选区是如何工作的文章。然后突然意识到一个问题:fcitx-clipboard 是什么时候请求选区内容的呢?

这里先科普一下好了。X Window 的每一个选区,是由某个窗口作为所有者持有的,并且在被请求时输出内容。「复制」的时候,内容并不会被存起来放在某处,而是窗口跟 X 服务器说,「我现在是这个选区的所有者了!」至于是哪个,取决于应用程序(及用户的操作)。通常用户明确的「复制」操作(Ctrl-C 快捷键、菜单项等)会使用 CLIPBOARD 选区,而只是选中内容的话,会使用 PRIMARY 选区。我还没有见到有程序使用别的选区的。

如果使用的是 PRIMARY 选区,这个时候,旧的所有者(如果存在的话)会失去选区。在 Vim / GVim 里,可视区域会由「Visual」高亮组变为「VIsualNOS」高亮组(在我的主题里就是变灰了),而多数终端,选中的区域会失去高亮。下一次,有程序想要「粘贴」PRIMARY 选区,就会跟 X 服务器讲,请把 PRIMARY 选区的内容,以某个指定的格式写到指定窗口的指定属性上。然后 X 服务器把请求传给选区所有者,选区所有者就去按要求写属性。当然选区所有者也可能会拒绝请求,比如它拥有的是文本而请求方想要图片。当然后来大家要粘贴的内容比较大了,于是就有了 INCR 机制来一点一点地传数据。

这样的流程,也可以解释,为什么我往 Telegram 里粘贴图片,GIMP 却莫名其妙地挂掉了……

所以啦,在一个程序里复制之后,退出那个程序,你就粘贴不出来东西啦。可这样岂不是很不方便?是啊,所以又有了剪贴板管理器。它们的工作原理我还没有研究,猜想是支持 SAVE_TARGETS 的话,就等着对方退出之前把内容传过来,不支持的话一复制就传过来。

fcitx-clipboard 是这么干的:选区一有变化,它就获取其纯文本格式的内容,符合一定条件的就存起来。所以,当我在 Vim 里用键盘不断进入可视模式选东西进行各种操作时,因为我设置了 clipboard=autoselect 选项,Vim 会不断地通告「我拥有 PRIMARY 选区啦!」「我这边的 PRIMARY 选区又更新啦!」结果就是,fcitx-clipboard 会不断地把我在 Vim 中选中的内容给拿过去。

就那么点数据,本地传来传去当然没啥问题。但是,当我通过 ssh 使用的时候,我发现我在 Vim 里每一次扩大可视区域都如此地艰难。不得已只好关了 autoselect 选项。当时我还以为就选点文本,怎么就这么慢呢,谁曾想到,每一次更新可视区域,fcitx-clipboard 都会把我选中的文本请求一份……

那么好吧,不用 fcitx-clipboard 了。于是问题又回到了原点:怎么通过键盘粘贴 PRIMARY 选区呢?用程序把鼠标移过去点中键是不行的,因为程序不会知道当前光标在哪里。通过 xdotool type 也行,但是这样一个个字地输入,而且还不仅仅是 ASCII,鬼晓得有多少程序跟 Minecraft 一样会处理不过来而丢字?而且,怎么判断当前是否是输入文本的状态也是个问题。所以我还是走输入法这条路了。

其实这事完成并不难,从肥猫的傲娇扩展开始改,照猫画虎地注册热键,然后请求选区,提交文本。可我遇到一个很奇怪的 bug:扩展加载了,但是热键不生效。为了调试这问题,我通过手机 termux ssh 连过来,tmux attach 上,然后用蓝牙键盘对着屏幕里那只由于 fcitx 被 gdb 停下来了因此从电脑收不到键盘事件的 tmux 调试好久,最终发现热键怎么没注册上,才找到配置文件里一处没有被更新到的 tsundere 字样……

这个扩展名叫 fcitx-paste-primary,源码放 GitHub 上了。Arch Linux 用户可以通过 AUR 或者 archlinuxcn 仓库安装 fcitx-paste-primary-git。

对了,差点忘了说,这个扩展「粘贴」的时候,只是把会被粘贴的文本提交给应用程序,程序并不会认为是真的粘贴,所以在一些需要区分的程序里会出现问题。比如 Vim 和 zsh,都会把来自 fcitx-paste-primary 的文本当作用户输入而非粘贴而可能造成问题。

Category: Linux | Tags: fcitx X Window X window
4
9
2019
4

T470p 使用N卡运行 Xorg

这么做的原因是:这样 minecraft 帧率高,不卡顿。

  • intel 显卡:帧率低,好像是20fps左右吧。开不了光影
  • optirun:坏了
  • primusrun:帧率高了一些,不多
  • nvidia-xrun:丝般顺滑,只是切换回我之前跑程序的 Xorg 时,发现我的 Awesome 已经没了。一开始是黑屏,经过配置之后倒是能得到 LightDM 的登录画图。另外 nvidia-xrun 无法卸载模块,因为被 Xorg 使用了,需要停止 lightdm。

那么,既然 nvidia-xrun 效率不错,我要是把整个桌面都搬上去呢?经过了一些折腾之后,取得了不错的结果。一个意料之外的好处是,播放视频、网页浏览器里滚动页面时常出现的画面撕裂好了~

当然这样做会费电,降低续航时间。不过既然是 T470p,一开始我就没打算整天带着它到处跑,所以无所谓啦。需要的时候再切回去好了。有个叫 optimus-manager 的软件,看介绍是帮助这么切换的。不过我对一切自动化程度太高的软件都心存疑虑,不确定它到底干了什么,会不会和我其他的配置相冲突。所以以后再看看啦。

最终的配置方案是这样的——

首先,把 bumblebeed.service 关掉并禁用。

然后,Xorg 配置一份,放 /etc/X11/xorg.conf.d/ 下就好。这份配置来自于惠狐的《Archlinux 下 Intel 和 NVIDIA 双显卡 de 折腾笔记》一文。

Section "OutputClass"
    Identifier "intel"
    MatchDriver "i915"
    Driver "modesetting"
EndSection

Section "OutputClass"
    Identifier "nvidia"
    MatchDriver "nvidia-drm"
    Driver "nvidia"
    Option "AllowEmptyInitialConfiguration"
    Option "PrimaryGPU" "yes"
    ModulePath "/usr/lib/nvidia/xorg"
    ModulePath "/usr/lib/xorg/modules"
EndSection

lightdm.conf 里在 [Seat:*] 里加一个 hook 配置,否则会黑屏的:

display-setup-script=/usr/local/bin/lightdm-setup

这个脚本内容如下:

#!/bin/bash -e

xrandr --setprovideroutputsource modesetting NVIDIA-0 || exit 0
xrandr --auto

写了一个 systemd service,用来启用 N 卡。因为默认它是关的。

[Unit]
Description=Switch On nvidia card
ConditionPathExists=/proc/acpi/bbswitch
Before=display-manager.service

[Service]
Type=oneshot
ExecStart=/bin/sh -c "echo ON > /proc/acpi/bbswitch"

[Install]
WantedBy=graphical.target

我之前在 ~/.xprofile 配置了视频的硬件加速,现在得删掉。GM108M [GeForce 940MX] 这个显卡的视频加速没法用的。

设置内核模块的选项 options nvidia_drm modeset=1,不然 xrandr --scale 时结果会不对。

暂时就这些了。


2019年07月20日更新:我又换回 Intel 显卡了。虽然这样性能差一点,滚动、视频时画面有点撕裂,外接屏幕中鼠标会闪,但是它稳定可靠啊!Nvidia 的驱动实在是崩得太闹心了(而且我那卡不支持视频硬解)。

2019年09月03日更新:我用上了 NVIDIA 新的 PRIME 方案,效果很好~

Category: Linux | Tags: linux 硬件 显卡驱动
4
3
2019
0

正确的隐藏挂载点的方法

脚本需要挂载文件系统,但是不希望外部看到。正确的做法是:

mount --make-rprivate /

然后该干嘛干嘛。当然如果你不知道在执行之前先调用 unshare 或者等价的系统调用,说明这篇文章不适合你阅读。

错误的做法是在挂载的时候加 --make-private 或者把 / --make-private。这个标志(MS_PRIVATE)的意思是挂载/卸载事件在这里停止传播,而不是这个挂载点的事件是否传播出去。至于为什么需要使用 --make-rprivate(增加了 MS_REC 标志),暂时我还不理解。

这个用法是从 unshare 工具的 strace 结果里挖掘出来的。因为我的目的跟 unshare -m 一样嘛,当然首先想到的是看看它是怎么干的了。你问我为什么不用 unshare -m?你自己写脚本的时候试试看啰?

Category: Linux | Tags: linux 文件系统
3
31
2019
8

迁移系统到 SSD

最近一段时间,不知道是磁盘、缓存相关算法的更新,还是我开的服务太多,又或者是新软件占用内存太高,我的系统越来越卡了,尤其是更新系统的时候(备份系统时也特别卡,然后我用限制内存占用的办法解决了)。我当然知道最主要的原因是因为机械硬盘的处理能力就那么多,于是经过一些了解和计划之后,还是决定换 SSD 了。

刚才查看了一下历史数据。从去年七八月份起,平均内存使用量从2G多升高到了3G多。大概是火狐更占内存了吧。我都尽量减少内容进程数量了……也可能是 PHP / MediaWiki 的锅,因为使用 SQLite 存储时,经常发生错误也是这段时间的事情。不过也可以理解为由于磁盘负载重导致的。算了不管了。

准备工作

当然首先要去买块 SSD 啦。我买的是 LITEON T11 Plus 512,512GB,800块。实际操作系统得到的空间是 477GiB,因为硬盘产业还在沿用1000进制的单位词头。它比我预期的要小不少呢,不过拿在手里感觉比一般同样大小的电路板要重。

拆开我的 T470p,把空闲接口旁边的螺丝下下来,然后 SSD 标签朝外插进去。我也不清楚这个接口叫什么。插进去之后它是翘起来的,难怪要用螺丝固定。然后用下下来的螺丝固定好,再把机器装好,就好了。启动系统,可以看到 /dev/nvme0n1 设备在了~GNOME 磁盘软件不能读取到 SMART 信息,用 smartctl -a /dev/nvme0n1 命令就好了。

设备没问题了,接下来当然是备份系统啦。

开始迁移

备份妥当之后,我就开始格式化 SSD。计划是 EFI 分区 512M,400G 给我的 Arch Linux,然后剩下 76G 左右的空间预留给我的 Win10。

然后这 400G,首先上一层 LUKS 加密,然后格式化为 btrfs 文件系统。其实我想要 btrfs 很久了,快照、去重、压缩都挺棒的。但是听说它的性能比较差,而我已经在受磁盘 I/O 能力不足的苦了,所以到现在有了 SSD,是时候换 btrfs 了!

其实之前 zfs(zfsonlinux)也是候选项,并且已经在工作本上使用过了。然而最近我的 zfs 备份两度出现问题(磁盘掉线之后 zfs 元数据损坏,导致一整个 zfs 文件系统一写就卡住;近期莫名其妙 rsync 跑着跑着就卡在那里不动了,磁盘也没有什么活动),再加上之前遇到的各种大小问题(ARC 被算进内存使用量中;挂载期间一旦磁盘离线就卡死;克隆出来的文件系统无法摆脱原文件系统;不支持 overlayfs;因为是树外模块所以需要专门准备的支持 zfs 的系统来执行安装),以及 TRIM 支持刚刚才加入,我已经停用 zfs 并将其排除考虑范围了。

然后就是规划子卷。参考了 openSUSE 的方案,最终决定分为这么几个子卷:/, /var/cache, /var/tmp, /var/log, /var/lib/lxc/lxc-debian/rootfs, /var/lib/lxc/lxc-centos6/rootfs, /home/lilydjwg, /home/lilydjwg/.cache。主要考虑的是快照。另外我给 /var/log/journal 和 /var/lib/postgres chattr +C 禁用了 CoW。这样也会禁用压缩,不过本来它们基本上就没什么可压缩的。需要排除的有:我的公开第三方源码和各类大文件用的 /ldata 还是放在机械硬盘上、/var/cache/pacman/pkg 缓存不要、/var/lib/pacman.fs 不用单独放连续的文件里了、/home/lilydjwg/.cache 缓存不要、/home/lilydjwg/.debug 这个 perf top 用的目录会有 libc 的硬链接,rsync 时会失败所以就不要了。

最终的同步命令如下:

sudo systemd-run -p MemoryMax=64M --scope \
  rsync -aviHAXKhPS --inplace --delete --exclude='*~' --one-file-system \
  / /mnt/root --exclude=/var/cache/pacman/pkg --exclude=/home/lilydjwg/.cache \
  --exclude=/var/lib/pacman.fs --exclude=/ldata --exclude=/home/lilydjwg/.debug

同步好之后,重启进入 live 系统再同步一次以保证最新数据也同步好了。然后把部分被排除的目录再同步一下:~/.cache/winetricks 这个以后不一定能够下到、~/.cache/sxiv 都是有效缓存(我有清理)而且生成耗 CPU、/var/lib/pacman 这个是被 --one-file-system 排除掉的。

然后是在 /etc/default/grub 里更新内核命令行 cryptdevice=/dev/disk/by-partlabel/ssd:ssd:allow-discards。这个 allow-discards 会轻微地降低安全性,不过在中国没什么用的。更新 /etc/fstab。

然后还有 /boot 要处理。其实就是把内核和 initrd 复制过去,然后重新安装 grub、生成 grub 配置。位于机械硬盘上的旧文件之后再删掉即可。

重启,使用 fallback 版 initrd 进入系统,开始修复各种问题。

首先是更新默认的 initrd。不过在更新它之前,我要修改一下我自己的 hook。之前这个 hook 里只有 partprobe 我解密之后的机械硬盘分区,因为我在它上边又分了 xfs 和 swap 两个区。现在因为 encrypt hook 解密的是 SSD 上的分区,所以这个机械硬盘上的加密分区的解密也要自己做。其实也很简单,给这个加密分区添加一下文件密钥,然后

cryptsetup open --type=luks --key-file=/etc/keys/hdd.luks /dev/disk/by-partlabel/main main

就可以了。不需要输入两次密码。

/ldata 使用 automount 延迟挂载,所以需要写 ldata.mount 和 ldata.automount 两个文件,然后 enable ldata.automount 那个。不知道写在 /etc/fstab 里是不是也行。然后把机械硬盘里的目录结构调整一下,把原来 /ldata 下的东西上移一级,旧的 / 里的其它东西都放到隐藏的 .oldroot 里去好了。

swap 本来我是保留着的,不过发现这样子我会时不时听到机械硬盘启动了。而且因为机械硬盘启动比较费时,所以系统会卡好一会儿(大概有一两秒)……所以我默认就不开 swap 了,但是 resume hook 还是保留,需要的时候打开 swap 就可以休眠了。这个 resume hook 也是我需要在启动的时候就解密机械硬盘上的加密分区的原因。

加了一个每周运行的 fstrim -v / cron 任务。没有使用 fstrim.timer 是因为它会 trim 所有设备。而我可不希望它去 trim 我挂载的机械硬盘上的 loop 设备,会造成大量碎片的。

还有一些小问题要处理。chattr +i /etc/resolv.conf 以避免 DNS 服务器被不知不觉修改了。我有用 dnsmasq 的所以这个文件不用动。我有一个 MediaWiki 实例的文件是使用 overlayfs 的,它现在挂载提示「failed to verify upper root origin」。后来才发现相关目录上有同步到几个 trusted. 开头的、overlayfs 使用的扩展属性。是它还挂载的时候被同步到的,不知道为什么最后一次同步时没有被清除掉。手动使用 setxattr 删除掉就好了。

rsync 还出了另外几个莫名其妙的问题。我在 /usr/local/sbin 下有个最近新加的文件的执行权限消失了,造成使用它的 systemd 服务失败。另外有个最近被删除的配置文件竟然还在。我不是有指定 --delete 选项吗?火狐缓存的网站图标也都没有了,需要访问之后才会重新出现。~/.cache 下有很多 root 所有的空目录,也许是我哪次忘记 --exclude 它然后又中断才加上?

Wine 有几个文件有几十 KiB 大的 user.wine.sd 扩展属性。太大了以至于 btrfs 里放不下,报「No space left on device」错误。我刚看到时还吓一跳,以为是我的 SSD 满了,仔细一看才发现只是扩展属性写不下而已。

我于是又带 --dry-run 参数同步了一次,确定再没有什么需要的东西被落下。这次 rsync 出现这些问题很是奇怪,不过我没有留日志,加上操作的时候其实是有不少修修改改的,所以就不深究了吧。

修好所有发现的问题,再次重启之后,systemctl status 和 systemctl --user status 没有失败项了~撒花 O(∩_∩)O~

后记

现在我的系统超快的!比如启动时间:

>>> systemd-analyze
Startup finished in 9.257s (firmware) + 1.466s (loader) + 15.110s (kernel) + 6.945s (userspace) = 32.780s 
graphical.target reached after 6.945s in userspace

firmware 和 loader 咱管不了。kernel 那儿包含了我输入密码解密,以及解密和探索机械硬盘上的分区,所以花了些时间。userspace 那里你别看花了好几秒,其实大部分时间都是花在联网上了。不依赖网络的服务在差不多一秒的时间内就全部启动好了。

之后我还要更新备份脚本,因为我用了 --one-file-system 而现在它们在不同的子卷上。再写一下每日快照的脚本,就不用一不小心删错文件啥的都要去备份里找了。

关于写入量,smartctl -a /dev/nvme0n1; sleep 300; smartctl -a /dev/nvme0n1 统计了一下,因为我开了 collectd 收集一些系统数据,每分钟大概会写入 60MiB 的数据。算下来,一年要写 20T 左右。这块 SSD 标称的是 280TBW,也就是可以写 280TB 的数据。这么算起来能用十年,所以就这样吧,不用再优化了。顺便说一下,SMART 信息里的「Data Units Written」数据,乘以 512000 之后是字节数。

就这样啦。最后还要说一句:SSD 超快的!

3
2
2019
2

使用 cgroups 限制指定进程的内存使用

最近我的系统有这么个问题:在备份或者系统更新等高 I/O 负载的时候,系统整体性能下降严重,界面经常卡到动不了。经过分析发现此时比平常多了许多磁盘读操作。平常的时候磁盘读操作是很少的,会有大量的缓存命中,反倒是写操作一直都有(因为我本地搭了个监控系统)。啊对,分析用到的磁盘性能数据就是来自于这个监控系统。

所以原因很清楚了:备份和系统更新不仅造成了大量缓存未命中,还占用了本来存放着热数据的缓存,导致常规使用的缓存命中率也急速下降,结果我的机械硬盘就忙不过来了。

那么,要是能够限制这些操作占用的缓存,性能不就能好一点吗?那些新读进来的数据反正是短期内再也用不到了,缓存起来也只是浪费有限的内存空间啊。

研究了一下 /sys/fs/cgroup/memory/memory.stat,看起来 cgroups 内存限制是包含缓存部分的,于是就试试呗。正好 systemd 直接就能设置了:

$ sudo systemd-run -p MemoryMax=512M --scope pacman -Syu

本来我是设置的 256M 限制,结果发现 dkms 编译内核模块的时候超级慢,还用掉了不少 swap……于是分了 512M。效果还是不错的,常规操作偶尔还是卡一卡(毕竟还是有一些 I/O 操作),但比起不限制的时候要少很多。

要注意一点的是,不使用 cgroups v2 的话(Arch Linux 默认),这个命令不能加 --user 以在用户级的 systemd 下跑的。而使用 cgroups v2 的话,lxc 和 docker 都跑不了……

备份也是类似的,而且因为 rsync 自己用不到多少内存,这个效果更好:

$ systemd-run -p MemoryMax=256M --scope ./backup-my-system

终于又一次在半小时内完成了备份 QAQ 之前动不动就一两小时的。

我也不知道为什么这个问题近期才出现,总之现在是缓解了。(接下来有空继续计划换 SSD 硬盘的事情~

2020年10月06日更新:现在知道系统在内存不足、使用 swap 的时候出现的卡顿现象可能是这个 bug,并且在 5.5 版本中被修复。我在迁移到 SSD 之后也一直禁用 swap 也是因为这个问题。现在更新到 5.8 之后开启 swap,并没有在 swap 时发生明显的卡顿了。PS: 关于 swap 是干嘛的,可以参看 farseerfc 的这篇《【譯】替 swap 辯護:常見的誤解 》以及后续

Category: Linux | Tags: linux systemd cgroups
2
16
2019
4

在 Linux 下整理磁盘碎片

磁盘碎片其实有两种:文件碎了,和空闲空间碎了。使用 FIEMAP 命令可以获取到文件在磁盘(的逻辑地址上)的分布情况。也是 filefrag -v 命令输出的东西。比如我的 pacman.log 就很碎:

Filesystem type is: 58465342
File size of /var/log/pacman.log is 11052443 (2699 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..    2015:  170210423.. 170212438:   2016:
   1:     2016..    2017:  170567879.. 170567880:      2:  170212439:
   2:     2018..    2027:  170569969.. 170569978:     10:  170567881:
   3:     2028..    2030:  170574582.. 170574584:      3:  170569979:
   4:     2031..    2031:  170574631.. 170574631:      1:  170574585:
   5:     2032..    2033:  170592662.. 170592663:      2:  170574632:
....
 123:     2683..    2687:   56903805..  56903809:      5:   56906403:
 124:     2688..    2698:   56903011..  56903021:     11:   56903810: last,eof
/var/log/pacman.log: 125 extents found

整理的办法也很简单,复制一下,基本上就好了。只要剩余空间足够,小文件会变成一整块,大文件也是少数几块。如果非要弄一整块大的,比如我存放 pacman 数据库的那个小文件系统,可以用 fallocate -l 200M pacman.fs2 这样子的命令分配空间,然后把数据 dd 进去(cp 不行,因为它会先截断文件再写入,之前分配的空间就释放掉了)。

介绍完毕,重点来了:怎么找到那些被写得很碎很碎的文件呢?

对每个文件调用 filefrag 肯定太慢了,所以我写了个库和工具 fiemap-rs 直接调用 FIEMAP。它提供两个工具。一个是 fraghist,统计碎片数量分布直方图,用来了解一下某群文件有多碎。另一个是 fragmorethan,用来寻找碎到一定程度的文件。运行起来是这样子的:

/var/log:
# Number of samples = 712
# Min = 1
# Max = 297
#
# Mean = 11.338483146067423
# Standard deviation = 40.138129228003045
# Variance = 1611.0694179238724
#
# Each ∎ is a count of 13
#
  1 ..  31 [ 658 ]: ∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
 31 ..  61 [  11 ]:
 61 ..  91 [   9 ]:
 91 .. 121 [  10 ]:
121 .. 151 [   6 ]:
151 .. 181 [   5 ]:
181 .. 211 [   3 ]:
211 .. 241 [   2 ]:
241 .. 271 [   3 ]:
271 .. 301 [   5 ]:
/var/log/journal/00000000000000000000000000000000/system@xxx.journal: 271
/var/log/journal/00000000000000000000000000000000/system@xxx.journal: 277
/var/log/journal/00000000000000000000000000000000/system.journal: 274
/var/log/journal/00000000000000000000000000000000/system@xxx.journal: 297
/var/log/journal/00000000000000000000000000000000/system@xxx.journal: 274

我系统上最碎的两群文件是 journal 日志和 python2-carbon 的数据文件。carbon 优化做得挺不好的,明明是预分配的固定大小文件啊,不知道怎么的就弄得很碎了。部分程序的日志(如 pacman、getmail)和火狐的 SQLite 数据库也挺碎的。后边这些我已经处理掉了所以示例输出只好用 journal 的啦。

找到想要整理的过碎的文件之后,复制一下就好啦:

for f in $(<list); do sudo cp -a $f $f.new; sudo mv $f.new $f; done

啊对了,工具的编译方法是,获取源码并安装 Rust 之后,在项目根目录里 cargo build --release 然后就可以在 target/release 下找到新鲜的可执行文件了~顺便说一下,这东西是支持 Android 的哦。

Category: Linux | Tags: linux 文件系统 Rust

Mastodon | Theme: Aeros 2.0 by TheBuckmaker.com