← Back to context

Comment by rahen

12 hours ago

Before WSL, the best ways to run unmodified Linux binaries inside Windows were CoLinux and flinux.

http://www.colinux.org/

https://github.com/wishstudio/flinux

flinux essentially had the architecture of WSL1, while CoLinux was more like WSL2 with a Linux kernel side-loaded.

Cygwin was technically the correct approach: native POSIX binaries on Windows rather than hacking in some foreign Linux plumbing. Since it was merely a lightweight DLL to link to (or a bunch of them), it also kept the cruft low without messing with ring 0.

However, it lacked the convenience of a CLI package manager back then, and I remember being hooked on CoLinux when I had to work on Windows.

Cygwin is way older than CoLinux. CoLinux is from 2004. Cygwin was first released in 1995.

The problem with Cygwin as I remember it was DLL hell. You'd have applications (such as a OpenSSH port for Windows) which would include their own cygwin1.dll and then you'd have issues with different versions of said DLL.

Cygwin had less overhead which mattered in a world of limited RAM and heavy, limited swapping (x86-32, limited I/O, PATA, ...).

Those constraints also meant native applications instead of Web 2.0 NodeJS and what not. Java specifically had a bad name, and back then not even a coherent UI toolkit.

As always: two steps forward, one step back.

  • Just use ssh from Cygwin. DLL hell was rarely a problem, just always install everything via setup.exe.

    The single biggest problem it has is slow forking. I learned to write my scripts in pure bash as much as possible, or as a composition of streaming executables, and avoid executing an executable per line of input or similar.

    • On your own system, sure.

      As a dependency of a shipping Windows application that needs to cleanly coexist side-by-side with existing Cygwin installations and optionally support silent install/upgrade/uninstall through mechanisms like SCCM, Intune, and Group Policy?

      Not so much.

      I do use the setup program to build the self-contained Cygwin root that's ultimately bundled into my program's MSI package and installed as a subdirectory of its Program Files directory, however.

    • I've never had a problem installing from setup, but some tools were (maybe still are, it is a long time since I've needed anything not in the main repo) ported to windows using the cygwin dlls were distributed with their own versions and could clobber the versions you have otherwise (and have their versions clobbered when you fix that).

      > slow forking

      There isn't much that can be done about that: starting up and tearing down a process on Windows is much more resource intensive operation than most other OSs because there is a lot going on by default that on other OSs a process ops into, only if it needs to, by interacting with GUI libraries and such. This is why threads were much more popular on Windows: while they are faster than forking on other OSs too, especially of course if data needs to be shared between the tasks because IPC is a lot more expensive than just sharing in-process memory, the difference is not as stark as seen under Windows so the potential difficulties of threaded development wasn't always worth the effort.

      Cygwin can't do anything about the cost of forking processes, unfortunately.

    • Slow forking is only the second biggest problem IMO. The biggest is the lack of proper signals. There's a bunch of software out there that just isn't architected to work well without non-cooperative preemption.

      1 reply →

  • I used cygwin pretty heavily in the late 90s and early 2000s. It was slow. I had scripts that took days to run dealing with some network file management. When I moved them over to linux out of frustration (I think I brought in something like a pentium 90 laptop, gateway solo I think?) they were done in tens of minutes.

    I'm sure they did the best they could ... it was just really painful to use.

    • This matches me experience as well. Some of my earliest rsync experiences were with the Cygwin version and I can remember scratching my head and wondering why people raved about this tool that ran so slowly. Imagine my surprise when I tried it on Linux. Night and day!

  • Cygwin works fine if I am compiling stuff locally for my own use, but that cygwin1.dll (plus any dependencies) is a problem for distribution.

    What I usually do is make sure my code builds with both Cygwin and MingW, and distribute the binaries built with MingW.

  • > Cygwin had less overhead which mattered in a world of limited RAM and heavy, limited swapping (x86-32, limited I/O, PATA, ...).

    Maybe so, but my memory of Cygwin was waiting multiple seconds just for the Cygwin CLI prompt to load. It was very slow on my machines.

  • Meanwhile those that complained about Java, now ship a whole browser with their "native" application, and then complain about Google taking over the Web.

Technically correct by some estimation, perhaps, but Cygwin is a crazy approach, was slow (contrary to the implication of the "low cruft" claim), was not as compatible as these other approaches, required recompilation, and was widely disliked at most points in its life. There's a lot of crazy voodoo stuff happening in cygwin1.dll to make this work; it totally qualifies as "hacking in some foreign Linux plumbing", it's just happening inside your process. Just picture how fork() is implemented inside cygwin1.dll without any system support.

Cygwin doesn't work at all in Windows AppContainer package isolation; too many voodoo hacks. MSYS2 uses it to this day, and as a result you can't run any MSYS2 binaries in an AppContainer. Had to take a completely different route for Claude Code sandboxing because of this: Claude Code wants Git for Windows, and Git for Windows distributes MSYS2-built binaries of bash.exe and friends. Truly native Windows builds don't do all the unusual compatibility hacks that cygwin1.dll requires; I found non-MSYS2-built binaries of the same programs all ran fine in AppContainer.

Nowadays MSYS2, which does depend on cygwin under the hood, offers such a package manager (pacman of Arch Linux) and it is quite a user friendly to run native POSIX binaries on Windows without a linux VM.

  • In my personal experience, Msys 2 would work great until it didn't. Unless this has changed, from what I remember, Msys2 compiled everything without PIC/PIE, and Windows does allow you to configure, system-wide, whether ASLR is used, and whether it's used "if supported" or always. If that setting is set to anything but off, Msys2 binaries will randomly crash with heap allocation errors, or they do on my system. It happened so much to me when I had actual coreutils installed that I switched to uutils-coreutils even though I knew that uutils-coreutils has some discrepancies/issues. Idk if they've fixed that bug or not; I did ask them once why they didn't just allow full ASLR and get on with things and they claimed that they needed to do non-ASLR compilations for docker.

  • MSYS2 is very confusing. When you pick "MSYS2", you are building exclusively for the MSYS2 target environment, and might not have proper compatible windows headers. When you pick "MINGW32/64", you are instead building for the normal windows environment, and get proper windows headers. But if you didn't know that, you would end up confused about why your program is not building.

    It doesn't help that the package simply named "gcc" is for the MSYS2 target.

    • And just to add insult to injury, you probably don't want MINGW64 either, as it relies on the ancient MSVCRT.DLL C runtime library that lacks support for "new" features like C99 compatibility and the UTF-8 locale, and that Microsoft never supported for use by third-party applications in the first place.

      Instead, you either want UCRT64 or CLANG64, depending on whether you want to build with the GNU or LLVM toolchains, as it uses the newer, fully-supported Universal C Runtime instead.

      1 reply →

  • It's annoying to wade through six different versions of the same package for different runtimes and word sizes. Heaven forbid you accidentally install the wrong one.

  • MSYS2 is my favorite in this area. Super lightweight and easy to use, highly recommend.

Developing on cygwin, however, was a right pain. If a C library you wanted to use didn't have a pre-built cygwin version (understandable!) then you end up doing 'configure, make' on everything in the dependency tree, and from memory about two thirds of the time you had to edit something because it's not quite POSIX enough sometimes.

  • Ha ha doing Unix like it was 1989. At the time I thought configure was the greatest of human achievements since I was distributing software amongst Sun machines of varying vintage and a Pyramid. I want to say good times but I prefer now ha ha

Cygwin implements a POSIX API on Win32 with a smattering of Nt* calls to improve compatibility but there's a lot of hoop jumping and hackery to get the right semantics. Fork isn't copy on write, for one thing.

I was a Cygwin user from about 1999 to 2022 or so, spent a little time on wsl2 (and it's what I still use on my laptop) but I'm fully Linux on the desktop since last year.

I remember when I first put cygwin in my path on Windows and it felt like magic. I can just ssh and git now? No need for putty or WinGit????

> However, it lacked the convenience of a CLI package manager back then

Cygwin still lacks that to this day, you have to fire up to GUI installer to update packages.

MSYS2 is cygwin with pacman cli.

I've been running colinux for years until early 2009 when I reinstalled my laptop with Ubuntu 8.04 and Windows XP in a VM. So much faster.

On Windows NT building software from source under Interix[0] (nee OpenNT, later "Subsystem for Unix Applications") was pretty nice.

Interix was implemented as proper NT kernel "subsystem". It was just another build target for GNU automake, for example.

(Being that Interix was a real kernel subsystem I have this fever dream idea of a text-mode "distribution" of NT running w/o any Win32 subsystem.)

[0] https://en.wikipedia.org/wiki/Interix

>Cygwin was technically the correct approach

Requiring every single Linux app developer to recompile their app using Cygwin and account for quirks that it may have is not the correct approach. Having Microsoft handle all of the compatibility concerns scales much better.

  • Why not? That is just a matter of porting stuff over, like a FreeBSD ports collection, an apt repo, or a bunch of scripts for Proton/Wine such as Lutris.

    Cygwin started in 1995. Microsoft wasn't cooperative with FOSS at all at that point. They were practicing EEE, and eating some expensive Unix/VMS machines with WNT.