ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

麒麟系统服务精简实战:禁用冗余服务提升性能

麒麟系统服务精简实战:禁用冗余服务提升性能

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 -m

systemctl 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 -30

systemd-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.serviceNFS 端口映射不用 NFS低
nfs-client.targetNFS 客户端同上低
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 服务名.service

systemctl status的输出里,如果看到Active: inactive (dead)说明已经停止,看到Loaded: ...; disabled说明已经取消自启。两个都满足才算真正禁用成功。

有时候你会遇到一种情况:disable执行了,但status里显示的还是enabled。这通常是因为有另一个服务或 target 依赖它,把它又拉起来了。这时候需要用mask命令:

# mask 比 disable 更彻底,它会创建一个指向 /dev/null 的符号链接 sudo systemctl mask 服务名.service

mask的效果是这个服务彻底无法启动,连手动启动都不行。除非你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 err

journalctl -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.txt

comm -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,每个月跑一次,几分钟就能完成复查。

我个人在实际操作中的体会是,服务精简这件事,收益最大的往往不是那些占用资源最多的服务,而是那些你根本不知道它存在、但它一直在后台跑的服务。把它们找出来关掉,不仅省资源,更重要的是让系统变得透明可控。你清楚每一个在跑的东西是干什么的,这种掌控感比省下来的那几百兆内存更有价值。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进