TimothyQiu's Blog

keep it simple stupid

给 Arch 添加 Swap 文件

分类:技术

自从半年前换了 Arch Linux,就一直没有设置 swap,心想最多也就编译的时候 Chrome 标签页「哦哟」一下嘛。

不过实际没有 swap 不能休眠还是有点不踏实,毕竟是台式机,又懒得接 UPS。于是以下就是这周末设置 swap 文件用于休眠的历程(内容 Arch Wiki 上都有提及,就是总结记录一下)。

查看当前 Swap 状态

使用 swapon --show

创建多大的 Swap 文件

Swap 文件的大小一般至少是 512M。如果你和我一样是为了让系统能够休眠,那么可以参考 /sys/power/image_size 里的字节数,如果你没有修改过,这个值默认是当前内存大小的五分之二。

/sys/power/image_size 控制的就是休眠镜像的文件大小。系统会尽可能保证镜像大小不超过这个值,即便无法实现,也会尽量将镜像缩小。也就是说,你给它设 0 也是可以的,此时休眠镜像的大小会是最小的。

创建 Swap 文件

首先在一些文件系统上使用 swap 文件是会有问题的,比如早期的 Btrfs 就不支持。

创建 Swap 文件有很多种方法,但最可移植的方法是使用 dd。比如创建 1G 的 /swapfile

# dd if=/dev/zero of=/swapfile bs=1M count=1024 status=progress
# chmod 0600 /swapfile
# mkswap -U clear /swapfile

这里的 chmod 是为了安全,所有人都可读写的 Swap 文件是巨大隐患。

mkswap-U 参数用于设置 swap 的 UUID。但是因为 swap 文件必须使用文件系统路径去指定,所以这里使用特殊的 clear 作为参数去清空它(实际效果是设置了全零的 UUID)。

打开与关闭

创建好 swap 文件后,就可以直接启用了:

# swapon /swapfile

最后把它放进 /etc/fstab 里,这样每次启动就会直接启用:

/swapfile none swap defaults 0 0

如果是要关闭,顾名思义就:

# swapoff /swapfile

关闭以后它就是个普通文件,想删除就可以直接删除了。

使用 Swap 文件进行休眠

故事讲到这里,休眠和混合睡眠应该就都可以创建休眠镜像了。但我们还需要进行额外设置,才能在启动时使用这个休眠镜像。

内核参数

首先需要为内核设置 resumeresume_offset 参数,告诉它去哪里找休眠镜像。

resume 参数是 swap 文件所在的分区(例如 /dev/nvme0n1p2,或者 UUID=4209c845-f495-4c43-8a03-5363dd433153),可以在 /etc/fstab 里查看,也可以通过 findmnt -no UUID -T /swapfile 获取。

resume_offset 是文件开头在这个分区中的物理偏移量,可以通过 filefrag -v /swapfile 查看。

关于内核参数的设置,如果你和我一样使用的是 GRUB,可以编辑 /etc/default/grub,把 resume=XXX resume_offset=XXX 追加到 GRUB_CMDLINE_LINUX_DEFAULT 里。最后重新生成一下 grub.cfg 即可:

# grub-mkconfig -o /boot/grub/grub.cfg

如果你有闲情逸致,也可以先不这么做。直接在 GRUB 启动界面按 e,然后去手动编辑本次启动所使用的参数 😛

initramfs

如果你的 initramfs 没有使用 systemd 钩子(使用的是 base),那么就还需要添加一个 resume 钩子才会尝试从休眠中恢复:

编辑 /etc/mkinitcpio.conf 文件,在 HOOKS=(...) 里的 udev 之后的任何位置加入 resume。最后重新生成 initramfs 即可:

# mkinitcpio -p linux

参考

Arch Linux 下 Display Manager 黑屏

分类:技术

最近 MBP 电池彻底坏了不想修,换 Windows 用了几天又很不习惯,于是就装了个 Arch。

用 Arch Linux 得自行装图形界面、使用 Display Manager 进行图形化的登录操作。但这时候就遇到个很好玩的事情:进 Display Manager 时黑屏(如果去 BIOS 里绕一下,有小概率不黑屏),不过在黑屏里 Ctrl+Alt+F2 还是可以切到别的 tty 的。

内核参数、驱动之类的检查一圈都没有问题,把我用的 LightDM 换成别的 Display Manager 也还是如此。最后在万能的 Arch Wiki 找到了解决方法,在 /etc/lightdm/lightdm.conf 里配置一下:

[LightDM]
logind-check-graphical=true

黑屏的原因是,图形驱动还没加载完,LightDM 就启动了:系统启动得太快了,得显式让 LightDM 等待图形设备 🤣

VPS 自动备份到邮箱

分类:技术

我已经忘了上次备份服务器是什么时候的事了,只记得前不久因为「以为备份过」做错事然后手忙脚乱了。更考虑到自己邮箱那么多空间浪费着也是浪费着,于是就决定抽空写个脚本定期备份一下。

说是「自动」备份,其实一句话解释就是通过 cron 定期执行脚本,对需要备份的目录使用 tar 打包,然后 sendmail 发到邮箱。

这里的操作系统是 CentOS,其它系统应该也是大同小异。以下便是本次行动的流水帐 :)

Sendmail 相关

因为系统里原生没有安装 sendmail,于是

yum install sendmail   # 安装
service sendmail start # 启动服务
chkconfig sendmail on  # 开启自动启动

因为只想使用发信功能,所以也不用花时间改配置(收信功能的默认配置是只接受本地邮件)。确定防火墙允许 25 端口后,就可以使用下面的命令直接发送邮件了。

# 使用交互模式(使用只包含一个句点的行结束正文并发送)
mail <收件人>
# 命令行直接发送
echo <正文> | mail -s <主题> <发件人>

使用这种方法发出的邮件显示发件人地址为「<用户名>@<主机名称>」。因为 sendmail 只支持纯文本,所以需要借助 uuencode 来编码附件,于是:

# 只包含附件
uuencode <文件路径> <文件显示名称> | mail -s <主题> <收件人>
# 正文和附件
(echo <正文>; uuencode <文件路径> <文件显示名称>) | mail -s <主题> <收件人>

备份脚本

编写 Shell 脚本实现对 Web 目录的备份和发送工作,因为博客使用的是 SQLite 数据库,直接打包即可,不需要先用 mysqldump 导出。(新手上路,表示快被引号什么的弄疯了,于是大多数情况下都为变量加上了引号。另外邮件正文纯属无聊,完全可以不加 = =)

#!/bin/sh

# 备份文件存放目录
BACKUP_DIR="/path/to/backup/directory"

# 备份文件邮件接收地址
BACKUP_MAIL_RECEIVER="example@example.com"

# =====================================

# 保证目录存在
[ ! -d "$BACKUP_DIR" ] &&  mkdir -p "$BACKUP_DIR"

# 需要备份的路径
WEB_DIR="/path/to/web/root"

BACKUP_DATE="`date +%Y-%m-%d`"

BACKUP_FILE_NAME="$BACKUP_DATE.tar.gz"
BACKUP_FILE_FULLNAME="$BACKUP_DIR/$BACKUP_FILE_NAME"

cd $WEB_DIR
    tar czpf "$BACKUP_FILE_FULLNAME" *

BACKUP_MAIL_SUBJECT="VPS Backup: $BACKUP_DATE"
BACKUP_MAIL_MESSAGE=$(
    echo "Sir,";
    echo "";
    echo "Backup file $BACKUP_FILE_FULLNAME created.";
)

(echo "$BACKUP_MAIL_MESSAGE"; uuencode "$BACKUP_FILE_FULLNAME" "$BACKUP_FILE_NAME") \
    | mail -s "$BACKUP_MAIL_SUBJECT" "$BACKUP_MAIL_RECEIVER"

保存后给脚本加上执行权限,就可以直接运行试一下,应该会有邮件发到邮箱了。

chmod u+x <脚本名>

定时执行

定时执行肯定就是 cron 了,这个不用多说,直接 crontab -e 然后添加一行:

0 0 * * 2 /path/to/script

就大功告成鸟~从此,每周二午夜就会有一封谜之信件安静地躺到你的邮箱里……

SSH Escape Sequences

分类:技术

前阵子因为要折腾 VPS 的缘故把博客暂时放在了 @likounin 童鞋的服务器上,不想最近他的服务器出了点问题,于是只能赶紧把 VPS 重新折腾回原样,然后把博客搬回来。

慌忙折腾之中遇到个问题:在 SSH 里卡住了,没法 Ctrl-C(因为这种按键会直接发给远端),该怎么正常退出?

想来也应该有个转义序列可以直接对 SSH 做操作……于是,那就是 ~ 键。

SSH 到服务器后,确保之前按过一次回车,然后按 ~?,就会列出一堆可用的转义序列,比如:

Supported escape sequences:
~. - terminate connection
~B - send a BREAK to the remote system
~C - open a command line
~R - Request rekey (SSH protocol 2 only)
~^Z - suspend ssh
~# - list forwarded connections
~& - background ssh (when waiting for connections to terminate)
~? - this message
~~ - send the escape character by typing it twice
(Note that escapes are only recognized immediately after newline.)

总之,要退出已经卡住的 SSH 程序,可以在按一次回车(见上面帮助信息的最后一行)之后,按下 ~.

话说回来,转义字符也只是默认是这个 ~ 字符而已,如果不喜欢的话,ssh_config 文件中有一个选项叫 EscapeChar 可以修改转义字符。

以上。

睡眠排序算法

分类:技术

4chan 上有个家伙发帖说他发明了一种很牛叉的排序算法:睡眠排序(Sleep Sort):

#!/bin/bash
function f() {
    sleep "$1"
    echo "$1"
}
while [ -n "$1" ]
do
    f "$1" &
    shift
done
wait

主要就是对输入的每一个数都新开一个进程,进程里用这个数进行倒数,倒数到0就输出这个数。于是较小数就先输出、较大数后输出。睡排成功~

当然啦群众的眼睛是雪亮的,纷纷指出这个会存在竞态条件,而且如果的数比较大会很悲催(比如要排的数字里有86400的话,那么至少要等86400秒,也就是一整天 =。=),时间复杂度是 O(最大的那个数)……

在这个欢乐的帖子里还看到了各种其它语言对睡眠排序的实现,包括一个 Lua 的(#165):

#!/usr/bin/env lua
function sleepsort(arr)
    local res, thread = {}, {}
    local nthreads = #arr

    for i = 1, #arr do
        thread[i] = coroutine.create(function()
            for n=arr[i], 0, -1 do coroutine.yield() end
            nthreads = nthreads - 1
            thread[i] = nil
            res[#res+1] = arr[i]
        end)
    end
    while nthreads > 0 do
        for i = 1, #arr do
            if thread[i] then coroutine.resume(thread[i]) end
        end
    end
    return res
end

math.randomseed(os.time())
local arr = {}
for i = 1,10 do arr[i] = math.random(1,99) end
print(unpack(sleepsort(arr)))

嗯哼~ Lua 的 coroutine 还是很强大滴(<ゝω·)

p.s. 终于还是见识到了最无厘头撞大运的 Bogo 排序算法

while not inOrder(deck) do
    shuffle(deck);