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 --destorg.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_MIN15 # 用户多久没操作后直接关机单位分钟必须大于预警值 IDLE_SHUTDOWN_MIN20 # 每轮检测间隔单位秒 CHECK_INTERVAL_SEC10 # 白名单进程如果匹配到这些进程则本次不执行关机判断 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 \ --destorg.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 21; 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] DescriptionKylin Auto Shutdown Service ConditionEnvironmentDISPLAY [Service] Typesimple ExecStart/usr/local/bin/kylin-auto-shutdown.sh Restarton-failure RestartSec15 [Install] WantedBydefault.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文件里同时指定EnvironmentDISPLAY: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为主但如果某些高版本环境切换到Waylandxprintidle就失效了。检查会话类型的方法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分钟。运维这行稳比快值钱。