Cygwin 实战指南:Windows 上的 POSIX 兼容层与选型避坑
1. Cygwin 到底是什么一个翻译层不是操作系统1.1 先把最常见的那个误解拆掉Cygwin 既不是 Windows也不是 Linux。很多人第一次看到这个名字会本能地把它归类成某个 Linux 发行版或者Windows 的精简版这两个方向都偏了。它准确的定位是一个跑在 Windows 上的 POSIX 兼容层。你装完之后桌面上会多出一个终端窗口里面能敲ls、grep、sed、awk、make、gcc,能跑 bash 脚本,能连 SSH,看起来和一台 Linux 机器几乎一样——但底层跑的仍然是 Windows 内核进程仍然是 PE 格式的.exe文件路径仍然挂在 NTFS 上。这个看起来像 Linux、实际上是 Windows的特性是理解 Cygwin 全部行为的钥匙。它的目录结构模仿 Unix/usr、/etc、/home但C:\cygwin64才是真实位置它提供fork()系统调用但 Windows 内核压根没有 fork 这东西所以只能靠模拟它有/proc但里面大部分内容是算出来的不是内核暴露的。你越早接受它是一层翻译不是另一个系统这个设定后面踩坑就越少。我第一次接触它是在一台公司配发的 Windows 电脑上当时需要跑一个只有.sh启动脚本的开源服务。装虚拟机太重装双系统不现实把脚本手工翻译成.bat又太蠢。Cygwin 让我在那台机器上原封不动地跑起了整套 shell 脚本改动量几乎为零。它解决的问题就是这一类你被困在 Windows 上但手上的东西是按 Unix 规则写的。1.2 cygwin1.dll 究竟做了什么Cygwin 的核心只有一样东西cygwin1.dll。这个动态库是整套体系的翻译官。当一个 Cygwin 程序调用open()、read()、write()、fork()、select()、signal()这些 POSIX 接口时请求并不会直接进 Windows 内核而是先被cygwin1.dll接住翻译成对应的 Win32 API 调用——CreateFile、ReadFile、WriteFile、CreateProcess、WaitForMultipleObjects等等。这解释了几件很反直觉的事。第一为什么 Cygwin 的fork()慢因为 Windows 没有真正的写时复制进程派生机制cygwin1.dll只能靠把当前进程内存镜像复制出来再拉起一个新进程把内存注回去然后修补各种句柄来模拟。这个动作涉及大量重定位和同步天生比 Linux 上快不了。第二为什么 Cygwin 编译出来的.exe依赖cygwin1.dll因为它就是个 PE 程序只不过所有系统调用都绕道这个 DLL。第三为什么 Cygwin 程序和原生 Windows 程序能互相调用因为它们共享同一套 Win32 进程模型和文件系统只不过 Cygwin 程序多带了一层路径和语义的适配。注意cygwin1.dll是全局单例。一个进程树里只能有一份版本不一致会出现两个 cygwin1.dll 打架的诡异现象表现为 fork 直接失败或者随机崩溃。这也是后面讲PATH 里有多个 Cygwin 安装为什么危险的原因。1.3 谁适合用谁别碰我一般会这样区分。适合用的场景手上有一批 Unix 风格的 shell 脚本或 Makefile 必须在 Windows 上跑需要一套完整的 GNU 工具链gcc、make、autotools、binutils来做跨平台编译验证需要 SSH、rsync、grep、sed 这类命令行工具做日常运维和日志处理想学习 Linux 命令但暂时没有 Linux 环境。不太适合的场景想跑需要真实 Linux 内核特性的东西比如依赖特定ioctl、依赖内核模块、依赖 cgroup/namespace 的服务对 I/O 性能和进程创建速度有硬要求的场景想拿它当完整的 Linux 桌面替代品。说实话如果你只是想要一个 Linux 命令行现在的选择比十年前多得多。Cygwin 的价值从来不在最好用而在于最贴近传统 Unix 语义、可移植性最好、改动最小。它是一把老派但锋利的刀用对了地方极其顺手用错了地方会让你怀疑人生。实操心得我把 Cygwin 当成Windows 上的 Unix 语法执行器来用而不是当成Linux 发行版来用。这个心理定位一旦立住就不会去纠结它为什么不支持某个内核特性了。2. Cygwin、WSL、MSYS2、虚拟机怎么选2.1 五种方案摆在一起对比这是被问得最多的一类问题。与其背结论不如把几个关键维度摊开看。方案本质POSIX 兼容度与 Windows 互操作性能上手成本CygwinWindows 上的 POSIX 翻译层高语义接近传统 Unix极好同一文件系统中等进程创建偏慢低MSYS2从 Cygwin 分叉面向 MinGW 工具链中偏构建环境好中等低WSL2轻量虚拟机跑真实 Linux 内核最高好跨文件系统有损耗高Linux 侧中传统虚拟机完整虚拟化最高一般需共享目录较低高纯原生 Windows 工具编译成 PE 的程序集低最好最高视工具而定看懂这张表的关键在于**POSIX 兼容度和互操作是一对反向指标**。Cygwin 的优势恰好是它的哲学不模拟内核只翻译调用所以它和 Windows 是同一个进程世界、同一个文件系统。你在 Cygwin 里cd /cygdrive/c/Users和资源管理器打开C:\Users看到的是同一批文件没有跨边界拷贝没有 9P 协议的额外开销。2.2 Cygwin 到今天还没被淘汰的原因按道理说WSL2 出现之后 Cygwin 应该凉了。但实际工作中它仍然有不可替代的位置原因有三点。第一是路径与文件系统的透明性。WSL2 里的 Linux 文件系统在虚拟磁盘里从 Windows 侧访问要走\\wsl$这种网络路径很多 Windows 原生程序处理不了长路径和特殊字符。而 Cygwin 的/cygdrive/c/...就是本地 NTFS任何 Windows 工具都能直接读写。做跨平台构建、给 Windows 程序喂 Linux 风格路径这种活Cygwin 天然顺手。第二是无需虚拟化权限。装 WSL2 需要开启虚拟机平台、可能需要 BIOS 里的虚拟化开关、在企业环境里有时还要 IT 开策略。Cygwin 就是一个普通的用户态安装程序双击下一步下一步就完事在受限的办公电脑上这一点非常关键。第三是工具链的老派一致性。很多十几年前的开源项目构建脚本就是为 Cygwin 写的configure脚本里甚至有显式的*-cygwin*分支判断。你在 MSYS2 或 WSL 里跑反而会遇到 host triplet 不匹配的问题。这种历史包袱听起来是缺点但它保护了一大批仍然在维护的老项目。2.3 选型的几条实操建议我自己的判断流程是这样要跑真实 Linux 服务、要装 systemd、要搞容器 → 上 WSL2 或虚拟机要在 Windows 上编译原生 Windows 程序 → 用 MSYS2/MinGW要跑 Unix 脚本、要用 GNU 命令行工具、要最小改动地移植构建流程 → Cygwin。注意 MSYS2 和 Cygwin 是同源但不同目标MSYS2 的bash、make本身是 Cygwin 血统但它默认希望你最终产出的是不依赖msys-2.0.dll的原生 Windows 程序Cygwin 则希望你整个环境就是一个 Unix 世界。还有个很现实的点别在一台机器上同时装多个 Cygwin 系环境并都塞进 PATH。我没少见有人装了 Cygwin、又装了 MSYS2、又装了 Git for Windows 自带的那套 MSYS结果cygwin1.dll、msys-2.0.dll版本互相干扰fork随机失败git报奇怪的错。遇到这种玄学问题第一反应就该是查 PATH 里有几套环境。常见问题有人问Cygwin 和 Git for Windows 里的 Git Bash 是一回事吗。它们是亲戚。Git Bash 用的是 MSYS2 血统的运行时命令集是子集别把它当完整 Cygwin 用也别指望在里面编译出依赖完整 POSIX 语义的程序。3. 从零装好一套能用的 Cygwin3.1 安装过程逐屏说明官方提供的安装器是setup-x86_64.exe它同时承担安装、更新、添加删除包三个职责——也就是说之后你想装新包不是用apt而是重新运行这个安装器走一遍向导。这个设计刚开始会让人不习惯但它是幂等的重复跑不会重装已有包。向导的关键几屏我按顺序说。Choose Installation Type选Install from Internet除非你在离线环境那就用Download Without Installing先在联网机器上下载完整包目录再拷过去用Install from Local Directory。Root Directory默认C:\cygwin64我建议保持默认或者放到一个没有空格、没有中文、路径较短的盘符下。Local Package Directory这是下载缓存目录会越滚越大放一个空间充裕的位置后续重装能省很多下载时间。Connection一般选Direct Connection。Choose Download Site挑一个延迟低的镜像站列表里带http和ftp的都有。Select Packages这一屏是重头戏下一节专门讲。安装完成后C:\cygwin64\bin下会有一大堆.exeC:\cygwin64\cygwin1.dll是核心库。桌面上通常会有个 Cygwin 终端快捷方式点开就是 mintty 包着的 bash。3.2 包选择的取舍装什么别装什么包选择界面有两个视图Category按分类罗列Full是全部包的平铺列表。中间那个搜索框是我最常用的入口直接敲包名快速定位。包名左边有图标表示状态带循环箭头的表示要升级空白表示未安装对勾表示已安装还有个Keep表示保持当前版本。默认安装只会装 Base 分类这套东西只够你ls、cd、cat远远不够用。我的推荐清单如下。Base 之外的必备coreutils各种基础命令、grep、sed、gawk、findutils、tar、gzip、bzip2、xz、zip、unzip、wget、curl、less、tree、which、procps。开发相关gcc-core、gcc-g、make、binutils、gdb、autoconf、automake、libtool、pkg-config、cmake、git。Shell 与编辑器bashBase 自带、zsh、vim、nano、tmux、screen。网络与远程openssh、rsync、openssl、ca-certificates、inetutils。解释器python3、python3-pip、perl、ruby按需。终端体验mintty默认装了、ncurses、terminfo。几个要谨慎的包。cygwin本身这个包绝对不能乱动它是运行时核心升级版本要谨慎一定先关掉所有 Cygwin 相关进程再升级否则会出现 DLL 被占用导致更新失败甚至半残。另外别在 Cygwin 里装nodejs和用 Windows 原生 Node 混着跑两边的路径语义和二进制格式不一致npm 的 postinstall 脚本经常炸这类需求建议用 Windows 原生的 Node 工具链。注意所有包的选择结果在向导走完后想改就重新跑一遍setup-x86_64.exe在 Select Packages 里搜包名点一下状态让它变成安装或卸载然后继续。这是 Cygwin 唯一的包管理方式但它有个好处能记住你上次装了什么Keep状态一目了然。3.3 终端体验调优默认的 mintty 已经比老式的 cmd 窗口好太多但还能再调几项。字符集右键标题栏 →Options→Text→Character set选UTF-8Locale选C或者跟语言环境一致。字体在Text里挑一个等宽字体带上连字的字体也行但要是 Monospace。滚动缓冲Window里把Rows调到 40 以上Scrollback给到几千行看日志方便。复制粘贴Keys里可以设成选中即复制、中键粘贴习惯 Unix 的人会舒服很多。~/.minttyrc是这些设置落盘的地方格式是KeyValue比如FontConsolas、FontHeight12、CharsetUTF-8。想换主题配色可以直接改ForegroundColour、BackgroundColour这些值是R,G,B三元组。我还会顺手改几个 shell 层面的东西。~/.bashrc里加上alias llls -alF、alias lals -A、alias gsgit status这类设置PS1让它显示完整路径和 git 分支把HISTCONTROLignoreboth和HISTSIZE10000加上避免命令历史里全是重复项。这些东西看着琐碎但一天敲几百次命令积累下来的效率差别很大。3.4 PATH 的正确摆法Cygwin 启动 bash 时/etc/profile会把 Windows 的PATH转换成 Unix 风格再追加到自己的PATH后面。这个行为会带来灾难性的后果C:\Windows\System32里也有find.exe、sort.exe、tree.com虽然是 Windows 原生版本、参数完全不兼容 GNU 版本但一旦它们排在前面你的find . -name *.log就会报参数无效。判断方法很简单进 bash 敲type -a find看有几个结果、哪个在前面。解决办法是在~/.bashrc或者/etc/profile的末尾重设PATH把/usr/local/bin:/usr/bin明确放到最前面并且把C:\Windows\System32那一段剔掉。我通常这么写# 去掉原生 Windows 系统目录避免 find/sort/tree 冲突 _clean_path() { local IFS: local out for p in $PATH; do case $p in */Windows/System32|*/Windows|*/Windows/System32/Wbem) continue ;; esac out${out:$out:}$p done echo $out } PATH/usr/local/bin:/usr/bin:$(_clean_path) export PATH另外C:\cygwin64\bin是不是要加到 Windows 系统PATH里这是个取舍。加进去的好处是 cmd 和 PowerShell 里能直接调ls.exe、grep.exe坏处是可能和其他环境里的同名命令打架而且会让系统PATH变得很长。我的做法是不加全局 PATH只在需要的批处理脚本里临时指定完整路径或者干脆用 Git Bash 之类已经处理好的入口。实操心得改完PATH之后一定要开一个全新终端验证type -a逐个检查高频命令落在哪个路径上。which -a也是好帮手。这个检查花三十秒能省掉后面半小时的困惑。4. Cygwin 里的路径世界/cygdrive 与路径转换4.1 盘符是怎么映射成目录的Windows 的C:\Users\me\docs在 Cygwin 里是/cygdrive/c/Users/me/docs。/cygdrive是一个虚拟挂载点后面跟盘符大小写不敏感。这套映射由挂载表控制文件在/etc/fstab格式和 Linux 的 fstab 类似。默认内容大概长这样none /cygdrive cygdrive binary,posix0,user 0 0那串选项值得解释一下。binary表示不做换行符转换posix0表示路径解析时盘符不加前导斜杠user表示普通用户可以访问。如果你不喜欢/cygdrive这个前缀可以在 fstab 里改成别的比如你想直接/c/Users/...这样用就把挂载点写成/然后加posix1或者利用cygdrive的posix选项——不过这类改动会影响所有脚本动手之前先把影响想清楚我不太建议新手改这个。查看当前挂载状况用mount不带参数会列出所有挂载点。想临时加一个映射mount -b D:\data /data-b表示二进制模式。mount -u是卸载。4.2 cygpath路径互转的瑞士军刀只要你的工作涉及Windows 程序 Cygwin 脚本混用cygpath就是必备工具。它能在两种路径表示之间来回倒。选项作用示例输入输出-u转成 Unix 风格C:\Users\me/cygdrive/c/Users/me-w转成 Windows 风格/home/me/xC:\cygwin64\home\me\x-m转成混合风格正斜杠/home/me/xC:/cygwin64/home/me/x-d转成 Windows 短路径8.3/home/me/xC:\cygwin6\home\me\x-a输出绝对路径./x/cygdrive/c/...最典型的用法是把 Cygwin 里算出来的路径喂给 Windows 程序。比如你要调一个只认 Windows 路径的编译器OUT_WIN$(cygpath -w $(pwd)/build/output.dll) native_tool.exe --input $OUT_WIN反过来从 Windows 程序拿到的路径要在 shell 里用就套一层cygpath -u。写脚本的时候我习惯在任何跨边界的地方都显式转换一次而不是指望对方能理解另一种格式。注意-m输出的混合路径在大多数 Windows API 里是能用的但少数老程序只认反斜杠这种时候老老实实用-w。别偷懒。4.3 三个必踩的坑软链接、权限位、换行符软链接。Cygwin 的ln -s默认在 Windows 上生成的是.lnk快捷方式文件而不是 NTFS 的原生符号链接。这在 Cygwin 环境内部能正常解析但一旦被别的工具读看到的就是一个普通的快捷方式。想让ln -s生成真正的符号链接需要在环境变量里设CYGWINwinsymlinks:nativestrict而且当前用户需要有创建符号链接的权限Windows 默认对非管理员限制了这个能力。设winsymlinks:lnk是默认行为winsymlinks:native是能原生就原生不能就退回 lnk。权限位。Cygwin 试图把 NTFS 的 ACL 映射成 POSIX 的rwx权限但映射本身是有损的。你会发现chmod改了权限Windows 侧看起来没什么变化或者反过来资源管理器里改了权限Cygwin 里ls -l显示的东西很怪。如果不需要精确的权限语义可以在挂载时加noacl选项跳过 ACL 到权限位的转换一切文件都按默认权限显示脚本反而更稳定。这个在只需要读写的场景下是省心的做法。换行符。这是跨平台脚本的头号杀手。Windows 用CRLFUnix 用LF。一个在 Cygwin 里创建的脚本带到 Linux 上执行报错bad interpreter: /bin/bash^M就是^MCR在作祟。处理办法dos2unix和unix2dos两个工具专门干这个sed -i s/\r$// file也能批量清vim 里:set ffunix然后保存或者:set fileformatunix。git 用户可以在配置里设core.autocrlf来统一管理。我的习惯是shell 脚本一律 CRLF→LF 转换后再入库仓库里配.gitattributes明确指定*.sh text eollf。5. 用 Cygwin 干活编译、脚本、命令行的真实场景5.1 搭一套能用的编译工具链装完gcc-core、gcc-g、make、binutils之后gcc --version应该能看到类似gcc (GCC) x.y.z的输出并且gcc -dumpmachine会显示x86_64-pc-cygwin或x86_64-unknown-cygwin。这个 triplet 很关键——它告诉configure脚本我现在是在 Cygwin 上脚本会据此启用或禁用某些代码分支。编译一个简单的 autotools 项目标准流程是tar xf project-1.0.tar.gz cd project-1.0 ./configure --prefix/usr/local make -j4 make install--prefix/usr/local会装到C:\cygwin64\usr\local下/usr/local/bin已经在 PATH 里装完直接能调。make -j4开并行核多的话可以开到核数甚至核数加一。要留意的几个点。头文件路径Cygwin 的头文件在/usr/include链接库在/usr/lib和 Linux 逻辑一致但里面很多是适配层别指望看到和 Linux 一模一样的内容。pkg-config装了这个包之后pkg-config --cflags --libs xxx才能正常工作很多现代项目依赖它。libtool涉及动态库的项目基本都绕不开装的时候顺手把libltdl也带上。长路径问题Windows 默认路径长度限制 260 字符深层目录的构建产物容易超限可以用\\?\前缀或者开启系统的长路径支持但更省事的办法是把项目放在盘符根目录附近的短路径下。如果目标不是在 Cygwin 上跑而是产出能在纯 Windows 上跑的程序那你需要的是 MinGW 交叉工具链包名是mingw64-x86_64-gcc-core这一类。用x86_64-w64-mingw32-gcc编译出来的 exe 不依赖cygwin1.dll可以发给别人直接用。这两条路子不要搞混选择依据就是最终产物给谁用。5.2 Shell 脚本在 Cygwin 与 Linux 之间迁移这是 Cygwin 最有价值的场景之一也是坑最集中的地方。我总结下来脚本迁移要过四道关。第一关解释器路径。#!/bin/bash在 Cygwin 和 Linux 上都成立因为 Cygwin 有/bin/bash。但#!/usr/bin/env python这种就要看环境Cygwin 里的 python 路径可能是/usr/bin/python3。迁移脚本前先which bash python3确认一下。第二关路径语义。脚本里硬编码/home/user/...的在 Cygwin 下对应的是C:\cygwin64\home\user\...和 Windows 用户的实际家目录C:\Users\user不是一个地方。要么改脚本要么在 Cygwin 里做个软链把两者接起来。第三关命令差异。GNU 工具在两边基本一致但有些细节不同readlink -f的行为在旧版本 Cygwin 上可能不支持stat的参数格式有差别sed -i在某些版本上需要明确给后缀参数写成sed -i.bakgrep -P依赖 PCRE 支持需要装libpcre相关包。写跨平台脚本时能不用这些边缘特性就不用用最保守的写法。第四关外部命令依赖。脚本里调了rsync、ssh、7z、jq之类先确认 Cygwin 里装没装对应包。jq在 Cygwin 的包里能搜到7z对应的是p7zip。我的实践是写脚本时就按最保守的 POSIX 子集来把平台相关的部分抽到一个单独的配置段用变量控制路径和工具名。这样在 Cygwin 上调通过的脚本拿到 Linux 上基本不用改。5.3 和 Windows 原生工具混用的边界Cygwin 程序可以直接CreateProcess启动 Windows 原生的 exe参数按普通字符串传。所以notepad.exe file.txt、explorer.exe .、cmd.exe /c dir都能跑。反过来的限制更多一些Windows 原生程序看不懂/cygdrive/c/...这种路径所以给它传参前必须用cygpath -w转。进程间的信号传递是个大坑。你在 Cygwin 里kill一个 Windows 原生程序不会按 POSIX 语义工作因为目标进程根本不理解这些信号Cygwin 只能做有限的模拟大多数时候直接强杀。反过来Windows 任务管理器结束 Cygwin 进程进程收不到SIGTERM清理逻辑不会跑可能留下锁文件。需要优雅退出的场景脚本里要自己做超时和清理。管道和标准流是能通的但要注意编码。Windows 程序默认输出可能是 GBK 或 UTF-16 的某个变体Cygwin 侧按 UTF-8 解析就会乱码。这种跨边界的文本处理我一般会在中间加一层显式的编码转换或者在调用时先设chcp 65001切到 UTF-8 代码页。6. 常见问题与排查速查6.1 中文乱码从根源上分清是哪一类Cygwin 下的中文乱码至少有三种不同的成因混在一起治永远治不好。第一类是终端显示层。表现是命令输出里的中文变成方块或者问号。根因是 mintty 的字符集设置和实际输出编码不一致。解决Options → Text → Character set设为UTF-8。如果是 locale 导致的检查locale命令的输出把LANG设成zh_CN.UTF-8或en_US.UTF-8并确认/usr/share/locale下有对应的 locale 定义。/etc/profile里通常有一段 locale 的自动探测逻辑出问题时可以手动在~/.bashrc里覆盖。第二类是归档文件里的文件名。表现是解压出来的中文文件名变成乱码但文件内容正常。这是最典型的问题Windows 下用旧版压缩工具打的 zip文件名用的是 GBK 编码而 Cygwin 的unzip默认按 UTF-8 解释。解决办法是unzip -O CP936 file.zip前提是你装的 unzip 版本支持-O参数Cygwin 版本的 unzip 通常支持。tar 包的话用tar --iconvGBK,UTF-8 -xf file.tar可以在解压时做编码转换。还有一个万能的绕法用 Windows 侧的压缩工具重新打包一遍选 UTF-8 文件名选项。第三类是文件内容本身。表现是cat出来的中文乱码但文件名正常。这是文本内容编码问题跟 Cygwin 没关系需要iconv -f GBK -t UTF-8 src.txt dst.txt逐个转换。写批处理脚本时可以用file -i *.txt先探测编码。实操心得我处理中文乱码的顺序固定是先看终端字符集 → 再看文件名编码 → 最后看文件内容编码从外到内逐层排除。反过来排查会浪费大量时间。6.2 fork 失败、启动慢、被杀软拦截fork: resource temporarily unavailable是 Cygwin 用户最经典的报错。原因通常有几类。第一杀毒软件/安全软件干扰。Cygwin 的 fork 要复制进程内存并重新映射这个行为在安全软件眼里非常像恶意代码注入会被实时监控拦住或者大幅拖慢。解决方式是把C:\cygwin64整个目录加进杀软的排除列表包括实时扫描和主动防御两个模块。这一条能解决相当大一部分 fork 问题。第二地址空间布局冲突。Cygwin 需要在自己的进程地址空间里给cygwin1.dll找一个固定位置如果被别的 DLL 占了就得做重定位每次 fork 都要重做非常慢。Cygwin 官方有一份已知会干扰的应用列表常见的元凶是各类输入法、某些安全控件、部分虚拟化软件的注入模块、一些截图和录屏工具。排查方法是用最小化的环境启动关掉所有第三方常驻程序看是否好转。第三cygwin1.dll版本混用。前面提过PATH 里出现了多个不同版本的cygwin1.dll会导致 fork 直接失败。用cygcheck -s检查版本用which -a cygwin1.dll找有没有多份。第四/proc或临时目录问题。Cygwin 的共享内存和信号机制依赖一些系统资源如果TMP、TEMP指向一个不可写或者路径奇怪的目录也会出问题。检查这两个环境变量指向一个简单的短路径比如C:\Temp。6.3 问题速查表现象最可能的原因处理动作fork: resource temporarily unavailable杀软拦截、DLL 地址冲突、多版本 cygwin1.dll加排除目录关掉注入类常驻程序cygcheck -s查版本find/sort报参数错误命中了 Windows 原生同名命令重排 PATH剔除System32中文文件名乱码zip 用了 GBK 编码unzip -O CP936或tar --iconv终端里中文显示为方块mintty 字符集或 locale 不对设 Character setUTF-8检查locale输出脚本报bad interpreter: ^MCRLF 换行符dos2unix或sed -i s/\r$//ln -s生成的是快捷方式默认 winsymlinks 模式设CYGWINwinsymlinks:nativestrict装包时提示 DLL 被占用有 Cygwin 进程在跑关掉所有 mintty、bash、相关服务后重试启动 bash 要等很久杀软全量扫描、磁盘慢排排除目录检查/etc/profile里是否有阻塞调用ssh连不上或密钥权限报错权限位映射异常挂载加noacl或手工chmod 600编译报找不到头文件缺-dev类包或 pkg-config 未装用 setup 装对应的开发包最后补一句关于升级的经验。Cygwin 的运行时升级不像普通的软件更新那么无痛因为cygwin1.dll是全局单例、被大量进程加载。我踩过的坑是一边开着终端跑构建一边跑安装器升级cygwin包结果更新到一半 DLL 被占用装出了一个半新半旧的状态之后所有 Cygwin 程序启动都报版本不匹配。恢复方式是用安装器重新强制安装cygwin包。所以升级前务必先关掉所有 Cygwin 进程包括后台跑的服务、编辑器、文件管理器窗口确认干净了再动。这个动作只需要十秒能避免一次彻底重装。