自从半年前换了 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 文件进行休眠
故事讲到这里,休眠和混合睡眠应该就都可以创建休眠镜像了。但我们还需要进行额外设置,才能在启动时使用这个休眠镜像。
内核参数
首先需要为内核设置 resume
和 resume_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
参考
最近 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 等待图形设备 🤣
我已经忘了上次备份服务器是什么时候的事了,只记得前不久因为「以为备份过」做错事然后手忙脚乱了。更考虑到自己邮箱那么多空间浪费着也是浪费着,于是就决定抽空写个脚本定期备份一下。
说是「自动」备份,其实一句话解释就是通过 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
就大功告成鸟~从此,每周二午夜就会有一封谜之信件安静地躺到你的邮箱里……
前阵子因为要折腾 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);