无外网 Linux 服务器离线安装 VS Code Remote-SSH 指南(新版双包机制 + commit 一致)

点一下"转到定义",等三秒。打开个文件,状态栏转圈半分钟。想用 clangd 让跳转又快又准,结果它压根启动不了------因为你的 VS Code 在 Windows 上跑,代码却在网络盘的另一头。

这是我接手一台无外网 Linux 编译服务器时遇到的真实情况。解决办法大家都说得上来:上 Remote-SSH。可真到装的时候,网上一堆教程照着做,连不上。卡在 "Setting up SSH Host..." 转半天,最后报个错让你怀疑人生。

折腾一圈才发现,新版 VS Code 离线安装要下两个包,不是一个。网上老教程只讲一个,对新版根本无效。这篇文章就把这台离线服务器从"卡到不能用"到"clangd 秒跳"的完整过程拆开讲清楚,包括为什么这么做、踩了哪些坑、怎么排查。

你的代码跳转为什么慢到怀疑人生

先看问题出在哪。我们这台 Linux 编译服务器没外网,但 Windows 和它之间通过 SMB 把 Linux 的一个目录映射成了 F 盘。于是 VS Code 直接打开 F 盘里的工程,看起来跟本地一样。

问题在于,VS Code 的 C/C++ 扩展是作为 Windows 进程在跑的。它要读的那几千个源文件、头文件,全在 F 盘后面那台 Linux 上。每一次文件读取,都是一次跨网络的 IO。

老的 Tag Parser 方案更惨。它要扫描工程里所有文件建符号索引,几千个文件一个个读,每个都走网络。你点个跳转,它在后台疯狂读网络盘,延迟就这么累积起来的。

clangd 呢,更直接。它根本跑不起来。clangd 是个 Linux 二进制,需要 Linux 路径、Linux 工具链。你在 Windows 端装 clangd 扩展,它找不到对应的二进制,或者拿到了也跑不了。

这里有个判断基线,我后面会反复强调:做代码索引的程序,必须和被索引的文件在同一台机器上。跨网络盘做索引,注定慢,注定别扭。

Remote-SSH 的本质:把索引进程搬过去

理解了上面这点,Remote-SSH 的解法就很自然:把索引进程搬过去,代码不用动。

Remote-SSH 不是远程桌面那种"把整个界面投过来"。它把活儿拆开:UI 留在你的 Windows 上(你熟悉的快捷键和插件都在),真正干活的进程跑在 Linux 服务器上。这个干活的进程叫 vscode-server。

连上之后,clangd 扩展装在远端,作为 Linux 进程跑,直接读本地磁盘上的源文件、compile_commands.json、头文件,零网络 IO。你点跳转,clangd 在本地几毫秒就响应了,结果传回 Windows 端渲染。

为什么这比 Tag Parser 强?Tag Parser 是全盘正则扫描,扫一遍建个静态索引,慢且不准(宏、条件编译搞不定)。clangd 基于 compile_commands.json,每个文件用真实的编译参数去解析,宏展开、头文件路径都是准的。一个是从目录里翻,一个是照着编译命令精确读,根本不是一个量级。

所以 Remote-SSH 不是打补丁,它从根上纠正了"索引进程和文件分离"这个错误架构。

离线安装最大的坑:新版要两个包

到装的时候了。有外网的话啥都好说,VS Code 首次连接会自动下载 server。可我们这台没外网,自动下载必失败,卡在 "Setting up SSH Host..." 直到超时。

离线安装的思路是:在 Windows 下载好包,传到 Linux,手动解压到位。但新版 VS Code(1.102+)需要两个包,缺一不可。这是整件事最容易踩的坑。

两个包分别是:

  • server 包 vscode-server-linux-x64.tar.gz,解压到 ~/.vscode-server/cli/servers/Stable-<commit>/server/,里面是 server 本体(node + out/ + 扩展)。
  • CLI 包 vscode-cli-linux-x64.tar.gz,解压成 ~/.vscode-server/code-<commit>,是连接入口二进制。

为什么是两个?新版把连接管理和语言服务解耦了。ssh 进来先执行的是 CLI 入口(code-<commit>),CLI 再去拉起 server(cli/servers/Stable-<commit>/server/bin/code-server)。CLI 管连接,server 管干活。

这里还有个路径演进的坑。旧版 VS Code(大约 1.86 之前)server 装在 ~/.vscode-server/bin/<commit>/,新版改到了 ~/.vscode-server/cli/servers/Stable-<commit>/server/网上很多老教程写的是 bin/<commit>/,对新版完全无效 。判断你装的是新版还是旧版路径:看 ~/.vscode-server/ 下有没有 code-<commit> 这个文件,有就是新版路径。

只装 server 不装 CLI 会怎样?ssh 进来找不到 code-<commit> 入口,VS Code 尝试自动下载 CLI,但没外网,连接失败。我就这么卡过,对比一位同事的 ps -ef | grep vscode-server 进程才发现,人家有 code-<commit> 入口而我没有。

commit 号:不是版本号,且必须三处一致

下载那两个包,要用到 commit 号。注意,commit 号不是版本号

版本号是 1.102.1 这种,commit 号是一串 40 位十六进制 hash,在 VS Code 的 Help → About 里能看到,类似 7adae6a56e34cb64d08899664b814cf620465925。下载地址是拿 commit 号拼的:

复制代码
https://update.code.visualstudio.com/commit:<commit号>/server-linux-x64/stable
https://update.code.visualstudio.com/commit:<commit号>/cli-linux-x64/stable

关键是,三处 commit 必须完全一致:Windows 客户端的 commit、server 包的 commit、CLI 包的 commit。差一个字符,VS Code 都认为你没装,重新触发下载,离线环境下就是连不上。

这意味着每次 VS Code 升级,commit 号会变,两个包都要重新下载对应版本。这是离线维护的持续成本,躲不掉。所以装之前一定先查准 commit,别下错版本白忙活。

完整安装十步走

理清原理后,操作其实不复杂,十步:

  1. Windows 装 Remote-SSH 扩展(ms-vscode-remote.remote-ssh
  2. Help → About 查 commit 号(40 位)
  3. 浏览器下两个包,分别重命名为 vscode-server-linux-x64.tar.gzvscode-cli-linux-x64.tar.gz
  4. 通过 F 盘(或任何内网传输)把两个包传到 Linux
  5. Linux 解压两个包到位:server 到 cli/servers/Stable-<commit>/server/,CLI 到 ~/.vscode-server/code-<commit>
  6. 配 SSH config 并连接
  7. 装 clangd 扩展到远端
  8. 验证代码跳转
  9. 多工程共存
  10. 编译之外的代码怎么看

第 5 步是重点。解压用两个脚本:setup_vscode_server.sh <commit号> 把 server 包解压到新版路径,验证 nodebin/code-serverout/server-main.js 齐全;setup_vscode_cli.sh 把 CLI 包解压出 code 二进制,复制成 ~/.vscode-server/code-<commit>

两个脚本跑完,再跑自检 check_vscode_server.sh,8 项全 [OK] 才能去连:server 的 node、server-main.js、CLI 入口、clangd、.clangd、settings.json、compile_commands.json、SSH 服务。哪项 FAIL 脚本会直接给修复命令。

SSH key 免密的四个坑

配 SSH 连接本身不难,但免密登录这里坑特别多,一个个说。

坑一:VS Code 走 ssh.exe 是非交互的。 你用 SecureCRT 连服务器,没配 key 它会弹窗问你密码。VS Code Remote-SSH 调的是 Windows 自带的 ssh.exe,非交互模式,没配 key 直接 Permission denied,不会弹窗。所以你以为"密码能连啊",到 VS Code 这就连不上。

坑二:配了 key 还得加两行配置。 生成密钥 ssh-keygen -t ed25519,把公钥传到服务器的 ~/.ssh/authorized_keys,这都好理解。但光这样不够,VS Code 还会要密码。得在 SSH config 里加:

复制代码
Host nordic-server
    HostName <服务器IP>
    User <你的用户名>
    Port 22
    IdentityFile C:\Users\<你的用户名>\.ssh\id_ed25519
    IdentitiesOnly yes

IdentitiesOnly yes 强制只用你指定的这个 key,避免 ssh-agent 里的其他 key 干扰。VS Code 走 ssh.exe 非交互,没这两行会回退到要密码。

坑三:key 认证对权限敏感到变态。 服务器端 ~/.ssh 必须 700、authorized_keys 必须 600、home 目录不能让 group/other 可写。权限不对,SSH 会静默拒绝你的 key,直接回退到密码认证,你看着像"key 没生效",其实是权限问题。

坑四:验证免密必须用 config 别名。 这是最隐蔽的坑。你想测免密通没通,习惯性敲 ssh <你的用户名>@<服务器IP>,发现不问密码,以为成功了。但 VS Code 还是连不上。

原因是直连 IP 时,ssh-agent 可能记住了你私钥的 passphrase,帮你免密了。但 VS Code 走的是 config 别名 + IdentitiesOnly,不走 agent,passphrase 问题就暴露了。只有 ssh nordic-server(用别名)不问任何东西,才算真免密。如果别名还问 passphrase,去掉它:

复制代码
ssh-keygen -p -f $env:USERPROFILE\.ssh\id_ed25519

输旧 passphrase,新 passphrase 两次回车留空。

clangd 装远端,别装本地

连上 SSH 后,装 clangd 扩展。这里容易错------clangd 必须装在 SSH 远端,不是本地

VS Code 的扩展面板分两栏:LOCAL(本地 Windows)和 SSH: nordic-server(远端 Linux)。在 clangd 扩展卡片上要点 "Install in SSH: nordic-server"。如果只装到 LOCAL,clangd 作为 Windows 进程跑,又回到网络盘问题了。

clangd 精确跳转的基石是 compile_commands.json。这个文件记录了每个源文件真实的编译参数,包含路径、宏定义全在里面。clangd 拿到它,每个文件都按真实编译命令解析,宏和头文件路径都是准的。没有它,clangd 只能 fallback 用默认参数猜,简单符号能跳,复杂的一跳一个不准。

这里还有个版本坑。我在 settings.jsonclangd.arguments 里配了 --cache-dir=...,结果 clangd 启动直接退出,Output 里报 Server process exited with code 1。折腾半天才发现,clangd 18 根本没有 --cache-dir 这个参数--background-index-cache-dir 也没有)。手动跑 /usr/bin/clangd <参数> --check=xxx.c,stderr 会打印 Unknown command line argument '--cache-dir=...'。删掉就好了,--background-index 自带索引持久化,clangd 自己管存储位置。

教训:clangd 不同版本支持的参数不一样,配 clangd.arguments 前用 clangd --help 确认参数存在。远端装的 clangd 扩展版本(比如 0.6.0)倒不影响跳转能力,它只是个前端,真正干活的是 /usr/bin/clangd 那个二进制。

如果远端装 clangd 扩展也因网络失败,走 VSIX 离线:Windows 浏览器从扩展市场下载 .vsix,传到 Linux,VS Code 里 Install from VSIX

连不上?按这五层逐层排查

装完连不上,别盲目重试。按这个顺序逐层定位,每层通了再查下一层

第 1 层 网络层 :Windows PowerShell 跑 ssh <你的用户名>@<服务器IP> "echo OK"。打印 OK 就通,进下一层。超时是网络/防火墙,拒绝是 SSH 服务没开。

第 2 层 SSH 认证层 :跑 ssh nordic-server "echo OK"(用别名)。打印 OK 进下一层;Permission denied 是认证问题,回去查 key;Could not resolve hostname 是 config 没配对。注意区分:ssh user@IP 能连但 ssh nordic-server 不行是 config 问题,两个都不行是认证/网络问题。

第 3 层 server 安装层 :Linux 端跑 check_vscode_server.sh,应全 [OK]。最常见 FAIL 是 CLI 入口那项,说明没装 CLI 包。手动验证 server 能否启动:~/.vscode-server/cli/servers/Stable-$COMMIT/server/node ~/.vscode-server/cli/servers/Stable-$COMMIT/server/out/server-main.js --version,打印版本号就正常。再确认三处 commit 完全一致。

第 4 层 VS Code 状态层 :前三层都通还连不上,通常是 VS Code 缓存了失败状态。F1 → Remote-SSH: Kill VS Code Server on Host,再 Developer: Reload Window,重新连。远端可清残留:pkill -f vscode-server、清 logs 和 lock 文件,但别删 cli/servers/code-<commit>,那是安装包

第 5 层 clangd 层 :连上了但跳转不对,看这层。打开 .c 文件,View → Output 选 clangd 通道,看日志。clangd version 18.1.3 + Loaded compilation database from ... 就是正常的。

有个实战定位手段很管用:如果同事用同版本 VS Code 在同台服务器连上了,对比他的 ~/.vscode-server/ 路径结构,ps -ef | grep vscode-server 看他的进程命令行,照抄路径。我这次排查就是靠对比同事进程发现新版路径变了、需要 CLI 包的。

编译之外的代码怎么看

clangd 有个局限得说清楚:只有编译进镜像的文件才有精确跳转。没在 compile_commands.json 里的文件,clangd 没有编译参数,跳转会失败或不准。

哪些算"编译之外"?prj.confCONFIG_XXX=n 的源文件、其他工程的代码、SDK 里没选用的驱动、samples 和 tests 目录。这些 clangd 都管不了。

对策按推荐顺序:第一,用 CodeGraph(如果有的话),它索引整个工作区所有符号,不依赖 compile_commands,没编译的代码也能查,几秒出结果,补上 clangd 的短板。第二,VS Code 全局文本搜索 Ctrl+Shift+F 兜底,不精确(同名混),但能定位文件再人工判断。第三,把要看的文件纳入编译------Zephyr/NCS 在 prj.confCONFIG_XXX=y 重新 build,CMake 在 CMakeLists 加源文件重新 cmake,一劳永逸。第四,少数文件用 compile_flags.txt,每行一个参数放文件所在目录。第五,clangd fallback 模式自动启用但别指望它准。

日常跳转用 clangd,编译内的秒跳。跨工程或编译外的代码,用 CodeGraph 或全局搜索。长期要看的模块,开配置重新 build 纳入 clangd。

六条真实踩坑记录

这次安装实际遇到的问题,供你排查时参考:

  1. 只装 server 没装 CLI → 连接失败。根因:新版需要 cli-linux-x64 单独的包作 ssh 入口。定位:对比同事进程发现他有 code-<commit> 而我没有。

  2. server 装在旧路径 bin/<commit>/ → VS Code 找不到。根因:新版路径改到 cli/servers/Stable-<commit>/server/。定位:看同事进程命令行里的路径照抄。

  3. server 入口文件名变了 → 老脚本找 bin/code-server-oss 找不到。根因:新版叫 bin/code-server(无 -oss)。不影响 VS Code(它按 product.json 找),但自检脚本要适配。

  4. SecureCRT 能连但 VS Code 不能 → 排除网络/认证,锁定 VS Code 自身。定位:PowerShell 跑 ssh <你的用户名>@<服务器IP> "echo OK" 直接打印 OK,说明 Windows 的 ssh.exe 没问题,问题在 server 安装层。

  5. 本机自连报 Permission denied → 误判为认证问题。实际是本机没密码,不代表服务器拒绝你。教训:诊断命令要在客户端(Windows)跑,不是在服务器本机跑。

  6. clangd Server process exited with code 1 → clangd 启动失败。根因:clangd.arguments 配了 --cache-dir,但 clangd 18 没这参数,启动直接退出。定位:手动跑 /usr/bin/clangd <参数> --check=xxx.c,stderr 打印 Unknown command line argument。解决:删掉 --cache-dir

升级维护

VS Code 升级后 commit 号变,需重新走下载两包、解压的流程。旧 commit 目录可删:rm -rf ~/.vscode-server/cli/servers/Stable-<旧commit>rm -f ~/.vscode-server/code-<旧commit>

改了 prj.conf 后跳转变不准?重新 build 生成新的 compile_commands.json,clangd 自动重载(.clangd 里配了 Index.Background: Build)。


回到开头那个判断基线:索引进程必须和文件同机。Remote-SSH 就是把这条原则在离线环境里落地。离线环境下真正的难点是新版那两个包、commit 三处一致、SSH key 的四个坑。这些理清了,clangd 秒跳不难。

你离线服务器上还在用网络盘硬扛代码跳转吗?或者装 Remote-SSH 卡在哪一步了?评论区说说,有用的话点个在看,让更多被网络盘折磨的工程师看到。


标签:VS Code · Remote-SSH · 离线安装 · clangd · 嵌入式开发

相关推荐
BugShare1 小时前
告别来回切换,Navop:一站式整合数据库、SSH、终端与 AI 的开发运维工作台
运维·数据库·ssh
郴墨3 小时前
Ubuntu 24.04 配置大量ip及ssh远程
tcp/ip·ubuntu·ssh
Tronlong创龙14 小时前
告别系统崩溃!ARM工控机 + OverlayFS,系统一键还原
嵌入式开发·硬件开发·工业控制·工业开发板
hxhy0019 小时前
异地组网实战:从 frp 中转到 Tailscale 直连,SSH 延迟 410ms → 29ms
运维·ssh
俊基科技1 天前
AU-48 双麦多功能语音处理模组让每一句话都清晰抵达 —— 一颗 23×20mm 小芯片,重新定义“听得清“
嵌入式开发·硬件开发·ai降噪·回声消除·拾音降噪
华科大胡子1 天前
VS Code Git 工作树:多分支并行开发体验
vs code
浩风祭月2 天前
Agent Plugins 1.0怎么把一套技能同时带进VS Code和Copilot CLI?
ai编程·vs code·github copilot·mcp·copilot cli·agent plugins