when someone abandons you,it is him that gets loss because he lost someone who truly loves him but you just lost one who doesn’t love you.
这台机器环境如下:
7.2.3-1-t2-trixiei915tty1因为机器大部分时间作为服务器运行,没有必要让 MacBook 内置屏幕一直亮着
目标有两个:
tty1 长时间没人操作时自动熄屏;Linux virtual console 本身支持 blank timer,可以直接使用 setterm:
1 | # 直接设置 |
上面的设置只需要执行一次,但重启以后需要重新应用,因此创建一个 systemd service
创建 /etc/systemd/system/tty1-blank.service:
1 | [Unit] |
启用:
1 | $ sudo systemctl daemon-reload |
检查:
1 | $ systemctl status tty1-blank.service |
如果屏幕已经自动熄灭,需要从 SSH 手动点亮:
1 | $ sudo sh -c 'TERM=linux setterm --blank poke < /dev/tty1 > /dev/tty1' |
使用 framebuffer 的 FBIOBLANK ioctl:
1 | $ sudo python3 -c 'import os,fcntl; fd=os.open("/dev/fb0",os.O_RDWR); fcntl.ioctl(fd,0x4611,1); os.close(fd)' |
执行后屏幕立即黑掉,按 MacBook 本地键盘,屏幕会自动重新亮起
目标 1 使用 setterm --blank 1 就可以实现,没有遇到什么问题
问题主要出现在实现“立即熄屏”的过程中
setterm --blank force 可以熄屏,但键盘无法唤醒最开始想到的是 setterm 提供的 force:
1 | $ sudo sh -c 'TERM=linux setterm --blank force < /dev/tty1 > /dev/tty1' |
执行以后,屏幕确实立即黑掉
但很快发现:
按 MacBook 本地键盘无法重新点亮屏幕
只能通过 SSH 执行:
1 | $ sudo sh -c 'TERM=linux setterm --blank poke < /dev/tty1 > /dev/tty1' |
才能恢复
这里出现了第一个问题:
为什么 --blank 1 自动熄屏以后键盘可以唤醒,而 --blank force 却不可以?
首先确认 Linux 当前识别到了哪些 input event:
1 | $ grep -H . /sys/class/input/event*/device/name |
event8 和 event10 名字相同,所以进一步确认它们分别是什么设备:
1 | $ udevadm info -q property -n /dev/input/event8 | grep '^ID_INPUT' |
因此监听 event8:
1 | $ sudo evtest /dev/input/event8 |
保持 evtest 运行,再从另一个 SSH 会话执行:
1 | $ sudo sh -c 'TERM=linux setterm --blank force < /dev/tty1 > /dev/tty1' |
等屏幕黑掉后,按本地键盘,此时 evtest 可以看到类似:
1 | Event: time ..., type 1 (EV_KEY), code ..., value 1 |
也就是说:
force 熄屏以后,键盘事件仍然正常进入 Linux input subsystem。
所以问题不是:
1 | 屏幕熄灭 |
而是:
1 | 屏幕熄灭 |
因此可以排除 T2 键盘驱动和键盘设备本身
接着确认当前屏幕实际显示的是哪个 virtual terminal:
1 | $ cat /sys/class/tty/tty0/active |
这排除了另一个可能:
命令操作的是
/dev/tty1,但屏幕实际显示的是其他 VT
Linux framebuffer console 的 sysfs 位于:
1 | $ ls -la /sys/class/graphics/fbcon/ |
这里并没有一些资料中可以看到的 /sys/class/graphics/fbcon/blank,所以这台机器不能通过这个 sysfs 节点直接触发 framebuffer blank
继续查看 graphics 设备:
1 | $ ls -l /sys/class/graphics/ |
这里需要区分 fbcon 和 fb0:
1 | tty1 |
前面已经检查过 fbcon,没有找到可用的 blank sysfs 接口
接下来准备测试的是 framebuffer 的 FBIOBLANK ioctl,因此需要继续检查真正的 framebuffer device,也就是 fb0
1 | # 查看 fb0 由谁提供 |
因此当前显示链路可以简单理解为:
1 | tty1 → fbcon → fb0 (i915drmfb) → i915/DRM → 内置屏幕 |
/dev/fb0 存在,就可以继续尝试直接向 framebuffer 发送 FBIOBLANK ioctl
--blank force 不能当成“立即执行一次普通 blank”这里也是整个问题里最容易误解的地方
setterm 的帮助是:
1 | $ setterm --help | grep -A3 -- '--blank' |
乍一看很容易把:
1 | $ setterm --blank 1 |
和:
1 | $ setterm --blank force |
理解成:
1 | --blank 1 等一分钟再 blank |
实际上两者并不是简单的“延迟”和“立即”的区别
正常 timeout blank 的行为是:
1 | console 空闲 |
而 --blank force 使用的是强制 blank 行为
它不是把 blank timer “快进到现在”,而是要求 console 强制进入 blank 状态
所以实际行为变成:
1 | setterm --blank force |
需要显式:
1 | $ setterm --blank poke |
才能解除
这就解释了之前观察到的差异:
1 | setterm --blank 1 |
因此 --blank force 并不适合“立即熄屏,但之后允许键盘唤醒”这个需求
既然系统存在:
1 | /dev/fb0 |
并且:
1 | fb0 = i915drmfb |
就可以直接向 framebuffer 发 FBIOBLANK ioctl:
1 | $ sudo python3 -c 'import os,fcntl; fd=os.open("/dev/fb0",os.O_RDWR); fcntl.ioctl(fd,0x4611,1); os.close(fd)' |
测试结果:
1 | 执行 FBIOBLANK |
正好满足目标
这也进一步证明了之前的问题不是:
而是 setterm --blank force 本身的语义不符合这个需求
对于没有桌面环境、直接运行 Linux virtual console 的机器,可以把熄屏需求分成两种
使用:
1 | $ sudo sh -c 'TERM=linux setterm --blank 1 < /dev/tty1 > /dev/tty1' |
效果:
1 | 空闲 1 分钟 → 黑屏 → 本地键盘输入 → 自动亮屏 |
通过 systemd oneshot service,可以让它每次开机自动生效
在这台使用 i915drmfb 的机器上:
1 | $ sudo python3 -c 'import os,fcntl; fd=os.open("/dev/fb0",os.O_RDWR); fcntl.ioctl(fd,0x4611,1); os.close(fd)' |
效果:
1 | 执行命令 → 立即黑屏 → 本地键盘输入 → 自动亮屏 |
不要把下面这条命令当成“立即执行一次正常 blank”:
1 | $ sudo sh -c 'TERM=linux setterm --blank force < /dev/tty1 > /dev/tty1' |
如果需要从 SSH 显式点亮屏幕,则可以使用:
1 | $ sudo sh -c 'TERM=linux setterm --blank poke < /dev/tty1 > /dev/tty1' |
be slow to promise and quick to perform.