1. 为什么要对麒麟系统做服务精简
很多人拿到预装麒麟系统的机器后,第一反应是“能用就行”,直到某天发现开机要一分半、内存莫名其妙被吃掉一大块、后台还总有不明进程在跑,才开始琢磨是不是系统本身太臃肿了。其实问题往往不在系统本身,而在于默认开启了一大堆普通用户根本用不到的服务。这些服务有些是给特定硬件准备的,有些是给企业级场景预留的,还有些纯粹是历史遗留——它们安安静静躺在后台,单个占用不大,但十几个叠加起来,对老机器或者资源受限的环境就是实打实的负担。
所谓“禁用不必要的服务”,核心思路就一句话:只保留维持系统基本运行和你实际工作所需的那些,其余全部关掉。这件事听起来简单,但真动手时会遇到几个现实问题:哪些算“必要”?关错了会不会导致系统起不来?禁用和停止有什么区别?我该用 systemctl 还是别的工具?这些问题不搞清楚,很容易从“优化”变成“搞崩”。
这篇文章面向的是有一定 Linux 基础、正在使用麒麟系统(无论是桌面版还是服务器版)的运维人员或技术爱好者。我会把整个服务精简的完整思路、判断标准、实操步骤、参数选择依据,以及我自己踩过的坑,全部摊开讲清楚。你照着做,至少能省下 30% 到 50% 的常驻内存,开机速度也会有肉眼可见的提升。更重要的是,你会建立起一套自己的判断逻辑,以后遇到任何 Linux 系统都能套用。
2. 动手之前必须搞清楚的底层逻辑
2.1 麒麟系统的服务管理机制到底怎么回事
麒麟系统底层用的是 systemd 作为初始化系统和服务管理器,这一点和大多数现代 Linux 发行版一致。systemd 的核心概念是unit,每个服务、挂载点、定时任务都是一个 unit。跟服务最相关的是.service文件,它们通常放在/usr/lib/systemd/system/和/etc/systemd/system/这两个目录下。前者是软件包安装时自带的,后者是管理员自定义或者覆盖用的。
这里有个关键点很多人会忽略:systemd 的服务状态和“是否开机自启”是两回事。一个服务可以当前正在运行,但开机不会自动启动;也可以当前没运行,但开机时会自动拉起来。所以“禁用”这个词在 systemd 语境下其实对应两个不同操作:
systemctl stop xxx:立刻停止当前运行的服务,但下次开机还会启动。systemctl disable xxx:取消开机自启,但当前如果正在运行,它不会停。systemctl disable --now xxx:两个一起做,既停止又取消自启。
我见过太多人只做了 disable 就以为万事大吉,结果当前会话里服务还在跑,内存该占还是占。所以正确的做法是disable 和 stop 同时执行,用--now参数一步到位。
2.2 哪些服务算“必要”,哪些算“可有可无”
这是整个精简过程中最难的部分,因为“必要”是相对的。对一台跑数据库的服务器来说,网络管理服务是命根子;对一台只用来写文档的桌面机来说,蓝牙服务可能一辈子用不上。所以我不打算给你一个万能清单,而是给你一套判断方法。
第一类:绝对不能动的核心服务。这类服务停了系统直接出问题,比如systemd-journald(日志)、systemd-udevd(设备管理)、dbus(进程间通信)、NetworkManager或systemd-networkd(网络)。这些你连查都不用查,直接跳过。
第二类:取决于使用场景的服务。比如bluetooth(蓝牙)、cups(打印)、avahi-daemon(局域网设备发现)、ModemManager(移动宽带)。如果你确定不用这些功能,关掉完全没问题。我自己的桌面机上这几个全关了,从来没出过事。
第三类:大概率用不到但容易误判的服务。比如firewalld(防火墙)、sshd(远程登录)、chronyd(时间同步)。防火墙关了确实省一点资源,但安全风险你自己掂量;sshd 如果你不需要远程登录可以关,但关了之后想远程连都连不上;时间同步服务占用极小,关了可能导致时间漂移,反而带来麻烦。这类服务我的建议是保留,因为它们带来的收益远大于那一点点资源节省。
第四类:明显冗余的服务。比如各种getty(虚拟终端)、postfix(邮件服务,桌面机根本用不到)、rpcbind(NFS 相关,不用 NFS 就关)、abrt(自动错误报告)。这些是精简的主要目标。
2.3 精简之前必须做的安全措施
在动手之前,有一件事比什么都重要:确保你有退路。我吃过一次亏,把某个看起来无关紧要的服务关了,结果图形界面起不来,又因为没留后路只能重装。从那以后我养成了三个习惯:
第一,操作前先做快照或备份。如果是虚拟机,直接打快照,出问题秒回滚。如果是物理机,至少把/etc/systemd/system/目录备份一份,命令是cp -r /etc/systemd/system/ /root/systemd-backup-$(date +%Y%m%d)。
第二,保留一个 root 终端不关。你在图形界面里操作的时候,万一关了某个服务导致桌面崩溃,至少还有一个 tty 可以进去救。按Ctrl+Alt+F2切到第二个虚拟终端,用 root 登录,就能把服务重新启用。
第三,每次只改一两个服务,改完重启验证。不要一口气关二十个然后重启,出了问题你根本不知道是哪个导致的。我一般是一次关三到五个,重启确认没问题再继续下一批。
提示:如果你是在远程 SSH 连接下操作,千万不要禁用
sshd和网络相关服务,否则你会直接失联,只能去机房插显示器。
3. 服务排查与筛选的完整实操流程
3.1 先把当前运行的服务全部列出来
第一步永远是摸清现状。systemd 提供了几个非常实用的命令,我按使用频率从高到低排列:
# 列出所有正在运行的服务,按内存占用排序 systemctl list-units --type=service --state=running --no-pager # 列出所有已启用的服务(开机自启的) systemctl list-unit-files --type=service --state=enabled --no-pager # 查看某个服务的详细信息,包括依赖关系 systemctl status 服务名 # 查看服务占用的内存 systemd-cgtop -msystemctl list-units这条命令输出的信息量很大,我一般会配合grep过滤。比如我想看看有哪些跟打印相关的服务在跑:
systemctl list-units --type=service --state=running --no-pager | grep -i -E "cups|print|avahi|bluetooth"还有一个更直观的方式是用systemd-analyze看启动耗时:
# 查看整体启动时间 systemd-analyze # 查看每个服务的启动耗时排名 systemd-analyze blame | head -30systemd-analyze blame这个命令特别有用,它会按耗时从高到低列出所有服务。我实测下来,麒麟桌面版默认启动时,NetworkManager-wait-online.service经常排在前三,这个服务的作用是等待网络完全就绪,对桌面机来说完全可以禁用,能省好几秒开机时间。
3.2 用依赖关系判断服务能不能关
光看服务名字有时候判断不准,因为很多服务之间存在依赖关系。你关了一个,可能连带影响另一个。systemd 提供了查看依赖的命令:
# 查看某个服务依赖了谁(它需要谁才能运行) systemctl list-dependencies --reverse 服务名 # 查看谁依赖了这个服务(关了它会影响谁) systemctl list-dependencies 服务名举个例子,假设我想关掉avahi-daemon,先查一下谁依赖它:
systemctl list-dependencies avahi-daemon.service如果输出里只有它自己,那就可以放心关。如果发现cups依赖它,而你又需要打印功能,那就得斟酌一下。
我自己的经验是,大部分独立服务之间的依赖关系并不复杂,真正需要小心的是那些被多个服务共同依赖的基础组件,比如dbus、systemd-logind这些。判断方法很简单:如果一个服务被三个以上的其他服务依赖,那它大概率是基础设施,不要动。
3.3 我实际精简掉的服务清单与理由
下面这张表是我在一台麒麟桌面版机器上实际禁用掉的服务,以及每个服务的用途和禁用理由。注意,这不是让你照抄的清单,而是给你一个判断参考。
| 服务名 | 用途 | 禁用理由 | 风险等级 |
|---|---|---|---|
cups.service | 打印服务 | 没有打印机 | 低 |
cups-browsed.service | 网络打印机发现 | 同上 | 低 |
avahi-daemon.service | 局域网设备发现 | 不需要发现局域网设备 | 低 |
bluetooth.service | 蓝牙 | 台式机没有蓝牙 | 低 |
ModemManager.service | 移动宽带管理 | 没有移动网卡 | 低 |
postfix.service | 邮件传输 | 桌面机不发邮件 | 低 |
abrtd.service | 自动错误报告 | 不需要上报 | 低 |
abrt-journal-core.service | 错误报告日志 | 同上 | 低 |
rpcbind.service | NFS 端口映射 | 不用 NFS | 低 |
nfs-client.target | NFS 客户端 | 同上 | 低 |
NetworkManager-wait-online.service | 等待网络就绪 | 拖慢开机,桌面机不需要 | 中 |
getty@tty2.service到getty@tty6.service | 虚拟终端 | 只保留 tty1 | 中 |
kdump.service | 内核崩溃转储 | 桌面机不需要 | 中 |
firewalld.service | 防火墙 | 视安全需求决定 | 高 |
sshd.service | 远程登录 | 不需要远程连接 | 高 |
关于getty服务我要多说一句。默认情况下麒麟系统会开六个虚拟终端(tty1 到 tty6),每个都跑一个 getty 进程等着你登录。但实际上你最多用一两个,剩下的纯属浪费。我一般只保留 tty1(图形界面)和 tty2(应急用),其余全部禁用。每个 getty 大概占 1 到 2 MB 内存,六个就是 10 MB 左右,虽然不多,但积少成多。
kdump这个服务也值得说一下。它的作用是在内核崩溃时把内存内容转储到磁盘,方便事后分析。对服务器来说这很重要,但对桌面机来说,内核崩溃的概率极低,而且转储文件动辄几个 GB,还不如直接重启。关掉它能省下不少内存预留空间。
4. 禁用服务的标准操作与参数详解
4.1 单服务禁用的标准命令组合
对于确认要禁用的服务,我推荐的标准操作是:
# 一步到位:停止并禁用 sudo systemctl disable --now 服务名.service # 验证状态,确认已停止且已禁用 systemctl status 服务名.service systemctl is-enabled 服务名.servicesystemctl status的输出里,如果看到Active: inactive (dead)说明已经停止,看到Loaded: ...; disabled说明已经取消自启。两个都满足才算真正禁用成功。
有时候你会遇到一种情况:disable执行了,但status里显示的还是enabled。这通常是因为有另一个服务或 target 依赖它,把它又拉起来了。这时候需要用mask命令:
# mask 比 disable 更彻底,它会创建一个指向 /dev/null 的符号链接 sudo systemctl mask 服务名.servicemask的效果是这个服务彻底无法启动,连手动启动都不行。除非你unmask,否则它永远起不来。我一般只在遇到顽固服务时才用 mask,因为它的副作用比较大,容易忘记自己屏蔽过什么。
4.2 批量禁用的脚本化方法
如果你要禁用的服务比较多,一个个敲命令太慢,可以写个简单的脚本。我常用的做法是把服务名写到一个文本文件里,然后循环处理:
#!/bin/bash # 批量禁用服务脚本 # 用法:将服务名每行一个写入 service-list.txt,然后执行此脚本 SERVICE_LIST="service-list.txt" LOG_FILE="disable-services-$(date +%Y%m%d-%H%M%S).log" while IFS= read -r service; do # 跳过空行和注释行 [[ -z "$service" || "$service" =~ ^# ]] && continue echo "正在处理: $service" | tee -a "$LOG_FILE" if systemctl list-unit-files --type=service | grep -q "^${service}"; then sudo systemctl disable --now "$service" 2>&1 | tee -a "$LOG_FILE" echo "---" | tee -a "$LOG_FILE" else echo "服务不存在,跳过: $service" | tee -a "$LOG_FILE" fi done < "$SERVICE_LIST" echo "处理完成,日志保存在: $LOG_FILE"这个脚本有几个细节值得注意。第一,它会先检查服务是否存在,避免因为拼写错误报一堆无意义的错。第二,它把操作过程记录到日志文件,方便事后回溯。第三,它跳过了空行和以#开头的注释行,这样你可以在列表文件里写注释。
注意:批量操作有风险,建议第一次运行时先注释掉
disable --now那行,改成echo只打印不执行,确认列表没问题后再真正执行。
4.3 禁用后如何验证效果
禁用完一批服务后,怎么知道有没有效果?我一般从三个维度验证:
内存占用对比。用free -h看总内存变化,用systemd-cgtop -m看具体哪些服务还在占内存。我实测下来,精简十几个服务后,常驻内存能从 1.2 GB 降到 700 MB 左右,效果还是很明显的。
开机时间对比。用systemd-analyze看总启动时间,用systemd-analyze blame看排名变化。我之前那台机器从 48 秒降到了 26 秒,主要贡献就是NetworkManager-wait-online和几个 getty。
运行状态确认。用systemctl --failed看有没有服务启动失败。如果有 failed 的服务,说明可能关错了东西,需要排查。
# 查看所有启动失败的服务 systemctl --failed # 查看系统整体状态 systemctl status如果systemctl --failed输出为空,说明系统运行正常,没有因为你的操作导致服务异常。
5. 常见问题与排查技巧实录
5.1 关了服务之后系统起不来怎么办
这是最让人心慌的情况,但只要你之前留了后路,处理起来并不难。首先按Ctrl+Alt+F2切到虚拟终端,用 root 登录。如果连虚拟终端都进不去,那就需要进入救援模式或者用 Live USB 启动。
进入系统后,第一步是查看哪些服务启动失败了:
systemctl --failed找到可疑的服务后,重新启用它:
sudo systemctl enable --now 服务名.service然后重启验证。如果还是起不来,可以查看系统日志找线索:
# 查看本次启动的完整日志 journalctl -b # 只看错误级别的日志 journalctl -b -p errjournalctl -b会显示本次启动的所有日志,-p err过滤出错误级别以上的信息。我遇到过一次图形界面起不来,就是通过journalctl -b -p err发现是display-manager.service依赖的某个服务被误关了。
5.2 服务显示 disabled 但还在运行
这种情况通常有两个原因。第一个原因是服务被其他服务或 target 依赖,systemd 为了满足依赖关系把它拉起来了。排查方法是:
# 查看谁依赖了这个服务 systemctl list-dependencies --reverse 服务名.service第二个原因是服务被 socket 激活。有些服务平时不运行,但有对应的.socket文件监听端口,一旦有请求就自动启动。这种情况下你需要同时禁用 socket:
sudo systemctl disable --now 服务名.socket我遇到过cups就是这种情况,禁用了cups.service但cups.socket还在监听,结果一有打印请求服务又被拉起来了。后来把 socket 也禁用才彻底解决。
5.3 如何判断一个服务是否真的“没用”
这个问题没有标准答案,但我有一套自己的判断流程。首先看服务描述:
systemctl status 服务名.service | grep -i description然后看它属于哪个软件包:
rpm -qf $(systemctl show -p FragmentPath 服务名.service | cut -d= -f2)知道软件包名字后,就能大致判断它的用途。比如cups开头的肯定是打印相关,bluetooth肯定是蓝牙,nfs肯定是网络文件系统。
还有一个实用技巧是看服务的依赖链。如果一个服务只被自己依赖,而且没有其他服务依赖它,那它大概率是独立的,关掉影响面小。反之如果它被多个服务依赖,就要谨慎。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查命令 | 解决方法 |
|---|---|---|---|
| 系统起不来 | 误关关键服务 | systemctl --failed | 重新 enable 相关服务 |
| 服务显示 disabled 但仍在运行 | 被依赖或被 socket 激活 | systemctl list-dependencies --reverse | 禁用依赖服务或对应 socket |
| 图形界面异常 | 显示管理器依赖被关 | journalctl -b -p err | 恢复 display-manager 相关服务 |
| 网络不通 | 网络服务被误关 | systemctl status NetworkManager | 重新启用网络服务 |
| 开机仍然很慢 | 有其他耗时服务 | systemd-analyze blame | 继续排查耗时服务 |
| 内存没明显下降 | 服务被重新拉起 | systemd-cgtop -m | 检查是否有服务自动重启 |
5.5 我踩过的三个坑
第一个坑:只 disable 没 stop。刚开始做精简的时候,我以为disable就够了,结果重启后发现内存没怎么降。后来才明白disable只影响下次开机,当前运行的服务还在。必须用disable --now才能立即生效。
第二个坑:关了firewalld导致远程连接异常。有一次我在服务器上关了防火墙,结果某些端口规则失效,导致一个内部服务连不上。后来我学乖了,服务器上的防火墙服务一律保留,只在桌面机上考虑关闭。
第三个坑:批量操作没留日志。有一次我一次性关了十几个服务,结果系统出了点小问题,但我完全不记得关了哪些。从那以后我养成了写日志的习惯,每次操作都记录到文件里,方便回溯。
6. 进阶技巧:让服务精简更安全更高效
6.1 用 systemd 的 drop-in 机制做精细控制
有时候你不想完全禁用某个服务,只是想调整它的行为。比如某个服务你还需要,但希望它延迟启动,或者限制它的资源占用。这时候可以用 systemd 的 drop-in 机制,在/etc/systemd/system/服务名.service.d/目录下创建一个覆盖文件:
sudo mkdir -p /etc/systemd/system/NetworkManager-wait-online.service.d/ sudo tee /etc/systemd/system/NetworkManager-wait-online.service.d/override.conf << 'EOF' [Service] # 将超时时间从默认的 30 秒改为 5 秒 TimeoutStartSec=5 EOF sudo systemctl daemon-reload这种方式的优势是不修改原始服务文件,升级软件包时不会冲突,而且可以随时删除覆盖文件恢复默认行为。我一般用这种方式来调整那些“不能关但可以优化”的服务。
6.2 建立自己的服务白名单
与其每次装新系统都重新判断哪些服务该关,不如建立一份自己的白名单。我的做法是维护一个essential-services.txt文件,里面列出所有我认为必须保留的服务。每次精简时,先用systemctl list-unit-files --state=enabled列出所有自启服务,然后跟白名单对比,不在白名单里的就考虑禁用。
# 生成当前自启服务列表 systemctl list-unit-files --type=service --state=enabled --no-pager | awk '{print $1}' | sort > current-enabled.txt # 跟白名单对比,找出不在白名单里的服务 comm -23 current-enabled.txt essential-services.txtcomm -23的作用是找出第一个文件中有但第二个文件中没有的行,也就是“当前启用了但不在白名单里”的服务。这些就是你的候选禁用对象。
6.3 定期复查与动态调整
服务精简不是一劳永逸的事。系统更新、软件安装都可能引入新的自启服务。我一般每个月会跑一次复查,看看有没有新增的不必要服务。复查命令很简单:
# 查看最近安装的软件包引入的服务 systemctl list-unit-files --type=service --state=enabled --no-pager | wc -l如果这个数字比你上次检查时多了,就说明有新服务被启用了,需要重新评估。
另外,如果你发现某个服务被禁用了但系统运行一切正常,而且过了很长时间都没出问题,那就可以放心把它加入永久禁用列表。反之,如果某个服务禁用后你经常需要手动启动它,那就说明它对你来说是必要的,应该重新启用。
6.4 一个实用的服务审计脚本
最后分享一个我自己写的服务审计脚本,它会输出当前所有自启服务的列表、内存占用和启动耗时,方便你快速判断哪些值得关注:
#!/bin/bash # 服务审计脚本:列出所有自启服务及其资源占用 echo "=== 当前开机自启的服务 ===" systemctl list-unit-files --type=service --state=enabled --no-pager | head -50 echo "" echo "=== 启动耗时排名前 20 ===" systemd-analyze blame | head -20 echo "" echo "=== 内存占用排名前 20 ===" systemd-cgtop -m --order=memory -n 1 | head -25 echo "" echo "=== 启动失败的服务 ===" systemctl --failed --no-pager这个脚本我放在/usr/local/bin/service-audit.sh,每个月跑一次,几分钟就能完成复查。
我个人在实际操作中的体会是,服务精简这件事,收益最大的往往不是那些占用资源最多的服务,而是那些你根本不知道它存在、但它一直在后台跑的服务。把它们找出来关掉,不仅省资源,更重要的是让系统变得透明可控。你清楚每一个在跑的东西是干什么的,这种掌控感比省下来的那几百兆内存更有价值。