Linux 折腾笔记
这里总结一些 Linux 从日用到服务器运维的不成篇的经验技巧。
系统安装相关
Grub4Dos 引导 ISO 启动
下面的方法适用于 Debian / Ubuntu 系的发行版。对于 RHEL 系的发行版,Install Custom Operating System on Lighthouse Application Server 一文的末尾给出了适合的方法。
当手头没有可以使用的 U 盘来刻录 LiveCD 时,尝试此方法。
需要准备这些文件:
ISO 本体
ISO 下的 casper 目录下的 vmlinuz 和 initrd.lz
ISO 下的 .disk 目录(存疑)
Grub4Dos 启动命令:
root (hdx,x) # 这是存放上面这些文件的磁盘
kernel /casper/vmlinuz boot=casper iso-scan/filename=/ISO_FILE.iso
initrd /initrd.lz
boot
界面美化相关
字体安装
除了系统的全局字体(例如,更纱黑体)外,各种办公用字体都可以直接放在 ~/.fonts
下。复制好后使用
sudo fc-cache -fsv
来刷新 fontconfig 的字体缓存。
Fontconfig 配置
Fontconfig 是多数 Linux 发行版的一个库,用于管理系统上安装的字体,并配置字体的渲染方式。配置好 Fontconfig,可以让系统整体的字体显示更雅观。
字体的安装
这里使用更纱黑体 Sarasa 作为系统的全局字体。这个中文字体的西文部分与中文部分相对协调,基线平齐,又提供了半角引号的 UI 版本,因此适合作为系统字体。
安装 Sarasa Gothic SC、Sarasa UI SC 和 Sarasa Mono SC 这三个字体家族到 /usr/share/fonts
。
同时确保系统中有 Noto Sans CJK 对应的中日朝字体。
配置 fonts.conf
下面是我的 ~/.config/fontconfig/fonts.conf
:
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<!-- 设置西文字体(强绑定)优先级高于简体中文,
保证字体匹配时优先使用英文字体替换中文字体。
-->
<alias binding="strong">
<family>serif</family>
<prefer><family>Noto Serif</family></prefer>
</alias>
<alias binding="strong">
<family>sans-serif</family>
<prefer><family>Sarasa UI SC</family></prefer>
</alias>
<alias binding="strong">
<family>monospace</family>
<prefer><family>Sarasa Mono SC</family></prefer>
</alias>
<!-- 设置简体中文字体(强绑定)优先级低于西文字体,
用于覆盖 ubuntu 中其他配置文件,这里不再考虑语言选
项,强绑定优先级高于语言设置。其他语言用户,可以在
下一段中对中文字体进行替换,以符合需求。
-->
<alias binding="strong">
<family>serif</family>
<prefer><family>Noto Serif CJK SC</family></prefer>
</alias>
<alias binding="strong">
<family>sans-serif</family>
<prefer><family>Sarasa UI SC</family></prefer>
</alias>
<alias binding="strong">
<family>monospace</family>
<prefer><family>Sarasa Mono SC</family></prefer>
</alias>
<!-- 根据中文语言环境,选择合适的字体替换简体中文字体,
以符合需求。这里对港澳台等地区,均使用 noto 系列字体
对中文简体字体进行替换。
-->
<!-- 香港 -->
<match target="pattern">
<test name="lang"><string>zh-hk</string></test>
<test name="family"><string>Noto Serif CJK SC</string></test>
<test name="family"><string>serif</string></test>
<edit name="family" binding="strong"><string>Noto Serif CJK TC</string></edit>
</match>
<match target="pattern">
<test name="lang"><string>zh-hk</string></test>
<test name="family"><string>Sarasa UI SC</string></test>
<test name="family"><string>sans-serif</string></test>
<edit name="family" binding="strong"><string>Noto Sans CJK HK</string></edit>
</match>
<match target="pattern">
<test name="lang"><string>zh-hk</string></test>
<test name="family"><string>Sarasa Mono SC</string></test>
<test name="family"><string>monospace</string></test>
<edit name="family" binding="strong"><string>Noto Sans Mono CJK HK</string></edit>
</match>
<!-- 澳门 -->
<match target="pattern">
<test name="lang"><string>zh-mo</string></test>
<test name="family"><string>Noto Serif CJK SC</string></test>
<test name="family"><string>serif</string></test>
<edit name="family" binding="strong"><string>Noto Serif CJK TC</string></edit>
</match>
<match target="pattern">
<test name="lang"><string>zh-mo</string></test>
<test name="family"><string>Sarasa UI SC</string></test>
<test name="family"><string>sans-serif</string></test>
<edit name="family" binding="strong"><string>Noto Sans CJK TC</string></edit>
</match>
<match target="pattern">
<test name="lang"><string>zh-mo</string></test>
<test name="family"><string>Sarasa Mono SC</string></test>
<test name="family"><string>monospace</string></test>
<edit name="family" binding="strong"><string>Noto Sans Mono CJK TC</string></edit>
</match>
<!-- 台湾 -->
<match target="pattern">
<test name="lang"><string>zh-tw</string></test>
<test name="family"><string>Noto Serif CJK SC</string></test>
<test name="family"><string>serif</string></test>
<edit name="family" binding="strong"><string>Noto Serif CJK TC</string></edit>
</match>
<match target="pattern">
<test name="lang"><string>zh-tw</string></test>
<test name="family"><string>Sarasa UI SC</string></test>
<test name="family"><string>sans-serif</string></test>
<edit name="family" binding="strong"><string>Noto Sans CJK TC</string></edit>
</match>
<match target="pattern">
<test name="lang"><string>zh-tw</string></test>
<test name="family"><string>Sarasa Mono SC</string></test>
<test name="family"><string>monospace</string></test>
<edit name="family" binding="strong"><string>Noto Sans Mono CJK TC</string></edit>
</match>
<!-- 将 OpenSymbol 符号字体放在中文字体之后,优先级低于简体中文,
这里主要修正 libreoffice 中列表符号显示错误的问题,该问题主要是
符号字体匹配的问题。
-->
<alias binding="strong">
<family>serif</family>
<prefer><family>OpenSymbol</family></prefer>
</alias>
<alias binding="strong">
<family>sans-serif</family>
<prefer><family>OpenSymbol</family></prefer>
</alias>
<alias binding="strong">
<family>monospace</family>
<prefer><family>OpenSymbol</family></prefer>
</alias>
<!-- 字体渲染全部为使用矢量字体的用户考虑, 开启抗锯齿,优先使用
内置微调,微调类型全部设置为 hintslight,lcdfilter 模式全部设置
为 lcddefault,启用内置点阵(表情符号必须开启内嵌点阵),禁用合成粗体。
-->
<match target="font">
<edit name="antialias"><bool>true</bool></edit>
<edit name="hinting"><bool>true</bool></edit>
<edit name="autohint"><bool>false</bool></edit>
<edit name="hintstyle"><const>hintslight</const></edit>
<edit name="rgba"><const>rgb</const></edit>
<edit name="lcdfilter"><const>lcddefault</const></edit>
<edit name="embeddedbitmap"><bool>true</bool></edit>
<edit name="embolden"><bool>false</bool></edit>
</match>
<!-- 禁用中易黑体的点阵字体。 -->
<match target="font">
<test name="family"><string>SimSun</string></test>
<edit name="embeddedbitmap"><bool>false</bool></edit>
</match>
<!-- 为没有原生斜体的字体使用合成斜体。 -->
<match target="font">
<test name="slant" compare="eq"><const>roman</const></test>
<test name="slant" compare="not_eq" target="pattern"><const>roman</const></test>
<edit name="slant"><const>oblique</const></edit>
<edit name="matrix">
<times>
<name>matrix</name>
<matrix>
<double>1</double><double>0.2</double>
<double>0</double><double>1</double>
</matrix>
</times>
</edit>
</match>
<!-- 为没有原生粗体的字体使用合成粗体。 -->
<match target="font">
<test name="weight" compare="less"><int>105</int></test>
<test name="weight" compare="more" target="pattern"><int>105</int></test>
<edit name="weight"><const>bold</const></edit>
<edit name="embolden"><bool>true</bool></edit>
</match>
<!-- 修正等宽西文在与 Sarasa Mono SC 混排时的对齐效果,
这里首先设置奇偶标记,判断字体大小是否为奇数,是奇数返回 true
是偶数返回 false。
-->
<!-- 设置奇偶标记 -->
<match target="font">
<test name="family"><string>Sarasa Mono SC</string></test>
<edit name="isOddPx">
<eq>
<round><divide><plus><name>pixelsize</name><double>0.5</double></plus><double>2</double></divide></round>
<ceil><divide><plus><name>pixelsize</name><double>0.5</double></plus><double>2</double></divide></ceil>
</eq>
</edit>
</match>
<!-- 如果"视觉大小"是奇数,那么上调为偶像素,因为 Monospace 在奇像素下总是大一级显示 -->
<match target="font">
<test name="family"><string>Sarasa Mono SC</string></test>
<test name="isOddPx"><bool>true</bool></test>
<edit name="pixelsize"><plus><name>pixelsize</name><int>1</int></plus></edit>
</match>
<!-- 修正等宽西文在与 Noto Sans Mono CJK HK 混排时的对齐效果,
这里首先设置奇偶标记,判断字体大小是否为奇数,是奇数返回 true
是偶数返回 false。
-->
<!-- 设置奇偶标记 -->
<match target="font">
<test name="family"><string>Noto Sans Mono CJK HK</string></test>
<edit name="isOddPx">
<eq>
<round><divide><plus><name>pixelsize</name><double>0.5</double></plus><double>2</double></divide></round>
<ceil><divide><plus><name>pixelsize</name><double>0.5</double></plus><double>2</double></divide></ceil>
</eq>
</edit>
</match>
<!-- 如果"视觉大小"是奇数,那么上调为偶像素,因为 Monospace 在奇像素下总是大一级显示 -->
<match target="font">
<test name="family"><string>Noto Sans Mono CJK HK</string></test>
<test name="isOddPx"><bool>true</bool></test>
<edit name="pixelsize"><plus><name>pixelsize</name><int>1</int></plus></edit>
</match>
<!-- 修正等宽西文在与 Noto Sans Mono CJK TC 混排时的对齐效果,
这里首先设置奇偶标记,判断字体大小是否为奇数,是奇数返回 true
是偶数返回 false。
-->
<!-- 设置奇偶标记 -->
<match target="font">
<test name="family"><string>Noto Sans Mono CJK TC</string></test>
<edit name="isOddPx">
<eq>
<round><divide><plus><name>pixelsize</name><double>0.5</double></plus><double>2</double></divide></round>
<ceil><divide><plus><name>pixelsize</name><double>0.5</double></plus><double>2</double></divide></ceil>
</eq>
</edit>
</match>
<!-- 如果"视觉大小"是奇数,那么上调为偶像素,因为 Monospace 在奇像素下总是大一级显示 -->
<match target="font">
<test name="family"><string>Noto Sans Mono CJK TC</string></test>
<test name="isOddPx"><bool>true</bool></test>
<edit name="pixelsize"><plus><name>pixelsize</name><int>1</int></plus></edit>
</match>
<!-- 删除奇偶标记 -->
<match target="font">
<edit name="isOddPx" mode="delete"></edit>
</match>
</fontconfig>
验证系统字体匹配
首先执行 sudo fc-cache -fsv
来刷新字体缓存,然后使用 fc-match
来测试字体匹配。
主要是验证 sans-serif、serif 和 monospace 这三个默认字体——这将影响大多数 app 内的字体显示。
$ fc-match 'serif'
NotoSerif-Regular.ttf: "Noto Serif" "Regular"
$ fc-match 'sans-serif'
sarasa-ui-sc-regular.ttf: "Sarasa UI SC" "Regular"
$ fc-match 'monospace'
sarasa-mono-sc-regular.ttf: "Sarasa Mono SC" "Regular"
这样,当 app 请求 sans-serif 字体时,系统会提供更纱黑体 UI;app 请求 serif 字体时,系统提供 Noto Serif;而 app 请求 monospace 字体时,系统会提供更纱黑体 Mono。
他山之石
用 fontconfig 治理 Linux 中的字体 —— 双猫 CC
WPS 公式字体
从Windows中提取出这些字体(有几个是几个):
MTCORSVA.TTF
WINGDNG2.TTF
courbi.ttf
couri.ttf
cour.ttf
courbd.ttf
monotypesorts.ttf
Mt Extra Tiger.ttf
mtextra_01.ttf
Symbol Tiger Expert.ttf
symbol.ttf
Symbol Tiger.ttf
wingding.ttf
WINGDNG3.ttf
将这些字体安装到Linux中。
对于 Ubuntu 系统,安装
ubuntu-restricted-extras
这个包来将诸如 Times New Roman 等字体安装进入系统。
这是一种不自由的做法,若对非自由内容有精神洁癖,还请不要这么做。
软件替代相关
目前我使用 Icalingua++,一个第三方的 QQ 协议实现。
网易云音乐
官方网易云 Linux 版本已经年久失修,经常音乐加载不出来。我使用 YesPlayMusic 来替代。
服务器运维相关
免费 RHEL 申请
Red Hat 提供免费的个人开发者订阅,每次订阅时间为 1 年,可以支持最多 16 个 Red Hat Enterprise Linux 实例。RHEL 作为商业 Linux 的标杆存在,其可靠性和稳定性是不言而喻的。
获得免费开发者订阅的步骤如下:
登录 Red Hat 账号。如果没有,就注册一个。
同意条款和条件。
登出所有的 Red Hat 账号,然后等待 15 分钟。这期间将自动分发订阅。
打开 access.redhat.com/management,登录 Red Hat 账号,应该就能看到订阅了。
树莓派相关
使用 OverlayFS 保护树莓派上的 SD 卡
本节适用于 Raspberry Pi OS。若使用 Ubuntu 系发行版或其他 Debian 家族发行版(未尝试),直接使用软件包
overlayroot
就可以了。
一直以来,像树莓派这样的单板微型计算机都是使用 microSD 卡(TF 卡)作为「硬盘」使用。然而,SD 卡,尤其是质量一般的 SD 卡,采用的存储介质往往能接受的擦写次数较低,经受不住 ext4 这种日志式文件系统和 Linux 这种运行过程中产生 log 量非常庞大的操作系统的「折磨」。在使用了两个月之后,我的 Pi 开始出现问题:
前几天,在 Pi 上使用 apt install
安装软件时,意外地出现了大量类似于 dpkg: warning: files list file for package \'xxxx\' missing
这样的错误。一些系统服务当天并没有正常运行(我的树莓派每天会重启),更匪夷所思的是,树莓派的主机名变成了一串意义不明的字符串 iersdes
,经过排查,我初步认定这是 SD 卡出现了坏块导致的一系列问题。
但关于自己的主机名从
hansdepi
变成了iersdes
这件事,我并没有头绪,只是猜测这是由于存储主机名的文件有若干块出错或者丢失所造成的。
今天中午,在经历一番折腾之后,我的树莓派重新恢复了各项服务上线。为了让由于 SD 卡出现「磨损」造成系统宕机的悲剧不再发生,我开始寻找一种更好地保护树莓派上的 SD 卡的方法。使用 OverlayFS 保护 SD 卡,可能是这里面比较容易实现的一种。
什么是 OverlayFS
overlay ['oʊvər.leɪ] n.涂层 v.覆盖
OverlayFS 是一种“堆叠文件系统”,它依赖并建立在其它的文件系统之上,不直接参与磁盘空间结构的划分,仅将原来文件系统中不同目录和文件进行“合并”。关于 OverlayFS 的详细介绍,可以参见这两篇博客:
https://blog.csdn.net/luckyapple1028/article/details/77916194
https://blog.csdn.net/luckyapple1028/article/details/78075358
本文中,我们不对 OverlayFS 做什么细致的探讨,只需要知道我们使用了 OverlayFS 即可。我们将通过 OverlayFS,将 SD 卡挂载到一个只读的目录上,而将 /
挂载为一个由只读的 SD 卡和内存中的 tmpfs 堆叠而成的 OverlayFS。这样实现的效果就是,系统对 SD 卡的所有写入都将在重启之后消失——有如安装了还原卡或者「冰点还原」软件。
禁用交换分区
依次执行:
$ sudo dphys-swapfile swapoff
$ sudo dphys-swapfile uninstall
$ sudo update-rc.d dphys-swapfile remove
重新启动树莓派,使用 free -h
查看 Swap
一栏是否显示均为 0。
配置 overlayRoot 脚本
我们使用这里的脚本来实现自动配置 OverlayFS。
$ sudo nano /etc/overlayRoot.sh
复制粘贴这些代码:
#!/bin/sh
# Read-only Root-FS for Raspian using overlayfs
# Version 1.1
#
# Version History:
# 1.0: initial release
# 1.1: adopted new fstab style with PARTUUID. the script will now look for a /dev/xyz definiton first
# (old raspbian), if that is not found, it will look for a partition with LABEL=rootfs, if that
# is not found it look for a PARTUUID string in fstab for / and convert that to a device name
# using the blkid command.
#
# Created 2017 by Pascal Suter @ DALCO AG, Switzerland to work on Raspian as custom init script
# (raspbian does not use an initramfs on boot)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see
# <http://www.gnu.org/licenses/>.
#
#
# Tested with Raspbian mini, 2018-10-09
#
# This script will mount the root filesystem read-only and overlay it with a temporary tempfs
# which is read-write mounted. This is done using the overlayFS which is part of the linux kernel
# since version 3.18.
# when this script is in use, all changes made to anywhere in the root filesystem mount will be lost
# upon reboot of the system. The SD card will only be accessed as read-only drive, which significantly
# helps to prolong its life and prevent filesystem coruption in environments where the system is usually
# not shut down properly
#
# Install:
# copy this script to /sbin/overlayRoot.sh, make it executable and add \"init=/sbin/overlayRoot.sh\" to the
# cmdline.txt file in the raspbian image\'s boot partition.
# I strongly recommend to disable swapping before using this. it will work with swap but that just does
# not make sens as the swap file will be stored in the tempfs which again resides in the ram.
# run these commands on the booted raspberry pi BEFORE you set the init=/sbin/overlayRoot.sh boot option:
# sudo dphys-swapfile swapoff
# sudo dphys-swapfile uninstall
# sudo update-rc.d dphys-swapfile remove
#
# To install software, run upgrades and do other changes to the raspberry setup, simply remove the init=
# entry from the cmdline.txt file and reboot, make the changes, add the init= entry and reboot once more.
fail(){
echo -e \"$1\"
/bin/bash
}
# load module
modprobe overlay
if [ $? -ne 0 ]; then
fail \"ERROR: missing overlay kernel module\"
fi
# mount /proc
mount -t proc proc /proc
if [ $? -ne 0 ]; then
fail \"ERROR: could not mount proc\"
fi
# create a writable fs to then create our mountpoints
mount -t tmpfs inittemp /mnt
if [ $? -ne 0 ]; then
fail \"ERROR: could not create a temporary filesystem to mount the base filesystems for overlayfs\"
fi
mkdir /mnt/lower
mkdir /mnt/rw
mount -t tmpfs root-rw /mnt/rw
if [ $? -ne 0 ]; then
fail \"ERROR: could not create tempfs for upper filesystem\"
fi
mkdir /mnt/rw/upper
mkdir /mnt/rw/work
mkdir /mnt/newroot
# mount root filesystem readonly
rootDev=`awk \'$2 == \"/\" {print $1}\' /etc/fstab`
rootMountOpt=`awk \'$2 == \"/\" {print $4}\' /etc/fstab`
rootFsType=`awk \'$2 == \"/\" {print $3}\' /etc/fstab`
echo \"check if we can locate the root device based on fstab\"
blkid $rootDev
if [ $? -gt 0 ]; then
echo \"no success, try if a filesystem with label \'rootfs\' is avaialble\"
rootDevFstab=$rootDev
rootDev=`blkid -L \"rootfs\"`
if [ $? -gt 0 ]; then
echo \"no luck either, try to further parse fstab\'s root device definition\"
echo \"try if fstab contains a PARTUUID definition\"
echo \"$rootDevFstab\" | grep \'PARTUUID=\\(.*\\)-\\([0-9]\\{2\\}\\)\'
if [ $? -gt 0 ]; then
fail \"could not find a root filesystem device in fstab. Make sure that fstab contains a device definition or a PARTUUID entry for / or that the root filesystem has a label \'rootfs\' assigned to it\"
fi
device=\"\"
partition=\"\"
eval `echo \"$rootDevFstab\" | sed -e \'s/PARTUUID=\\(.*\\)-\\([0-9]\\{2\\}\\)/device=\\1;partition=\\2/\'`
rootDev=`blkid -t \"PTUUID=$device\" | awk -F : \'{print $1}\'`p$(($partition))
blkid $rootDev
if [ $? -gt 0 ]; then
fail \"The PARTUUID entry in fstab could not be converted into a valid device name. Make sure that fstab contains a device definition or a PARTUUID entry for / or that the root filesystem has a label \'rootfs\' assigned to it\"
fi
fi
fi
mount -t ${rootFsType} -o ${rootMountOpt},ro ${rootDev} /mnt/lower
if [ $? -ne 0 ]; then
fail \"ERROR: could not ro-mount original root partition\"
fi
mount -t overlay -o lowerdir=/mnt/lower,upperdir=/mnt/rw/upper,workdir=/mnt/rw/work overlayfs-root /mnt/newroot
if [ $? -ne 0 ]; then
fail \"ERROR: could not mount overlayFS\"
fi
# create mountpoints inside the new root filesystem-overlay
mkdir /mnt/newroot/ro
mkdir /mnt/newroot/rw
# remove root mount from fstab (this is already a non-permanent modification)
grep -v \"$rootDev\" /mnt/lower/etc/fstab > /mnt/newroot/etc/fstab
echo \"#the original root mount has been removed by overlayRoot.sh\" >> /mnt/newroot/etc/fstab
echo \"#this is only a temporary modification, the original fstab\" >> /mnt/newroot/etc/fstab
echo \"#stored on the disk can be found in /ro/etc/fstab\" >> /mnt/newroot/etc/fstab
# change to the new overlay root
cd /mnt/newroot
pivot_root . mnt
exec chroot . sh -c \"$(cat <<END
# move ro and rw mounts to the new root
mount --move /mnt/mnt/lower/ /ro
if [ $? -ne 0 ]; then
echo \"ERROR: could not move ro-root into newroot\"
/bin/bash
fi
mount --move /mnt/mnt/rw /rw
if [ $? -ne 0 ]; then
echo \"ERROR: could not move tempfs rw mount into newroot\"
/bin/bash
fi
# unmount unneeded mounts so we can unmout the old readonly root
umount /mnt/mnt
umount /mnt/proc
umount /mnt/dev
umount /mnt
# continue with regular init
exec /sbin/init
END
)\"
按 C-X, Y, Return 保存退出。然后,执行 sudo chmod a+x /sbin/overlayRoot.sh
来赋予这个脚本可执行权限。
接着,我们需要将这个脚本配置到树莓派的启动过程中。执行 sudo nano /boot/cmdline.txt
,在那一行的行尾加上:
init=/sbin/overlayRoot.sh
按 C-X, Y, Return 保存退出。这时,我们将系统重启,接下来的系统就处在“还原卡激活”的状态了。可以尝试对系统进行一些改动,例如安装软件、新建一些文件等。重新启动,你会发现这些改动都消失了。
注意:被保护的只有
/dev/mmcblk0p2
,也就是 rootfs,/boot
分区以及树莓派上所接的 U 盘、移动硬盘等都不会被保护。
临时解除保护
如果想要安装软件,需要临时解除保护,请先执行 sudo mount -o remount,rw /ro
以重新挂载 /ro
目录(这个目录是“真正的”SD 卡根分区)。然后,使用 sudo chroot /ro
命令,进入一个以 /ro
为根的 root 环境。在这个环境里,可以安装软件或者修改配置。结束之后,使用 exit
退出这个环境。直到重启之前,都可以再用 sudo chroot /ro
进入可写环境。
永久解除保护
直接编辑 /boot/cmdline.txt
,删除行尾的那句 init=/sbin/overlayRoot.sh
,重启即可。