分类目录归档:Linux

关于在 VNC 中可能无法使用 Tab 键的问题

仅作为记录,似乎这是个 Ubuntu 16.04 的 bug,在 18.04 中已经被修正

在 ~/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-keyboard-shortcuts.xml 中找到这样一行:

<property name="&lt;Super&gt;Tab" type="string" value="switch_window_key"/>

<property name=”&lt;Super&gt;Tab” type=”string” value=”switch_window_key”/>

改成

<property name="&lt;Super&gt;Tab" type="empty"/>

<property name=”&lt;Super&gt;Tab” type=”empty”/>

就行了

Kimsufi KS-4 在 Rescue 模式下安装 Ubuntu 18.04

这两天 KS-4 搞特价,4T 硬盘只要 9o,是公网保种机的绝佳配置,于是买买买。下机以后发现默认的系统模板沙雕了,/ 分区不能使用 SoftRaid0 方式挂载,虽然可以曲线救国把 /root 分一大块出来做 R0,但是我不喜欢这种分区方式,于是就想办法重装这个系统。

一开始我测试了 Airium 修改的 Vicer 重装脚本,这个脚本可以把 iKoula 的机器重装成 Raid0,然而不知道为什么一重装就 SSH 不通,遂放弃。在考察了 KS 的后台分区以后,发现 / 分区只能做成 Raid1,并且无法在上面配置 LVM,看来走官方模板的路子是走不通了。于是就剩下一个方法,在 Rescue 模式下手动安装系统。

KS 的机器在 Rescue 下安装系统主要有两个方法,一是使用 dd 安装 Windows,这不是我想要的,而且我也没有 Ubuntu 的 dd 镜像;二是下载系统镜像的 iso 安装包,使用 QEMU 将硬盘挂载以后在虚拟环境中完成安装。奈何 N2800 这个 CPU 过于古老,没有 VT-x 指令集,因而无法运行 QEMU,只好另辟蹊径。

在寻找如何在 Linux 下安装一个 ISO 文件的过程中,偶然发现了 Debian 系的 debootstrap 脚本,这个脚本可以在任何 Linux 环境下安装 Debian 系的 Linux。作为 Debian 的下游,Ubuntu 也有这个脚本,在一些文档的帮助下,我感觉这条路是可行的。

从一个比较 high-level 的角度来看,使用 debootstrap 安装系统需要如下几步:

  • 备份原系统的网络配置(不然等下 SSH 连不上)
  • 硬盘分区,配置 SoftwareRAID
  • 挂载硬盘
  • 在挂载文件夹下运行 debootstrap
  • chroot 进入新系统
  • 在 debootstrap 完成的基础系统中完成硬盘挂载、SSH、网络和引导的配置
  • 将启动方式还原为 local disk,重启

接下来是详细的步骤

备份网络配置

由于我要安装的是 Ubuntu 18.04,因此知道 OVH 的网络配置的最佳方法就是用官方模板装一遍,然后把配置弄出来。在安装之后,发现 OVH 并没使用 netplan,也没有使用之前的 ifupdown 文件,而是直接写了 systemd-networkd 的配置文件。需要备份的是这两个文件:

  • /etc/systemd/network/50-default.network
  • /etc/systemd/network/50-public-interface.link

文件名可能不同,但是基本就是在这个目录下。对于老版本的系统,备份 /etc/network/interfaces。

硬盘分区

理想的分区情况是这样的,两块盘 /dev/sda 和 /dev/sdb,分一个 512M 的区做 R1 挂载在 /boot,剩下的挂载在 /,swap 这里不考虑,装好了以后写一个文件作为 swap 是很方便的事情。

分区使用命令 fdisk,比如 fdisk /dev/sda,之后进入命令行交互环境。一个典型的配置情况如下:

root@rescue:/mnt# fdisk /dev/sda

Welcome to fdisk (util-linux 2.25.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0xc1d78def.

Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (1-4, default 1): 1
First sector (2048-3907029167, default 2048):
Last sector, +sectors or +size{K,M,G,T,P} (2048-3907029167, default 3907029167): +512M

Created a new partition 1 of type 'Linux' and of size 512 MiB.

Command (m for help): n
Partition type
   p   primary (1 primary, 0 extended, 3 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (2-4, default 2):
First sector (1050624-3907029167, default 1050624):
Last sector, +sectors or +size{K,M,G,T,P} (1050624-3907029167, default 3907029167):

Created a new partition 2 of type 'Linux' and of size 1.8 TiB.

按 n 增加分区,然后输入 p 作为 primary 分区,然后指定大小,默认是全部,因此第一个分区输入 +512M,第二个保持默认就好。

之后用 fdisk -l 可以看到当前的分区情况:

root@rescue:/mnt# fdisk -l /dev/sda

Disk /dev/sda: 1.8 TiB, 2000398934016 bytes, 3907029168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xc1d78def

Device     Boot   Start        End    Sectors  Size Id Type
/dev/sda1          2048    1050623    1048576  512M 83 Linux
/dev/sda2       1050624 3907029167 3905978544  1.8T 83 Linux

/dev/sdb 如法炮制即可。

创建 RAID 设备

使用命令 mdadm,先删除之前所有创建的 md,之后再创建新的,命令如下:

mdadm --stop /dev/mdX
mdadm --remove /dev/mdX
mdadm --create --verbose /dev/md0 --level=mirror --raid-devices=2 /dev/sda1 /dev/sdb1
mdadm --create --verbose /dev/md1 --level=stripe  --raid-devices=2 /dev/sda2 /dev/sdb2

前两行多运行几遍直到所有的都被删除,之后创建 /dev/md0 RAID1 作为 /boot,/dev/md1 RAID0 作为 /。

使用 lsblk 可以看到当前的分区情况:

root@rescue:/mnt# lsblk
NAME    MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sdb       8:16   0   1.8T  0 disk
├─sdb2    8:18   0   1.8T  0 part
│ └─md1   9:1    0   3.7T  0 raid0
└─sdb1    8:17   0   512M  0 part
  └─md0   9:0    0 511.4M  0 raid1
sda       8:0    0   1.8T  0 disk
├─sda2    8:2    0   1.8T  0 part
│ └─md1   9:1    0   3.7T  0 raid0
└─sda1    8:1    0   512M  0 part
  └─md0   9:0    0 511.4M  0 raid1

然后格式化成 ext4 文件系统:

mkfs.ext4 /dev/md0
mkfs.ext4 /dev/md1

安装 debootstrap

这里下载 Ubuntu 的 debootstrap 脚本,应该会获得一个类似于 debootstrap_1.0.112ubuntu1_all.deb 的 deb 文件,使用 ar 释放后再解压至根目录下。ar 后会看到一个 data.tar.gz,释放到根目录下。

ar -x debootstrap_1.0.112ubuntu1_all.deb
cd /
zcat /pat/to/data.tar.gz|tar xv

运行 debootstrap

接下来才是重头戏,挂载之前分好区的硬盘并安装系统。我们假定 /dev/md1 挂载在 /mnt/ubuntu 下,然后对 /mnt/ubuntu 运行 debootstrap 安装系统。

mkdir /mnt/ubuntu
mount /dev/md1 /mnt/ubuntu
debootstrap --arch amd64 bionic /mnt/ubuntu http://fr.archive.ubuntu.com/ubuntu/

debootstrap 的命令中,要求安装 amd64 架构的 Ubuntu bionic,也就是 18.04,系统在 /mnt/ubuntu 下,需要的包从 http://fr.archive.ubuntu.com/ubuntu/ 获得。

稍等一会儿, 看到 I: Base system installed successfully. 就安装完成了。

chroot 准备

debootstrap 完成以后,在 /mnt/ubuntu 下就可以看到熟悉的 Linux 根目录结构了,然后我们做一些准备以方便 chroot 运行。

mount /dev/md0 /mnt/ubuntu/boot
mount --bind /dev /mnt/ubuntu/dev
mount --bind /dev/pts /mnt/ubuntu/dev/pts
mount -t proc proc /mnt/ubuntu/proc
mount -t sysfs sys /mnt/ubuntu/sys

第一行是将 /dev/md0 挂载在 /boot 下,这是和我们的分区意图相一致,2 3 两行是将当前系统的 /dev 映射进去,方便之后配置 fstab 文件,4 5 两行是将 proc 和 sysfs 挂载进去,方便配置时进行交互。

之后 chroot 进入新的环境:

LANG=C.UTF-8 chroot /mnt/ubuntu /bin/bash
locale-gen en_US.UTF-8
export TERM=xterm-color

如果进去以后报错 “bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)” 则运行第二行,如果不报错就不用管了,第三行是为了 Terminal 好看点。

系统配置

首先配置一下软件包的源,编辑 /etc/apt/sources.list 写入如下几行:

deb http://fr.archive.ubuntu.com/ubuntu bionic main restricted
deb http://fr.archive.ubuntu.com/ubuntu bionic universe
deb http://fr.archive.ubuntu.com/ubuntu bionic multiverse
deb http://fr.archive.ubuntu.com/ubuntu bionic-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu/ bionic-security main restricted
deb http://security.ubuntu.com/ubuntu/ bionic-security universe
deb http://security.ubuntu.com/ubuntu/ bionic-security multiverse

并且 apt update。完成后先安装 mdadm 和 lvm2,方便之后挂载。

apt-get install mdadm lvm2

编辑 /etc/fstab 写入挂载方式:

# file system    mount point   type    options                  dump pass
/dev/md0        /boot   ext4    defaults        0       2
/dev/md1        /       ext4    defaults        0       1

运行 mount -a 自动挂载,此时使用 df 命令应该可以看到正确的硬盘占用了。

运行 passwd 命令配置 root 用户密码,如果需要加用户,此时使用 adduser 即可。

之后安装内核和 OpenSSH Server:

apt-get install openssh-server linux-generic linux-tools-generic

正常情况下,在安装时会弹出 grub 的配置窗口,选择将 GRUB 安装在 /dev/sda 和 /dev/sdb,如果没有询问的话,安装完成以后运行如下两行:

grub-install /dev/sda
grub-install /dev/sdb

之后写回最开始备份的网络配置文件,并且启动相关服务:

vi /etc/systemd/network/50-default.network
vi /etc/systemd/network/50-public-interface.link

systemctl enable systemd-networkd
systemctl enable systemd-resolved

注意如果只有 root 用户的话,去 /etc/ssh/sshd_config 中修改 PermitRootLogin 为 yes,不然可能无法登录。

这时候就安装完成了,去 KS 后台用本地硬盘重启即可。在登录后记得配置 Public Key,然后将 PermitRootLogin 改为 PermitRootLogin prohibit-password。

最后在安装好的系统中使用 df 和 lsblk 命令查看分区情况:

root@rescue:~# df -h
Filesystem      Size  Used Avail Use% Mounted on
udev            1.9G     0  1.9G   0% /dev
tmpfs           393M  676K  393M   1% /run
/dev/md1        3.6T  1.3G  3.4T   1% /
tmpfs           2.0G     0  2.0G   0% /dev/shm
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           2.0G     0  2.0G   0% /sys/fs/cgroup
/dev/md0        488M   73M  386M  16% /boot
tmpfs           393M     0  393M   0% /run/user/0
root@rescue:~# lsblk
NAME    MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sda       8:0    0   1.8T  0 disk
|-sda1    8:1    0   512M  0 part
| `-md0   9:0    0 511.4M  0 raid1 /boot
`-sda2    8:2    0   1.8T  0 part
  `-md1   9:1    0   3.7T  0 raid0 /
sdb       8:16   0   1.8T  0 disk
|-sdb1    8:17   0   512M  0 part
| `-md0   9:0    0 511.4M  0 raid1 /boot
`-sdb2    8:18   0   1.8T  0 part
  `-md1   9:1    0   3.7T  0 raid0 /

参考资料

系统安装相关:

https://www.codejam.info/2015/12/installing-nixos-on-a-kimsufi.html
https://help.ubuntu.com/community/Installation/MinimalCD
https://www.pozzo-balbi.com/help/Remote_install_OS_-_Qemu
https://help.ubuntu.com/community/Installation/FromLinux
https://help.ubuntu.com/lts/installation-guide/powerpc/apds04.html
http://ports.ubuntu.com/ubuntu-ports/pool/main/d/debootstrap/

分区相关:

https://wiki.archlinux.org/index.php/Fdisk_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)
https://raid.wiki.kernel.org/index.php/RAID_setup#Create_RAID_device
https://wiki.archlinux.org/index.php/Fstab_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)

配置相关:

https://help.ubuntu.com/community/Installation/SoftwareRAID
https://wiki.archlinux.org/index.php/systemd-networkd#Basic_usage
https://unix.stackexchange.com/questions/158400/etc-shadow-how-to-generate-6-s-encrypted-password

nginx 规则匹配与 acme.sh 自动证书续签

这事情是这样的,我半个月前发现之前配置的 Syncthing 代理和 Nextcloud 页面的 Let’s Encrypt 证书都过期了。按道理讲,oneinstack 的一键包会添加一个 crontab 任务自动续签证书,但是就是没 work,手动执行 acme.sh 以后,发现是域名认证无法通过,请求 .well-known/acme-challenge下的认证文件时返回 403 错误。

Nextcloud 使用了一键包中提供的 rewrite rule,Syncthing 是自己改的 nginx 配置文件,而同服务器上的 h5ai 却续签正常。在这种情况下,首先肯定是怀疑自己改错了。

在查看了 Syncthing 的 nginx 配置文件以后,发现默认将所有请求转发给 localhost 处理,那么问题就显而易见了,Syncthing 不知道那个 .well-known 是什么东西,自然返回错误,因此加上了一条 location 规则:

location /.well-known {
}

嗯,可以续签了。

但是同样的方法在 Nextcloud 上却行不通,加入了这个规则以后,curl 仍然返回 403 错误。查看了 oneinstack 的 rewrite rule 以后发现,似乎本来就对这个目录做了处理。考虑到一堆正则看得头大,想着直接 override 掉好了,在查找了 location 的用法以后,加上了这么一行:

location ^~ /.well-known/acme-challenge {
}

然后就能用了。

最后再转载一些对于 location 用法的解释:

location  = / {
  # 精确匹配 / ,主机名后面不能带任何字符串
  [ configuration A ]
}

location  / {
  # 因为所有的地址都以 / 开头,所以这条规则将匹配到所有请求
  # 但是正则和最长字符串会优先匹配
  [ configuration B ]
}

location /documents/ {
  # 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索
  # 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
  [ configuration C ]
}

location ~ /documents/Abc {
  # 匹配任何以 /documents/Abc 开头的地址,匹配符合以后,还要继续往下搜索
  # 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
  [ configuration CC ]
}

location ^~ /images/ {
  # 匹配任何以 /images/ 开头的地址,匹配符合以后,停止往下搜索正则,采用这一条。
  [ configuration D ]
}

location ~* \.(gif|jpg|jpeg)$ {
  # 匹配所有以 gif,jpg或jpeg 结尾的请求
  # 然而,所有请求 /images/ 下的图片会被 config D 处理,因为 ^~ 到达不了这一条正则
  [ configuration E ]
}

location /images/ {
  # 字符匹配到 /images/,继续往下,会发现 ^~ 存在
  [ configuration F ]
}

location /images/abc {
  # 最长字符匹配到 /images/abc,继续往下,会发现 ^~ 存在
  # F与G的放置顺序是没有关系的
  [ configuration G ]
}

location ~ /images/abc/ {
  # 只有去掉 config D 才有效:先最长匹配 config G 开头的地址,继续往下搜索,匹配到这一条正则,采用
    [ configuration H ]
}

location ~* /js/.*/\.js

已=开头表示精确匹配
如 A 中只匹配根目录结尾的请求,后面不能带任何字符串。
^~ 开头表示uri以某个常规字符串开头,不是正则匹配
~ 开头表示区分大小写的正则匹配;
~* 开头表示不区分大小写的正则匹配
/ 通用匹配, 如果没有其它匹配,任何请求都会匹配到
顺序 no优先级:
(location =) > (location 完整路径) > (location ^~ 路径) > (location ~,~* 正则顺序) > (location 部分起始路径) > (/)

上面的匹配结果
按照上面的location写法,以下的匹配示例成立:

/ -> config A
精确完全匹配,即使/index.html也匹配不了
/downloads/download.html -> config B
匹配B以后,往下没有任何匹配,采用B
/images/1.gif -> configuration D
匹配到F,往下匹配到D,停止往下
/images/abc/def -> config D
最长匹配到G,往下匹配D,停止往下
你可以看到 任何以/images/开头的都会匹配到D并停止,FG写在这里是没有任何意义的,H是永远轮不到的,这里只是为了说明匹配顺序
/documents/document.html -> config C
匹配到C,往下没有任何匹配,采用C
/documents/1.jpg -> configuration E
匹配到C,往下正则匹配到E
/documents/Abc.jpg -> config CC
最长匹配到C,往下正则顺序匹配到CC,不会往下到E

 

qBittorrent 自动重启

因为 libtorrent 全是 bug,所以用了它的 qBittorrent 经常会莫名其妙出现一些错误,然后自己挂掉。虽然每次的 backtrace 都是在 libtorrent 里面出现的错误,但是同样用了 lt 的 Deluge 就一点都没有问题,所以估计这俩的代码都写得太差吧。

因此,写了一个简单的脚本去监控 qb 的运行状态,在它挂掉的时候自动重启,在漏内存的时候自动 kill 掉重开一个。

具体代码可以看 Github。默认行为是在 Xserver 存在的情况下启动 GUI 版的 qb,如果没有 Xserver 就启动 qbittorrent-nox,一分钟检测一次运行状态,挂了就重开。

如果在 VNC 下面使用 GUI 版的 qb,且想要随着桌面自动启动的话,需要在 ~/.config/autostart 下放置一个 desktop 文件

如果想看到一个 Terminal,并且监控一下重启的原因,用这个版本:

[Desktop Entry]
Encoding=UTF-8
Version=0.9.4
Type=Application
Name=qBittorrent-daemon
Comment=
Exec=bash -c '/usr/bin/python3 <path>/launch.py'
OnlyShowIn=XFCE;
StartupNotify=false
Terminal=true
Hidden=false

如果不需要看到那个 Terminal,用这个版本:

[Desktop Entry]
Encoding=UTF-8
Version=0.9.4
Type=Application
Name=qBittorrent-daemon
Comment=
Exec=/usr/bin/python3 <path>/launch.py
OnlyShowIn=XFCE;
StartupNotify=false
Terminal=false
Hidden=false

 

Hetzner Cloud Volume 功能的使用

Hz cloud 什么都好,就是盘太小,最基础的实例只有 20G 盘,相比 Scaleway 的 50G 还是差了点。不过考虑到它 CPU 也好,盘也快,所以想把建在 Scaleway 上面的本博客搬到 Hz cloud。正好最近 Hz cloud 出了 Volume 的功能可以自己加硬盘,在星菊的帮助下,使用 LVM 完成了 Volume 和自带分区合并的功能。

要做到这点,需要分两步,一个是在 Rescue 模式下将系统安装在 LVM 分区上,第二步是在系统中扩展 LVM 分区。

安装系统时,需要在后台进入 Rescue 模式,Hz 家的 Rescue 都一样,在选定 image 以后会弹出配置文件让你编辑,在分区那里需要这么写:

PART /boot ext4 512M
PART lvm vg0 all
LV vg0 root / ext4 all

之后双击 Esc 退出,正常安装就行,之后 LVM 分区就挂载在 / 这里了。

然后重启进入系统,在 Volume 页面创建之后 attach 到之前的实例。通常来说,系统会安装在 /dev/sda,新的 Volume 在 /dev/sdb。执行以下命令将分区合并:

pvcreate -ff /dev/sdb
vgextend vg0 /dev/sdb
lvresize -l +100%FREE vg0/root
resize2fs /dev/vg0/root

做了如下几件事:

  1. 在 /dev/sdb 上创建 Physical Volume
  2. 将 Volume Group vg0 扩展到 /dev/sdb 上
  3. 扩展 Logical Volume vg0/root 到所有的可用空间
  4. 扩展 /dev/vg0/root 上的文件系统

我们可以通过 pvdisplay vgdisplay 和 lsblk 看到效果:

root@sb1 ~ # pvdisplay
  --- Physical volume ---
  PV Name               /dev/sda2
  VG Name               vg0
  PV Size               <37.65 GiB / not usable 2.00 MiB
  Allocatable           yes (but full)
  PE Size               4.00 MiB
  Total PE              9637
  Free PE               0
  Allocated PE          9637
  PV UUID               ZbfxjP-RNf5-ScPU-eclg-dMjj-0WKg-T3FA2p

  --- Physical volume ---
  PV Name               /dev/sdb
  VG Name               vg0
  PV Size               750.00 GiB / not usable 4.00 MiB
  Allocatable           yes
  PE Size               4.00 MiB
  Total PE              191999
  Free PE               191999
  Allocated PE          0
  PV UUID               ZphGg0-2XcJ-Kjzy-gfBX-7pSp-Wo4L-fbVkD5

root@sb1 ~ # vgdisplay
  --- Volume group ---
  VG Name               vg0
  System ID
  Format                lvm2
  Metadata Areas        2
  Metadata Sequence No  3
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                1
  Open LV               1
  Max PV                0
  Cur PV                2
  Act PV                2
  VG Size               787.64 GiB
  PE Size               4.00 MiB
  Total PE              201636
  Alloc PE / Size       9637 / 37.64 GiB
  Free  PE / Size       191999 / <750.00 GiB
  VG UUID               7It9l8-70zz-03Uk-8md3-mPBZ-o7fe-clN3QX

root@sb1 ~ # lsblk
NAME         MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda            8:0    0  38.2G  0 disk
├─sda1         8:1    0   512M  0 part /boot
└─sda2         8:2    0  37.7G  0 part
  └─vg0-root 253:0    0 787.7G  0 lvm  /
sdb            8:16   0   750G  0 disk
└─vg0-root   253:0    0 787.7G  0 lvm  /
sr0           11:0    1  1024M  0 rom

在完成了所有命令之后,可以在 lsblk 中看到 / 已经扩充到了 787.7G

关于公网分流任务使用 qBittorrent + libtorrent 内存泄漏的一些想法

出现的问题是,在 Linux qb 挂了 400+ 公网种子的情况下,使用 libtorrent 1.1 作为后端,在运行三至四天以后就会内存使用量不断增加,直到占用了几乎所有的可用内存,系统崩溃为止。

在 qb 的这个 Issue 中(https://github.com/qbittorrent/qBittorrent/issues/8630),有人在 Windows 上观察到了一样的现象,报告在 Enable OS cache 的情况下最终会导致内存泄漏。

在 https://github.com/qbittorrent/qBittorrent/issues/8295 和 https://github.com/arvidn/libtorrent/issues/1997 这两个 Issue 中,有人提到是 libtorrent 本身的 IO 和缓存实现存在问题,导致磁盘 IO 效率过低。但是这两个问题也都是在 Windows 上观察得出的,目前提供的方法是关闭 OS Cache,打开 libtorrent 的 coalesce reads & writes 选项,以在 Windows 上达到较好的 IO 性能表现(这个选项在 Linux 上是不必要的,因为 Linux 有 pwritev() 的实现,不需要通过 pwrite() 进行模拟——arvidn)。

但是在 Linux 上,这个问题仍然没有任何解决的迹象。在我看来,假定 libtorrent 自身的内存管理不存在问题,没有内存泄漏的情况,那么内存占用应该存在于两个方面:一是在 libtorrent 1.1 解决了 IO 性能问题以后引入的新 bug,即 lt 可以同时打开大量的 File Descriptor 且不陷入死锁,那么由于使用 buffered IO 的问题,在系统内核层面上消耗了大量的内存;另一个可能性是 TCP Socket buffer,libtorrent 1.1 相比 1.0 修正了大量 TCP Socket 连接卡死的问题(甚至出现了 too many open files 的情况,参见之前的文章,这个现象在 lt 1.0 从未出现过),大量 peer 和 tracker 连接使用的 TCP Socket 在内核层面占用了大量的内存,进而出现内存占用高的问题。

目前先禁用 OS Cache 进行下一步测试,观察是否还会出现内存占用过高。如果有,那么应该是 TCP 造成的问题了。(在看了 IBM 关于 Linux Direct IO 的这篇文章后,我对 IO Buffer 这个方向不报太大希望,因为无论怎么看 Buffered IO 的实现也不像会把自己噎死的样子)

红米 5A 刷欧洲版 MIUI 记录

老年人的第一次刷机。

最近把红米备机拿出来用了,升到了 ADUI 10,感觉还不错,但是广告实在太多。想起来当年被卡 15 天解 BL 锁的限制早过了很久了,就想着把这破系统刷了吧。

查了一圈发现 MIUI EU 版是相对干净的系统,可以在这里取得。

准备工作

解锁 Bootloader
MIUI EU 版刷机包
Android Debug Bridge
第三方 Recovery TWRP
Root 包 Magisk

刷机步骤

  1. 解锁 Bootloader
    1. 进入“设置 -> 开发者选项 -> 设备解锁状态”中绑定账号和设备;
    2. 手动进入 Fastboot 模式(关机后,同时按住开机键和音量下键);
    3. 通过USB连接手机,点击 “解锁”按钮;
  2. 刷入 TWRP
    1. 在 Bootloader 模式下,运行 fastboot flash recovery <twrp_image>刷入 TWRP
    2. fastboot reboot 重启设备,注意此时需要立即按下音量上 + 电源以进入 Recovery 模式,否则 MIUI 会覆盖刷入的自定义 Revocery
  3. Recovery 成功刷入设备后,将手机关机,在关机状态下按住电源键和音量上键,进入 Recovery 模式,滑动 Swipe to Allow Modifications 进入主菜单。点击 Wipe,点击 Format Data,输入 yes 完成格式化,返回上一菜单,点击 Advanced Wipe,选择 Dalvik,Cache,System,Data 和 Internal Storage 分区,并滑动 Swipe to Wipe 进行擦除,然后返回 Recovery 主菜单
    1. 注意可能在 Wipe /data 分区时可能会出现错误:TWRP Unable to mount '/data',如果出现该错误,在 Fastboot 模式下运行 fastboot format userdata清理 /data 分区之后再进行操作
  4. 在电脑磁盘列表中找到手机,复制 ROM 至手机,复制完成后在 Recovery主菜单中,点击 Install,点击 ROM 包,滑动 Swipe to confirm Flash 进行刷入,重启设备
    1. 任意方法刷入 ROM 后,命令窗口中结束 adb 服务adb kill-server
  5. 重启进入 Recovery 模式,按照刷 ROM 的方式刷入 root 包

然后大功告成

参考资料

https://www.zhihu.com/question/50231539/answer/530627637
https://www.reddit.com/r/Nexus6P/comments/3qnzz0/twrp_unable_to_mount_data/

 

优化 wine 的字体显示

有时候总有一些不得不在 Linux 下跑一些 Windows 程序的需求,所以我们会用到 wine,但是 wine 默认并不是给中文用户设计的,因此我们需要一些办法来解决中文显示乱码和效果差的问题。

中文乱码,主要表现是非 ASCII 字符全部都显示为方框,这个是没有字体造成的,需要在 ~/.wine/drive_c/windows/Fonts 下面安装中文字体,一般来说,装好宋体、黑体和微软雅黑这些常用的就能用了,如果想装点别的,自然也可以。这里提供了一些打包好的基础字体下载:

cd ~/.wine/drive_c/windows/Fonts
wget https://down.gloriousdays.pw/Fonts/wine_fonts.tar.xz
tar xJvf wine_fonts.tar.xz
rm wine_fonts.tar.xz

搞定了字体以后,很快就会发现这些字体的显示效果十分差,完全没有做 AA 的意思,我们用 winetricks 解决这个问题:

apt install winetricks
winetricks settings fontsmooth=rgb

重新打开 exe 程序即可

使用 zram 进行内存压缩

对于像 KS-3 这样只有 4GB 内存的小内存服务器,如果想在上面跑一些比较复杂的服务,经常会遇到内存不足的问题。一般说到内存不足,第一反应都是加 swap 空间,但是对于机械硬盘的场景,添加盲目添加 swap 空间并不是一个好的选择,因为这样会显著增加系统整体的 latency。这个时候 zram 就可以派上用场了。

zram 是在 Linux Kernel 3.2 加入的一个模块,其功能是在内存中开辟一块空间,用来存储压缩后的内存数据,这样可以在牺牲一定的 CPU Cycle 的情况下,在内存中存储尽量多的数据而不需要写入到磁盘。

对于 Ubuntu 系统,开启 zram 的方法很简单,只需要安装 zram-config 这个包之后重启即可。zram 默认会将系统内存的一半作为 zram,然后根据 CPU 核心数平均分配到每个 zram 设备。比如在我的 KS-3 上,通过 zramctl 查看 zram 的情况,是这样的:

# zramctl
NAME       ALGORITHM DISKSIZE DATA COMPR TOTAL STREAMS MOUNTPOINT
/dev/zram3 lz4           491M   4K   63B    4K       4 [SWAP]
/dev/zram2 lz4           491M   4K   63B    4K       4 [SWAP]
/dev/zram1 lz4           491M   4K   63B    4K       4 [SWAP]
/dev/zram0 lz4           491M   4K   63B    4K       4 [SWAP]

注意到这里的压缩算法,有两种算法 lzo 和 lz4 可选,默认是 lzo。根据 Benchmark,lz4 的压缩和解压性能在压缩率和 lzo 持平的情况下显著高于后者,因此我们应该采用 lz4 而非 lzo 以获得更高的系统效率。

虽然 zramctl 可以帮助调整 zram 的情况,但是我们还是应该在系统启动时就将这些东西配置好。zram-config 安装好后会默认添加 zram-config.service,这个 service 是运行 /usr/bin/init-zram-swapping 这个脚本以配置对应的 zram 设备,默认情况下,其配置设备的内容应该是这样:

#!/bin/sh

# load dependency modules
NRDEVICES=$(grep -c ^processor /proc/cpuinfo | sed 's/^0$/1/')
if modinfo zram | grep -q ' zram_num_devices:' 2>/dev/null; then
  MODPROBE_ARGS="zram_num_devices=${NRDEVICES}"
elif modinfo zram | grep -q ' num_devices:' 2>/dev/null; then
  MODPROBE_ARGS="num_devices=${NRDEVICES}"
else
  exit 1
fi
modprobe zram $MODPROBE_ARGS

# Calculate memory to use for zram (1/2 of ram)
totalmem=`LC_ALL=C free | grep -e "^Mem:" | sed -e 's/^Mem: *//' -e 's/  *.*//'`
mem=$(((totalmem / 2 / ${NRDEVICES}) * 1024))

# initialize the devices
for i in $(seq ${NRDEVICES}); do
  DEVNUMBER=$((i - 1))
  echo $mem > /sys/block/zram${DEVNUMBER}/disksize
  mkswap /dev/zram${DEVNUMBER}
  swapon -p 5 /dev/zram${DEVNUMBER}
done

如果我们要默认使用 lz4 算法,那么应该将最后一段改成这样:

# initialize the devices
for i in $(seq ${NRDEVICES}); do
  DEVNUMBER=$((i - 1))
  echo lz4 > /sys/block/zram${DEVNUMBER}/comp_algorithm
  echo $mem > /sys/block/zram${DEVNUMBER}/disksize
  mkswap /dev/zram${DEVNUMBER}
  swapon -p 5 /dev/zram${DEVNUMBER}
done

注意算法设置一定要在配置空间大小之前,否则不能正确修改。

修改过后,运行 systemctl restart zram-config 就可以载入新的配置。

参考资料:

https://askubuntu.com/questions/1044976/make-zram-use-lz4-compression-algorithm
https://github.com/lz4/lz4
https://sites.google.com/site/easylinuxtipsproject/speed#TOC-Only-768-MB-RAM-or-less:-enable-zRam
http://tuxdiary.com/2015/07/28/zram/
https://wiki.archlinux.org/index.php/improving_performance#Zram_or_zswap