服务器cup占用异常
在给服务器安装nodejs
时我使用了nvm
这个工具来简化安装流程,但是发现安装后出现cpu占用高的问题(经过了一番排查才发现是这个原因导致),究其原因是因为nvm会在配置文件中添加一些命令,然后被finalshell
重复执行导致
注释掉 .bashrc
中与 nvm
(Node Version Manager)相关的配置后,CPU 占用下降,这说明问题的根源在于 nvm
的初始化脚本在 FinalShell 连接时被频繁执行,导致了额外的 CPU 消耗。具体原因和原理如下:
一、为什么 nvm
初始化会导致 CPU 升高?
nvm
的初始化脚本(nvm.sh
和 bash_completion
)包含以下操作,在频繁触发时会消耗 CPU:
环境变量加载与解析
nvm.sh
需要读取$NVM_DIR
目录下的配置文件、版本列表,解析 Node 版本信息,这些操作涉及大量文件 I/O 和字符串处理,会触发系统调用(提升%sys
系统态 CPU)。命令补全脚本执行
bash_completion
脚本用于实现nvm
命令的自动补全(如输入nvm use
按 Tab 补全版本),但它需要扫描所有可用的 Node 版本和命令,生成补全缓存,这一过程在每次 Shell 初始化时都会执行,消耗用户态(%usr
)和系统态 CPU。FinalShell 的
exec channel
触发多次初始化 FinalShell 的exec channel
可能会在后台创建多个 Shell 会话(即使你只打开一个窗口),例如:- 实时监控面板通过单独的 Shell 会话执行
top
等命令; - 文件浏览器、日志查看器等功能会隐式创建临时 Shell 会话。
每个会话都会加载
.bashrc
,导致nvm
初始化脚本被重复执行(每创建一个会话执行一次),短时间内多次执行就会累积 CPU 消耗,最终导致整体占用达到 50%-60%。- 实时监控面板通过单独的 Shell 会话执行
二、为什么注释后 CPU 恢复正常?
注释掉 nvm
相关配置后,避免了以下消耗:
- 不再加载
nvm.sh
,减少了文件 I/O 和环境变量解析的系统态 CPU 消耗; - 不再执行
bash_completion
脚本,消除了命令补全的扫描和缓存生成过程; - 即使 FinalShell 创建多个 Shell 会话,
.bashrc
中也没有高消耗的初始化操作,CPU 自然下降到正常水平。
三、后续建议(如需使用 nvm
)
如果需要在服务器上使用 nvm
,但又想避免 FinalShell 连接时的高 CPU 消耗,可按以下方式优化:
1. 仅在需要时手动加载 nvm
将 .bashrc
中的 nvm
配置修改为手动触发,避免自动加载:
# 在 .bashrc 中替换原配置为:
# 手动输入 `load_nvm` 才加载 nvm
load_nvm() {
export NVM_DIR="${HOME}/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
}
- 需要使用
nvm
时,在终端输入load_nvm
手动加载,用完后关闭会话即可。
2. 限制 nvm
初始化的频率
修改 nvm
初始化逻辑,仅在交互式 Shell 中加载(FinalShell 的后台会话通常为非交互式,可跳过):
# 在 .bashrc 中添加判断,仅交互式 Shell 加载 nvm
if [[ $- == *i* ]]; then # 检查是否为交互式 Shell
export NVM_DIR="${HOME}/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
fi
- 原理:FinalShell 的后台
exec channel
会话通常为非交互式($-
中不含i
),因此不会加载nvm
,仅在你手动打开的交互式终端中才加载。
3. 减少 FinalShell 的后台会话
在 FinalShell 中关闭不必要的功能,减少自动创建的 Shell 会话:
- 关闭 “实时性能监控”“自动日志刷新” 等依赖后台会话的功能;
- 在连接设置中,将 “默认 Shell 类型” 从
login shell
改为non-login shell
(减少初始化脚本执行次数)。
通过以上方式,既能保留 nvm
的功能,又能避免 FinalShell 连接时的 CPU 高负载问题。核心逻辑是:减少非必要场景下的初始化脚本执行次数。
四、后续使用
最终我采用手动加载 nvm
的方式,以后使用nvm
和nodejs
方式如下
[root@dl ~]# nvm -v
-bash: nvm: 未找到命令
[root@dl ~]# load_nvm
[root@dl ~]# nvm -v
0.39.5
[root@dl ~]# node -v
v20.19.3
[root@dl ~]#