1. 先从一次真实的下班场景说起:为什么我盯上了“空闲自动关机”
大概两个月前,单位一台跑银河麒麟V10的办公电脑出了问题。白天业务正常,晚上有人用这台机器做批量数据处理,处理到一半人走开了,屏幕还亮着,机器也没有关机。第二天上班发现机箱热得能煎鸡蛋,系统日志里全是昨晚风扇转速拉满的记录。最关键的是,批量任务跑完之后还有一堆临时文件没有清理,第二天再处理数据时,磁盘差点被占满。
这种问题在国产系统运维里太常见了。银河麒麟这些年铺到的地方越来越多,政务窗口、学校机房、医院科室、企业办公区,处处都是它的身影。和普通个人电脑不一样,这些场景里经常是“人走机不停”:数据导完人没回来、视频渲染到凌晨、下载任务没人守着、机房公用电脑下课之后还在开机。作为运维,你不可能天天晚上蹲在机房里按关机键,也不可能指望每个值班同事都记住“今天下班前把机器关掉”。
那这时候最需要的是什么?是一个能让电脑“自己判断有没有人用完了”的免费小工具,一旦发现一段时间内没人操作,就自动关机。不需要额外买授权,不需要让使用者学一堆新操作,更不需要靠人工巡查。我后来把这个工具整理成了一个小脚本,在银河麒麟V10的x86_64和aarch64两种架构上都实测跑通了,这篇文章就是完整的运维笔记。
需要先说清楚,这不是新造轮子。Linux本身有电源管理,系统自带定时关机,就连银河麒麟桌面也有“不活动时休眠”之类的选项。但为什么最后还是自己搞了个小工具?因为“用完自动关机”这个需求,和“定时关机”“休眠”完全是两回事。稍等,我一个个拆开讲。
这句话建议读几遍:自动关机的本质不是“到时间关机”,而是“判断这台电脑现在处于无人使用状态”。只要理解了这一点,后面所有设计就顺了。
2. 系统自带方案为什么撑不起这个场景:休眠、定时关机都不对味
先说大家最容易想到的:用银河麒麟桌面自带的电源管理,设置多少分钟不操作就休眠或挂起。这个方法能管住一部分机器,但不适合当作通用运维方案。
第一,休眠和关机是两回事。机器休眠之后,CPU停了,内存数据还在,部分外设可能还带电。远程想重新唤醒一台休眠中的机器,很多时候做不到——尤其在没有配置好Wake-on-LAN的网络环境里,运维得到现场按电源键。用户第二天上班看到的是“黑屏机器”,下意识以为是故障,实际只是挂起。在窗口单位,这种情况很要命。
第二,桌面电源管理策略经常被用户自己改掉。很多人嫌屏幕老是自动锁,就去设置里把“空闲多少分钟休眠”改成“从不”。这一改,整台机器晚上就进入无限待机状态,散热、电费、安全风险全回来。运维不可能每周去每台电脑上检查一遍电源策略。
第三,更硬伤的是“定时关机”这个思路。有同事提过:干脆写个crontab,每天晚上十一点关机,不就行了?晚上十一点之前还在跑任务怎么办?跑到一半被你强行断电,轻则数据没保存,重则数据库文件损坏。运维圈里被定时关机任务搞崩过的生产机,真的不止一台。定时关机没有“感知使用状态”的能力,它是无脑执行,不是智能判断。
而“用完自动关机”的任务形态是:不只考勤时间,不强制某个点断电,而是在确认“一段时间内没有任何输入操作”之后,再给用户留出缓冲时间,最后优雅关机。这正好是桌面环境能给数据,但crontab给不了能力的地方。
既然系统自带方案存在这些缺口,那就需要一个轻量、免费、不依赖复杂框架的小工具。我把这个工具直接叫作kylin-auto-shutdown,其实核心东西就三样:X11空闲时间检测、一个bash循环、一个系统关机动作。下面把这套机制的原理讲明白。
3. 小工具的核心原理:X11空闲时间检测 + 二次预警 + 优雅关机
3.1 关键依赖:xprintidle到底在干什么
xprintidle是一个极小的命令行工具,运行之后会返回当前用户没有任何键盘和鼠标操作的毫秒数。它不是靠轮询输入事件硬算,而是直接读取X Server里的idle time计数器信息。简单来说,只要用户按了一下键盘、动了一下鼠标,这个数值就会归零,然后从零重新计时。后台跑什么程序、窗口播放什么内容,都不影响这个计数器——因为X Server只认物理输入设备事件。
在银河麒麟V10桌面默认的X11会话里,这个工具可以直接用。验证方式很简单:
xprintidle终端里如果输出类似0,表示当前刚刚有过操作。过几十秒不碰任何键鼠,再执行一次,返回的数字会变成几千,也就是毫秒数。把它除以1000就是空闲秒数。
这里要强调一下,银河麒麟V10目前大部分桌面环境走的是X11,而不是Wayland。如果你的机器碰巧跑的是Wayland会话,xprintidle是读不到空闲时间的。后面我会在排错部分专门提这个坑。
3.2 判断逻辑:从“空闲多久”到“执行关机”的完整链路
这个工具的逻辑可以概括成五步:
- 第一步:每隔若干秒读取一次空闲毫秒数,转换成秒。
- 第二步:如果空闲时长小于用户设定的“预警阈值”,说明电脑有人正在使用,不干预。
- 第三步:如果空闲时长超过“预警阈值”,用右下角的通知气泡提醒用户:本机即将自动关机,如果还在使用请动一下鼠标。
- 第四步:设置一个“强制关机阈值”,比如预警后再过5分钟。如果空闲时长继续增长,突破了这个阈值,说明用户一直没回来,那就不再等了,直接触发关机。
- 第五步:只要用户在通知发出之后动了键鼠,空闲计数立刻归零,预警状态自动解除,关机计划自动作废。
整个判断只看“当前是否无人操作”,不需要进程残留状态。所以写成一个死循环脚本就能搞定,没有复杂的并发状态管理。
3.3 播放视频和下载任务这种“非输入型占用”怎么处理
这里有一个躲不开的边界情况:用户在电脑上放着视频,人却躺在沙发上睡着了。视频在播放,CPU有负载,网络有流量,但是键盘鼠标的idle time一直在累加。如果阈值设得很短,比如5分钟,电脑就把放着视频的机器给关了。
对运维而言,最稳妥的做法是在小工具里加一个“活跃进程白名单”机制。用户可以通过配置文件,把自己正在使用的播放器、下载器、渲染程序的进程名加进去。脚本每轮检测时,如果发现这些进程存在,就认为机器不处于“用完”状态,自动跳过本次关机判断。
这个设计看起来简单,但很实用。单位里有人晚上挂机下大文件,加一个aria2/wget的进程名进去,就不会被误杀;有人用浏览器看教学视频,把chrome/firefox加进去,也能安全绕过。配置文件我会在部署部分给出来。
3.4 关机动作:为什么用DBus调login1而不是直接敲shutdown
脚本里最后一个关键环节就是真正执行关机操作。很多人在Linux上第一反应是sudo shutdown now或者sudo systemctl poweroff。这在手动操作时没问题,但放到无人值守的脚本里会碰到一个障碍:sudo要密码,而桌面上弹出的授权框如果没人点,关机就卡住了。
我实测下来比较靠谱的方式是走org.freedesktop.login1的DBus接口。这个接口是系统服务,桌面会话的用户本身有登录会话权限,配合桌面环境的polkit策略,可以弹出简洁的关机授权确认;在很多允许普通会话直接关机的默认配置下,甚至可以免密执行。命令长这样:
dbus-send --system --print-reply --dest=org.freedesktop.login1 \ /org/freedesktop/login1 org.freedesktop.login1.Manager.PowerOff boolean:true如果单位机器要求完全无人值守、不允许弹任何确认框,可以把关机的DBus调用做成专门的polkit规则,放行当前用户的PowerOff操作。这块不太复杂,我放在第4节部署部分一起说。
4. 银河麒麟V10上部署免费小工具:x86_64和aarch64都通用
4.1 安装依赖:麒麟系统装小工具的方式很直接
银河麒麟V10的生态虽然是国产Linux体系,但底层的包管理沿用了Debian风格,用apt安装工具非常自然。这个自动关机小工具只依赖一个外部程序xprintidle,安装命令:
sudo apt update sudo apt install xprintidle如果你的机器处于内网隔离环境,无法直接上网,可以在一台能联网的机器上先下载xprintidle的deb包,再拷贝到目标机器上用sudo dpkg -i xxxx.deb安装。其实这个依赖很小,不牵扯到一堆关联包,离线部署非常方便。
装完之后顺手验证一下,确保不是软件包里没有的可执行文件:
which xprintidle4.2 部署脚本本体:kylin-auto-shutdown.sh
我把整个工具的主要逻辑都放在一个bash脚本里。考虑到银河麒麟V10用户里既有纯桌面办公的,也有搞二次开发、跑内网服务的,脚本默认参数做成可配置的,预警阈值、强制关机阈值、检测间隔、活跃进程白名单都能改。
下面是完整脚本。建议保存为/usr/local/bin/kylin-auto-shutdown.sh。
#!/usr/bin/env bash # kylin-auto-shutdown.sh # 银河麒麟V10 空闲自动关机小工具 # 原理:xprintidle 读取X11空闲时间,超过阈值使用 login1 DBus 接口关机 # ===== 可配置区域 ===== # 用户多久没操作后弹出预警通知(单位:分钟) IDLE_WARN_MIN=15 # 用户多久没操作后直接关机(单位:分钟),必须大于预警值 IDLE_SHUTDOWN_MIN=20 # 每轮检测间隔(单位:秒) CHECK_INTERVAL_SEC=10 # 白名单进程,如果匹配到这些进程则本次不执行关机判断 ACTIVE_PROCESSES=("vlc" "mpv" "chrome" "firefox") # ====================== IDLE_WARN_SEC=$((IDLE_WARN_MIN * 60)) IDLE_SHUTDOWN_SEC=$((IDLE_SHUTDOWN_MIN * 60)) log_info() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" } shutdown_now() { log_info "空闲时间超限,开始调用系统关机接口" dbus-send --system --print-reply \ --dest=org.freedesktop.login1 \ /org/freedesktop/login1 \ org.freedesktop.login1.Manager.PowerOff boolean:true } check_active_process() { for proc in "${ACTIVE_PROCESSES[@]}"; do if pgrep -x "$proc" >/dev/null 2>&1; then return 0 fi done return 1 } log_info "kylin-auto-shutdown 已启动" log_info "预警阈值: ${IDLE_WARN_MIN} 分钟 / 关机阈值: ${IDLE_SHUTDOWN_MIN} 分钟" while true; do idle_ms=$(xprintidle 2>/dev/null) if [ -z "$idle_ms" ]; then log_info "xprintidle 读取失败,可能不是X11会话" sleep "$CHECK_INTERVAL_SEC" continue fi idle_sec=$((idle_ms / 1000)) if check_active_process; then log_info "检测到活跃进程,本轮跳过" sleep "$CHECK_INTERVAL_SEC" continue fi if [ "$idle_sec" -ge "$IDLE_SHUTDOWN_SEC" ]; then log_info "本机已空闲 ${idle_sec} 秒,触发自动关机" shutdown_now sleep 15 exit 0 fi if [ "$idle_sec" -ge "$IDLE_WARN_SEC" ]; then notify-send "自动关机提醒" "本机已经空闲超过 $IDLE_WARN_MIN 分钟,将在 $IDLE_SHUTDOWN_MIN 分钟无操作后自动关机。如果仍在使用,请移动鼠标或键盘片刻。" log_info "已发送预警通知,当前空闲 ${idle_sec} 秒" fi sleep "$CHECK_INTERVAL_SEC" done给脚本加上执行权限:
sudo chmod +x /usr/local/bin/kylin-auto-shutdown.sh脚本里ACTIVE_PROCESSES配置了vlc、mpv、chrome、firefox这几个常见进程。你可以按需修改,比如单位有人跑国产播放器App,把进程名加进数组即可。对bash数组不熟的同学要注意:元素之间用空格分隔,不要加逗号。
4.3 用systemd用户服务实现开机自启
脚本要随桌面启动跑起来,最省心的是systemd用户服务。在银河麒麟V10里,桌面用户登录后,用户级别的systemd实例会自动启动,配置起来和系统服务差不多。
创建一个用户服务文件:
mkdir -p ~/.config/systemd/user vim ~/.config/systemd/user/kylin-auto-shutdown.service写入以下内容:
[Unit] Description=Kylin Auto Shutdown Service ConditionEnvironment=DISPLAY [Service] Type=simple ExecStart=/usr/local/bin/kylin-auto-shutdown.sh Restart=on-failure RestartSec=15 [Install] WantedBy=default.target然后启用并启动服务:
systemctl --user daemon-reload systemctl --user enable kylin-auto-shutdown.service systemctl --user start kylin-auto-shutdown.service检查运行状态:
systemctl --user status kylin-auto-shutdown.service journalctl --user -u kylin-auto-shutdown.service -f这里有一个容易踩的坑:用户服务的环境变量继承自用户会话,但DISPLAY必须在场。如果脚本通过ssh方式测试,很可能发现服务启动后xprintidle读不到值,因为DISPLAY是空。可以在service文件里同时指定:
Environment=DISPLAY=:0银河麒麟V10本地桌面上,多数情况是:0,但也可能是:1。以实际桌面会话为准。如果拿不准,可以在终端里执行echo $DISPLAY来看。
4.4 无人值守关机:polkit策略与测试
默认情况下,通过org.freedesktop.login1的PowerOff接口关机时,logged-in用户是有权限的,可能会弹一次确认框。在有人值守的地方,弹框完全没问题,用户看到“是谁想关机”还能手动确认。
但如果是机房批量部署,希望晚上到点直接断电关机,没人去点确认框,就需要写一条polkit规则放行。在/etc/polkit-1/rules.d/49-kylin-auto-shutdown.rules里写入:
polkit.addRule(function(action, subject) { if (action.id === "org.freedesktop.login1.power-off" || action.id === "org.freedesktop.login1.power-off-multiple-sessions") { var active = subject.active; if (active && subject.isInGroup("kylinadmin")) { return polkit.Result.YES; } } });再把自己要放行的账号加入kylinadmin组:
sudo usermod -aG kylinadmin 你的用户名这样DBus发起关机请求时,polkit直接允许,不再弹框。如果不想为此单独建组,也可直接用subject.user === "xxx"指定用户名。
测试步骤建议这样走:
- 先把阈值临时调小,比如预警1分钟、关机2分钟。
- 重启服务,别碰键鼠,观察日志。
- 在第二次预警后,动一下鼠标,确认不会继续触发关机。
- 再次不碰键鼠,直到执行关机。
你可能想知道怎么开机测试、会不会把自己关掉。可以先把服务停掉,或者只保留预警、把关机阈值改得极大。实际操作中,我一般先用手动执行shutdown_now函数的方式验证DBus权限,再整体测流程。
5. 排错记录与经验补充:麒麟系统里跑自动化小工具,这几个坑最常见
5.1 xprintidle一直输出0,服务看起来没反应
最常见的原因是环境变量问题。脚本服务经由systemd用户服务启动时,继承的是服务管理器环境,不是桌面终端的真实环境。如果DISPLAY没有落到服务进程里,xprintidle连不上X Server,有的版本会直接输出0,有的会报错。
解决方案就是修改用户服务文件,指定DISPLAY。建议同时把XAUTHORITY也带上,防止在部分桌面会话里连X授权都失败。
5.2 有用户反映播放视频被自动关机
白名单能解决大部分情况。但要注意,pgrep -x匹配的是精确进程名,不是路径。像Chromium浏览器打开的多个进程,进程名是chrome,这个没问题;某些国产播放器进程名可能是mplayerc、gjpg,需要你到任务管理器里查清楚再填。
也可以把匹配方式从-x改成-f,通过完整命令行匹配,但误匹配的概率也会上升。比如填了python,系统里任何python进程都会让白名单生效,到时候这台电脑可能就一直不关机了。建议还是坚持精确进程名。
5.3 Wayland会话下不能用
银河麒麟V10默认桌面以X11为主,但如果某些高版本环境切换到Wayland,xprintidle就失效了。检查会话类型的方法:
echo $XDG_SESSION_TYPE如果是wayland,这个工具就用不了。别急着换方案,先看单位机器是不是真的切了Wayland。大部分国产软硬件兼容性要求下,桌面还是以X11为主。如果一定要支持Wayland,那需要换用idle检测的其他DBus接口,复杂度会上来,这超出了“免费小工具”的定位。我的建议是保持X11,系统默认就是它,不要改。
5.4 aarch64架构下依赖包不好装
银河麒麟V10的ARM版本,软件源里可能没有xprintidle的现成包。这很常见。解决办法有两个:一个是在软件源中加上合适的存档库配置项再update;另一个是直接从源码交叉编译。
我个人更推荐优先找软件包,因为xprintidle是个老牌小工具,各种架构的deb包在网上都能找到,离线拷贝安装最省事。如果离线包也找不到,那就得用gcc编译,但它依赖X11的库,在ARM机器上需要有对应的libX11开发包。这个过程本身是可行的,但不到万不得已不建议在现场折腾,浪费时间。
5.5 自动关机后重启,发现自己忘了保存文档
这是“强制关机”类工具的天然风险。你可以在脚本里加一道防线:关机前检查有没有正在运行的办公软件,比如WPS、LibreOffice,如果有,就跳过本次关机并记录日志。这个逻辑和前面白名单机制类似,但方向相反。默认白名单是“存在即不关”,办公软件也一样,直接加进ACTIVE_PROCESSES数组即可。
从经验看,单位里的用户很少会在电脑前连续坐20分钟不动。一旦真的触发强制关机,大概率人已经走远,把文档保存这个大前提留给用户自己掌握就行,运维侧能做的只是多给一次通知和多留一点缓冲。
6. 我实际用了两个月之后的一些后续想法
这工具我现在跑在办公室三台银河麒麟机器上,一台老款飞腾CPU的台式机,一台新的x86平台一体机,还有一台笔记本。飞腾那台的ARM环境,软件源里确实没有xprintidle,我是直接把x86这边下好的deb包转成了ARM包再装上的,运行稳定。总之这个方案在目前阶段是可复制的。
两个多月的使用里,唯一一次误关机发生在同事用平板投屏到电视,人一直在旁边看屏幕,但没碰电脑键盘鼠标。他的播放器用的进程名我漏了,被脚本逮住,在通知时限之后关了机。后来我在白名单里加上了对应的播放进程,再没出过问题。也是这次之后我才明白,这类工具的“误判”不可避免,但重点是要给用户留出足够的预警和取消时间,并让白名单配置足够灵活。
如果你们单位的机器是多人共用,还可以让这个脚本在预警通知里带上当前机器名,这样群里收到截图时能快速定位是哪台机。简单加一行hostname就行。
最后分享一个很土但很有用的习惯:刚上线这个工具时,别把第一个阈值设得比平时使用习惯更短。先设置成预警30分钟、关机40分钟,观察一周日志,再慢慢收紧到预警15分钟、关机20分钟。运维这行,稳比快值钱。